HTML5遊戲開發(五):飛機大戰之讓全部元素動起來

《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;
}
複製代碼

EnemyAIBackground都要實現接口IOnTickapp

class EnemyAI extends egret.EventDispatcher implements IOnTick {
    onTick() {
        //這裏每幀都會執行
    }
}
class Background implements IOnTick {
    onTick() {
        //這裏每幀都會執行
    }
}
複製代碼

讓背景動起來

爲了實現背景循環移動的效果,咱們須要建立兩個一樣背景圖像的位圖。 咱們建立了工具方法cloneImage來克隆一個位圖。對這個方法傳入一個egret.Bitmap,你將會得到一個如出一轍的egret.Bitmapasync

// 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'));
        }
    }
}
複製代碼

運行效果

相關文章
相關標籤/搜索