《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'));
}
}
}
复制代码