The goals of the HTML5 Game Development series are: 1) To get an introduction to Egret small project development at minimal cost, as the official EGREt tutorials have always been for medium and heavy projects; Egret can be very lightweight; 3. Egret documents are more mature and friendly than Pixi. js and Spritejs; Learn to build an efficient development workflow from zero.
- Create a Hello World in 3 minutes
- HTML5 Game Development part II: Writing code in TypeScript
- HTML5 Game Development (3) : Build TypeScript apps with Webpack
- HTML5 Game Development part 4: Aircraft Wars display scenes and elements
- HTML5 Game Development # 5: Airplane Wars Get everything moving
In this article we are going to make every element of the game work. This includes:
- Let the background loop from top to bottom to achieve the effect of the plane flying up. This can be done by alternating two identical backgrounds up and down.
- Let the enemy aircraft constantly appear from above and continue to move towards the friendly aircraft.
Game complete source: github.com/wildfirecod…
Online presentation: wildfirecode.com/egret-plane…
Animation implementation: refresh by frame
Each refresh of the game screen is one frame. If we can change the attributes of the scene element every frame, then we have dynamic effects.
To implement the frame loop, we register with egret.startTick and start a timer, onTick, which normally fires the callback method at a rate of 60FPS. FPS is how many frames per second. In addition, we will create an array ahead of time to store all objects that need to be refreshed by frame, as long as the onTick method of the IOnTick interface is implemented. Classes for these objects include Background, which handles Background loop movement, and EnemyAI, which controls enemy aircraft AI.
_IOnTicks: IOnTick[];// Store all objects that need to be refreshed by frame
async onAddToStage() {
this._IOnTicks = [];// Create the array ahead of time. this.createGame(); . egret.startTick(this.onTick, this); . } createGame() { ... const background =new Background();// Use frame loop to move background loop
this._IOnTicks.push(background);// Save to array
this.addEnemy();// Add an enemy plane
}
addEnemy() {
...
const enemy = new Enemy();// We instantiate EnemyAI in the Enemy class
this._IOnTicks.push(enemy.AI);// Save to array
}
onTick() {
this._IOnTicks.forEach(val= > val.onTick());// Execute the onTick method on all objects that need to be refreshed by frame
return false;
}
Copy the code
EnemyAI and Background both implement IOnTick interfaces
class EnemyAI extends egret.EventDispatcher implements IOnTick {
onTick() {
// Here every frame is executed}}class Background implements IOnTick {
onTick() {
// Here every frame is executed}}Copy the code
Get the background moving
To achieve this effect, we need to create two bitmaps of the same background image. We created the utility method cloneImage to clone a bitmap. Pass an egret.Bitmap to this method, and you’ll get an egret.Bitmap that is exactly the same.
// cloneImage API
const cloneImage: (bitmap: egret.Bitmap) = > egret.Bitmap
Copy the code
createGame() {
const [bg, hero, enemy] = this._bitmaps;
this.addChild(bg);
const bg2 = cloneBitmap(bg);
this.addChild(bg2);// Add the clone background image to the stage as well. const background =newBackground(bg, bg2); . }Copy the code
Finally, let’s look at how the Background class implements the Background loop.
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;// Every frame the background will be moved down 8px
this._bg.y += SPEED;
this._bg2.y += SPEED;
const height = this._bg.height;// The background moves alternately
if (this._bg.y > height) {
this._bg.y = this._bg2.y - height;
}
if (this._bg2.y > height) {
this._bg2.y = this._bg.y - height; }}}Copy the code
Get the enemy on the move
To generate one enemy per second, we must have a template bitmap of the enemy. In addition, when an enemy plane moves off-screen, we destroy it completely. We do the following design:
EnemyAI
Class is responsible for the algorithm for enemy aircraft moving off-screen and broadcasting when moving off-screenonEnemyDisappear
Events.Enemy
Class is responsible for destroying enemy aircraft from the display layer.Main.removeEnemy
Method responsible forEnemyAI
Object Remove frame refresh list to avoid unnecessary calculation.
createGame() {
const [bg, hero, enemy] = this._bitmaps; . this._enemyTemplate = enemy;// Save the enemy template
setInterval((a)= > this.addEnemy(), 1000);// Generate an enemy aircraft every 1000ms
}
addEnemy() {
const enemyImage = cloneImage(this._enemyTemplate);// Clone the picture of enemy aircraft
this.addChild(enemyImage);
this.centerAnchor(enemyImage);
const enemy = new Enemy(enemyImage);
enemy.AI.once('onEnemyDisappear'.this.onEnemyDisappear, this);// Listen for the broadcast event of enemy aircraft moving off screen
this._IOnTicks.push(enemy.AI);
}
onEnemyDisappear(e: egret.Event) {
const AI = e.currentTarget as EnemyAI;
AI.enemy.destroy();// Destroy enemy aircraft from display layer
this.removeEnemy(AI);// Remove EnemyAI objects from the frame refresh list to avoid unnecessary calculations.
}
removeEnemy(enemyAI: EnemyAI) {
const index = this._IOnTicks.indexOf(enemyAI);
this._IOnTicks.splice(index, 1);// Remove frame refresh object list
}
Copy the code
The Enemy class is responsible for destroying Enemy aircraft from the display layer
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);// Instantiate enemy AI
}
removeImage() {
this.image.parent.removeChild(this.image);// Remove from the display layer
}
destroy() {
this.removeImage(); }}Copy the code
EnemyAI class
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(a);this._image = enemy.image;
this.enemy = enemy;
this.initialX = this._image.stage.stageWidth / 2;
this.initialY = - 100.;
this.setInitialPosition();// Set the initial position of enemy aircraft
}
private setInitialPosition() {
this._image.x = this.initialX;
this._image.y = this.initialY;
}
onTick() {
this._image.y += 5; // Every frame the enemy will move down 5px
if (this._image.y > this._image.stage.stageHeight) { // Determine whether to move out of the screen
// Broadcast an off-screen event
this.dispatchEvent(new egret.Event('onEnemyDisappear')); }}}Copy the code