《HTML5遊戲開發》系列文章的目的有:1、以最小的成本去入門egret小項目開發,官方的教程一直都是面向中重型;2、egret能夠很是輕量;3、egret相比PIXI.js和spritejs文檔更成熟、友好;4、學習從0打造高效的開發工做流。webpack
本文咱們將會讓遊戲的全部元素動起來。這包括:git
遊戲完整源碼:github.com/wildfirecod…github
在線展現:wildfirecode.com/egret-plane…web
遊戲畫面每刷新一次,即爲一幀。若是咱們能在每一幀都對場景元素的屬性進行變動,這樣咱們就得到動態效果。算法
爲了實現幀循環,咱們向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
app
class EnemyAI extends egret.EventDispatcher implements IOnTick {
onTick() {
//這裏每幀都會執行
}
}
class Background implements IOnTick {
onTick() {
//這裏每幀都會執行
}
}
複製代碼
爲了實現背景循環移動的效果,咱們須要建立兩個一樣背景圖像的位圖。 咱們建立了工具方法cloneImage
來克隆一個位圖。對這個方法傳入一個egret.Bitmap
,你將會得到一個如出一轍的egret.Bitmap
。async
// cloneImage API
const 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;
}
}
}
複製代碼
爲了每秒生成一架敵機,咱們必需要有一個敵機的模板位圖。 另外,當敵機移動到屏幕以外時,咱們要完全的銷燬它。咱們作如下設計:post
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'));
}
}
}
複製代碼