做本地團(tuán)購網(wǎng)站游戲網(wǎng)站交換友情鏈接
文章目錄
- 引言
- 項目初始化
- 游戲設(shè)計和結(jié)構(gòu)
- 游戲程序?qū)崿F(xiàn)
- Vue頁面嵌入Phaser
- Preloader 場景加載
- 游戲場景功能實現(xiàn)
- 功能類定義
- Boom爆炸類
- Bullet子彈類
- Enemy敵軍類
- Player玩家類
- End游戲結(jié)束類
- 總結(jié)
更多相關(guān)內(nèi)容可查看
引言
飛機(jī)大戰(zhàn)(也被稱為射擊游戲或空戰(zhàn)游戲)是一種非常受歡迎的休閑游戲類型。在這個博客中,我們將探討如何使用 Vue.js 框架來構(gòu)建一個簡單的飛機(jī)大戰(zhàn)游戲。我們將從基本的游戲邏輯開始,逐步增加游戲元素和交互性,代碼詳解可參考注釋,最終展示畫面在文章底部
項目初始化
git地址:https://gitee.com/its-a-little-bad/vue-project—aircraft-battle.git
node版本:20.8.1
游戲設(shè)計和結(jié)構(gòu)
在 Vue.js 中,我們通常將游戲的各個部分分解為不同的場景。
主場景
游戲場景
游戲程序?qū)崿F(xiàn)
Vue頁面嵌入Phaser
在 Vue 應(yīng)用中嵌入一個 Phaser 游戲
<template><!-- Phaser 游戲的容器 --><div id="container"></div>
</template>
<script setup lang="ts">
import { onMounted, onUnmounted } from "vue";
import { Game, AUTO, Scale } from "phaser";
import { Preloader } from "./game/Preloader";
import { Main } from "./game/Main";
import { End } from "./game/End";// 使用正則表達(dá)式檢測當(dāng)前設(shè)備是否為移動設(shè)備
let isMobile = /(iPhone|iPad|Android)/i.test(navigator.userAgent);// 定義了一個 game 變量來存儲 Phaser 游戲?qū)嵗?/span>
let game: Game;
onMounted(() => {game = new Game({parent: "container",type: AUTO,width: 375,//游戲的大小根據(jù)設(shè)備類型進(jìn)行調(diào)整。如果設(shè)備是移動設(shè)備,則高度會根據(jù)設(shè)備的縱橫比計算得出。height: isMobile ? (window.innerHeight / window.innerWidth) * 375 : 667,//游戲的縮放模式也根據(jù)設(shè)備類型進(jìn)行設(shè)置。移動設(shè)備使用 Scale.FIT,這意味著游戲?qū)⒈M可能地適應(yīng)屏幕大小,//而不會保持其原始縱橫比。非移動設(shè)備則使用 Scale.NONE,這意味著游戲?qū)⒈3制湓即笮 ?/span>scale: {mode: isMobile ? Scale.FIT : Scale.NONE,},physics: {default: "arcade",arcade: {debug: false,},},scene: [Preloader, Main, End],});
});onUnmounted(() => {game.destroy(true);
});
</script><style>
body {margin: 0;
}
#app {height: 100%;
}
</style>
Preloader 場景加載
創(chuàng)建一個 Preloader 場景來加載游戲所需的資源和設(shè)置一些基本的游戲元素,示例如下
程序?qū)崿F(xiàn):
import { Scene } from "phaser";
import backgroundImg from "../assets/images/background.jpg";
import enemyImg from "../assets/images/enemy.png";
import playerImg from "../assets/images/player.png";
import bulletImg from "../assets/images/bullet.png";
import boomImg from "../assets/images/boom.png";
import spritesImg from "../assets/images/sprites.png";
import spritesJson from "../assets/json/sprites.json?url";
import bgmAudio from "../assets/audio/bgm.mp3";
import boomAudio from "../assets/audio/boom.mp3";
import bulletAudio from "../assets/audio/bullet.mp3"; export class Preloader extends Scene { // 構(gòu)造函數(shù),定義場景名稱為 "Preloader" constructor() { super("Preloader"); } // 預(yù)加載資源的方法 preload() { // 加載背景圖片 this.load.image("background", backgroundImg); // 加載敵人圖片 this.load.image("enemy", enemyImg); // 加載玩家圖片 this.load.image("player", playerImg); // 加載子彈圖片 this.load.image("bullet", bulletImg); // 加載爆炸動畫的精靈表(spritesheet) this.load.spritesheet("boom", boomImg, { frameWidth: 64, frameHeight: 48, }); // 加載精靈圖集(atlas) this.load.atlas("sprites", spritesImg, spritesJson); // 加載背景音樂 this.load.audio("bgm", bgmAudio); // 加載爆炸音效 this.load.audio("boom", boomAudio); // 加載子彈音效 this.load.audio("bullet", bulletAudio); } // 創(chuàng)建場景的方法 create() { const { width, height } = this.cameras.main; // 顯示背景(通常在Preloader場景中不展示實際游戲內(nèi)容,這里僅為示例) this.add.tileSprite(0, 0, width, height, "background").setOrigin(0, 0); // 播放背景音樂(在Preloader場景中播放通常是為了給玩家一個等待的反饋) this.sound.play("bgm", { loop: true }); // 循環(huán)播放背景音樂 // 添加標(biāo)題(通常也不在Preloader場景中,但可以作為加載提示) this.add .text(width / 2, height / 4, "飛機(jī)大戰(zhàn)", { fontFamily: "Arial", fontSize: 60, color: "#e3f2ed", stroke: "#203c5b", strokeThickness: 6, }) .setOrigin(0.5); // 添加開始按鈕(通常用于在加載完成后切換到主場景) let button = this.add .image(width / 2, (height / 4) * 3, "sprites", "button") // 假設(shè)"sprites"圖集中有名為"button"的幀 .setScale(3, 2) .setInteractive() .on("pointerdown", () => { // 當(dāng)按鈕被點擊時,切換到主場景(這里主場景名為'Main') this.scene.start('Main'); }); // 按鈕文案this.add.text(button.x, button.y, "開始游戲", {fontFamily: "Arial",fontSize: 20,color: "#e3f2ed",}).setOrigin(0.5); } // 創(chuàng)建動畫,命名為 boom,后面使用this.anims.create({key: "boom",frames: this.anims.generateFrameNumbers("boom", { start: 0, end: 18 }),repeat: 0,});
}
在Phaser 3框架中,從一個場景(如Preloader)切換到另一個場景(如Main)通常使用this.scene.start(‘Main’)這樣的代碼來實現(xiàn)。這是Phaser場景管理系統(tǒng)的一部分,它允許你動態(tài)地加載、創(chuàng)建、運(yùn)行和銷毀游戲的不同部分。
游戲場景功能實現(xiàn)
程序?qū)崿F(xiàn)
// 定義 Main 場景類,繼承自 Phaser 的 Scene 類
import { Scene, Physics, GameObjects } from "phaser";
import { Player } from "./Player";
import { Bullet } from "./Bullet";
import { Enemy } from "./Enemy";
import { Boom } from "./Boom";// 場景元素
let background: GameObjects.TileSprite;
let player: Player;
let enemys: Physics.Arcade.Group;
let bullets: Physics.Arcade.Group;
let booms: GameObjects.Group;
let scoreText: GameObjects.Text;// 場景數(shù)據(jù)
let score: number;export class Main extends Scene {constructor() {super("Main");}create() {let { width, height } = this.cameras.main;// 創(chuàng)建背景background = this.add.tileSprite(0, 0, width, height, "background").setOrigin(0, 0);// 創(chuàng)建玩家,調(diào)用Player類player = new Player(this);// 創(chuàng)建敵軍組// 注解:enemys 是一個 Phaser 的物理組,用于存儲和管理多個 Enemy 對象// frameQuantity 表示從 enemy 紋理集中加載的幀數(shù),key 是紋理集的名稱// enable, active, visible 分別是啟用物理、激活和可見性標(biāo)志// classType 指示組中新創(chuàng)建對象的類型enemys = this.physics.add.group({frameQuantity: 30,key: "enemy",enable: false,// 在此初始狀態(tài)下不啟用物理 active: false,// 在此初始狀態(tài)下不激活 visible: false,// 在此初始狀態(tài)下不可見classType: Enemy,// 當(dāng)組中添加新對象時使用的類});// 創(chuàng)建子彈// 注解:與敵軍組類似,但用于存儲和管理多個 Bullet 對象 bullets = this.physics.add.group({frameQuantity: 15,key: "bullet",enable: false,active: false,visible: false,classType: Bullet,});// 創(chuàng)建爆炸// 注解:booms 組用于存儲和管理多個 Boom 對象,可能是用于顯示爆炸動畫booms = this.add.group({frameQuantity: 30,key: "boom",active: false,visible: false,classType: Boom,});// 分?jǐn)?shù)// 注解:score 變量用于跟蹤玩家的分?jǐn)?shù),scoreText 是顯示分?jǐn)?shù)的文本對象 score = 0;scoreText = this.add.text(10, 10, "0", {fontFamily: "Arial",fontSize: 20,});// 注冊事件this.addEvent();}// 注冊事件addEvent() {// 定時器// 注解:此定時器每 400 毫秒觸發(fā)一次回調(diào),生成敵軍和發(fā)射子彈 this.time.addEvent({delay: 400,callback: () => {// 生成2個敵軍for (let i = 0; i < 2; i++) {enemys.getFirstDead()?.born();}// 發(fā)射1顆子彈bullets.getFirstDead()?.fire(player.x, player.y - 32);},callbackScope: this,repeat: -1,});// 子彈和敵軍碰撞,會調(diào)用 hit 方法this.physics.add.overlap(bullets, enemys, this.hit, null, this);// 玩家和敵軍碰撞,會調(diào)用 gameOver 方法this.physics.add.overlap(player, enemys, this.gameOver, null, this);}// 子彈擊中敵軍hit(bullet, enemy) {// 子彈和敵軍隱藏enemy.disableBody(true, true);bullet.disableBody(true, true);// 顯示爆炸booms.getFirstDead()?.show(enemy.x, enemy.y);// 分?jǐn)?shù)增加scoreText.text = String(++score);}// 游戲結(jié)束gameOver() {// 暫停當(dāng)前場景,并沒有銷毀this.sys.pause();// 保存分?jǐn)?shù)this.registry.set("score", score);// 打開結(jié)束場景this.game.scene.start("End");}update() {// 設(shè)置背景瓦片不斷移動background.tilePositionY -= 1;}
}
功能類定義
Boom爆炸類
import { GameObjects, Scene } from "phaser";export class Boom extends GameObjects.Sprite {constructor(scene: Scene, x: number, y: number, texture: string) {// 創(chuàng)建對象super(scene, x, y, texture);// 爆炸動畫播放結(jié)束事件this.on("animationcomplete-boom", this.hide, this);}/*** 顯示爆炸* @param x 爆炸x坐標(biāo)* @param y 爆炸y坐標(biāo)*/show(x: number, y: number) {this.x = x;this.y = y;this.setActive(true);this.setVisible(true);// 爆炸動畫this.play("boom");// 爆炸音效this.scene.sound.play("boom");}/*** 隱藏爆炸*/hide() {this.setActive(false);this.setVisible(false);}
}
Bullet子彈類
import { Physics, Scene } from "phaser";export class Bullet extends Physics.Arcade.Sprite {constructor(scene: Scene, x: number, y: number, texture: string) {super(scene, x, y, texture);// 設(shè)置屬性this.setScale(0.25);}/*** 發(fā)射子彈* @param x 子彈x坐標(biāo)* @param y 子彈y坐標(biāo)*/fire(x: number, y: number) {this.enableBody(true, x, y, true, true);this.setVelocityY(-300);this.scene.sound.play("bullet");}preUpdate(time: number, delta: number) {super.preUpdate(time, delta);// 子彈走到頭,銷毀if (this.y <= -14) {this.disableBody(true, true);}}
}
Enemy敵軍類
import { Physics, Math, Scene } from "phaser";export class Enemy extends Physics.Arcade.Sprite {constructor(scene: Scene, x: number, y: number, texture: string) {// 創(chuàng)建對象super(scene, x, y, texture);scene.add.existing(this);scene.physics.add.existing(this);// 設(shè)置屬性this.setScale(0.5);this.body.setSize(100, 60);}/*** 生成敵軍*/born() {let x = Math.Between(30, 345);let y = Math.Between(-20, -40);this.enableBody(true, x, y, true, true);this.setVelocityY(Math.Between(150, 300));}preUpdate(time: number, delta: number) {super.preUpdate(time, delta);let { height } = this.scene.cameras.main;// 敵軍走到頭,銷毀if (this.y >= height + 20) {this.disableBody(true, true)}}
}
Player玩家類
import { Physics, Scene } from "phaser";export class Player extends Physics.Arcade.Sprite {isDown: boolean = false;downX: number;downY: number;constructor(scene: Scene) {// 創(chuàng)建對象let { width, height } = scene.cameras.main;super(scene, width / 2, height - 80, "player");scene.add.existing(this);scene.physics.add.existing(this);// 設(shè)置屬性this.setInteractive();this.setScale(0.5);this.setCollideWorldBounds(true);this.body.setSize(120, 120);// 注冊事件this.addEvent();}/*** 注冊事件*/addEvent() {// 手指按下我方飛機(jī)this.on("pointerdown", () => {this.isDown = true;this.downX = this.x;this.downY = this.y;});// 手指抬起this.scene.input.on("pointerup", () => {this.isDown = false;});// 手指移動this.scene.input.on("pointermove", (pointer) => {if (this.isDown) {this.x = this.downX + pointer.x - pointer.downX;this.y = this.downY + pointer.y - pointer.downY;}});}
}
End游戲結(jié)束類
import { Scene } from "phaser";export class End extends Scene {constructor() {super("End");}create() {let { width, height } = this.cameras.main;// 結(jié)束面板this.add.image(width / 2, height / 2, "sprites", "result").setScale(2.5);// 標(biāo)題this.add.text(width / 2, height / 2 - 85, "游戲結(jié)束", {fontFamily: "Arial",fontSize: 24,}).setOrigin(0.5);// 當(dāng)前得分let score = this.registry.get("score");this.add.text(width / 2, height / 2 - 10, `當(dāng)前得分:${score}`, {fontFamily: "Arial",fontSize: 20,}).setOrigin(0.5);// 重新開始按鈕let button = this.add.image(width / 2, height / 2 + 50, "sprites", "button").setScale(3, 2).setInteractive().on("pointerdown", () => {// 點擊事件:關(guān)閉當(dāng)前場景,打開Main場景this.scene.start("Main");});// 按鈕文案this.add.text(button.x, button.y, "重新開始", {fontFamily: "Arial",fontSize: 20,}).setOrigin(0.5);}
}
總結(jié)
通過使用 Vue.js 框架,我們可以輕松地構(gòu)建出一個簡單而有趣的飛機(jī)大戰(zhàn)游戲。從基本的游戲邏輯開始,逐步增加游戲元素和交互性,最終得到一個完整且吸引人的游戲作品。希望這個博客能對你有所啟發(fā),并鼓勵你嘗試使用 Vue.js 來開發(fā)更多有趣的游戲和應(yīng)用程序!