《HTML5遊戲開發》系列文章的目的有:一、以最小的成本去入門egret小專案開發,官方的教程一直都是面向中重型;二、egret可以非常輕量;三、egret相比PIXI.js和spritejs文件更成熟、友好;四、學習從0打造高效的開發工作流。
- HTML5遊戲開發(一):3分鐘建立一個hello world
- HTML5遊戲開發(二):使用TypeScript編寫程式碼
- HTML5遊戲開發(三):使用webpack構建TypeScript應用
- HTML5遊戲開發(四):飛機大戰之顯示場景和元素
- HTML5遊戲開發(五):飛機大戰之讓所有元素動起來
本文我們將會讓遊戲的所有元素動起來。這包括:
- 讓背景從上到下迴圈移動,實現飛機向上飛行的效果。通過使兩張相同的背景圖上下交替的方式就可以實現。
- 讓敵機不斷地從上方出現,並持續地向友機移動。
遊戲完整原始碼:github.com/wildfirecod…
線上展示:wildfirecode.com/egret-plane…
動畫實現方式:按幀重新整理
遊戲畫面每重新整理一次,即為一幀。如果我們能在每一幀都對場景元素的屬性進行變更,這樣我們就獲得動態效果。
為了實現幀迴圈,我們向egret.startTick
註冊並啟動一個計時器onTick
,它通常會以60FPS的速率觸發回撥方法。這裡的FPS即多少幀每秒。另外,我們會提前建立一個陣列用來存放所有需要進行按幀重新整理
的物件,那麼前提條件是必須實現IOnTick
介面的方法onTick
。這些物件的類包括處理背景迴圈移動的Background
以及控制敵機AI的EnemyAI
。
_IOnTicks: IOnTick[];
//用來存放所有需要進行按幀重新整理的物件async onAddToStage() {
this._IOnTicks = [];
//提前建立這個陣列 ... this.createGame();
... egret.startTick(this.onTick, this);
...
}createGame() {
... const background = new Background();
//利用幀迴圈實現背景迴圈移動 this._IOnTicks.push(background);
//儲存到陣列 this.addEnemy();
//新增一個敵機
}addEnemy() {
... const enemy = new Enemy();
//我們在Enemy類中例項化了EnemyAI this._IOnTicks.push(enemy.AI);
//儲存到陣列
}onTick() {
this._IOnTicks.forEach(val =>
val.onTick());
//執行所有需要按幀重新整理的物件的onTick方法 return false;
}複製程式碼
EnemyAI
和Background
都要實現介面IOnTick
class EnemyAI extends egret.EventDispatcher implements IOnTick {
onTick() {
//這裡每幀都會執行
}
}class Background implements IOnTick {
onTick() {
//這裡每幀都會執行
}
}複製程式碼
讓背景動起來
為了實現背景迴圈移動的效果,我們需要建立兩個同樣背景影像的點陣圖。我們建立了工具方法cloneImage
來克隆一個點陣圖。對這個方法傳入一個egret.Bitmap
,你將會獲得一個一模一樣的egret.Bitmap
。
// cloneImage APIconst cloneImage: (bitmap: egret.Bitmap) =>
egret.Bitmap複製程式碼
createGame() {
const [bg, hero, enemy] = this._bitmaps;
this.addChild(bg);
const bg2 = cloneBitmap(bg);
this.addChild(bg2);
//將克隆的背景圖也新增到舞臺 ... const background = new Background(bg, bg2);
...
}複製程式碼
最後,我們來看看Background
類是如何實現背景迴圈的功能。
import IOnTick from "./IOnTick";
export default class Background implements IOnTick {
_bg: egret.Bitmap;
_bg2: egret.Bitmap;
constructor(bg: egret.Bitmap, bg2: egret.Bitmap) {
this._bg = bg;
this._bg2 = bg2;
this._bg2.y = -bg2.height;
} onTick() {
const SPEED = 8;
//每幀裡背景都會向下移動8px this._bg.y += SPEED;
this._bg2.y += SPEED;
const height = this._bg.height;
//背景交替移動 if (this._bg.y >
height) {
this._bg.y = this._bg2.y - height;
} if (this._bg2.y >
height) {
this._bg2.y = this._bg.y - height;
}
}
}複製程式碼
讓敵機動起來
為了每秒生成一架敵機,我們必須要有一個敵機的模板點陣圖。另外,當敵機移動到螢幕之外時,我們要徹底的銷燬它。我們做以下設計:
EnemyAI
類負責敵機移至螢幕之外的演算法以及在移至螢幕之外時廣播onEnemyDisappear
事件。Enemy
類負責從顯示層銷燬敵機。Main.removeEnemy
方法負責將EnemyAI
物件移除幀重新整理列表,避免不必要的計算。
createGame() {
const [bg, hero, enemy] = this._bitmaps;
... this._enemyTemplate = enemy;
//將敵機模板儲存起來 setInterval(() =>
this.addEnemy(), 1000);
//每1000ms生成一架敵機
}addEnemy() {
const enemyImage = cloneImage(this._enemyTemplate);
//克隆敵機圖片 this.addChild(enemyImage);
this.centerAnchor(enemyImage);
const enemy = new Enemy(enemyImage);
enemy.AI.once('onEnemyDisappear', this.onEnemyDisappear, this);
//監聽敵機移出螢幕的廣播事件 this._IOnTicks.push(enemy.AI);
}onEnemyDisappear(e: egret.Event) {
const AI = e.currentTarget as EnemyAI;
AI.enemy.destroy();
//將敵機從顯示層銷燬 this.removeEnemy(AI);
//將EnemyAI物件移除幀重新整理列表,避免不必要的計算。
}removeEnemy(enemyAI: EnemyAI) {
const index = this._IOnTicks.indexOf(enemyAI);
this._IOnTicks.splice(index, 1);
//移除幀重新整理物件列表
}複製程式碼
Enemy
類負責從顯示層銷燬敵機
import EnemyAI from "./EnemyAI";
export default class Enemy {
image: egret.Bitmap;
AI: EnemyAI;
constructor(image: egret.Bitmap) {
this.image = image;
this.AI = new EnemyAI(this);
//例項化敵機AI
} removeImage() {
this.image.parent.removeChild(this.image);
//從顯示層移除
} destroy() {
this.removeImage();
}
}複製程式碼
EnemyAI
類
import Enemy from "./Enemy";
import IOnTick from "../IOnTick";
class EnemyAI extends egret.EventDispatcher implements IOnTick {
enemy: Enemy;
_image: egret.Bitmap;
initialX: number;
initialY: number;
constructor(enemy: Enemy) {
super();
this._image = enemy.image;
this.enemy = enemy;
this.initialX = this._image.stage.stageWidth / 2;
this.initialY = -100;
this.setInitialPosition();
//設定敵機的初始位置
} private setInitialPosition() {
this._image.x = this.initialX;
this._image.y = this.initialY;
} onTick() {
this._image.y += 5;
//每幀裡敵機都會向下移動5px if (this._image.y >
this._image.stage.stageHeight) {
//判斷是否移至螢幕之外 //廣播移至螢幕之外的事件 this.dispatchEvent(new egret.Event('onEnemyDisappear'));
}
}
}複製程式碼