The introduction

This project uses H5 Canvas to draw aircraft war game and ES6 class object oriented development. The purpose is to practice js object oriented development ability and master canvas API.

1. Case demonstration

Plane war game, using canvas drawing, the program is only for practice, to achieve the main functions, without realizing the game start interface, end judgment, etc., the effect is shown below



Two, knowledge points

  • Basic use of Canvas
  • Basic use of the requestAnimationFrame for animation
  • Collision, boundary detection
  • Js Object-oriented Programming (OOP)
  • Game button event handling

3. The object in the case

  • Stage Stage
  • SceneBase scene, the base class of the scene
  • GameScene GameScene
  • Sprite, the base class of the game
  • Enemy Enemy planes flying in and out class
  • Player Fighter class
  • Bullet shot class
  • Particle Particle class

Four, the realization of ideas

1. The Sprite class

The game is the base class for all entities (Player | Enemy | Bullet | Particle were inherited)

Abstract out the common attribute methods as follows:

constructor() { this.unit = 10; // this.vx = 0.2; // this. Vy = 0.2; // this. SizeX = 2; this.sizeY = 2; this.posX = 0; // this. PosY = 0; // the y coordinate this.color ='# 369';
  this.alive = true; This. birth = new Date(); }getWidth() {... } // Get the true size of the objectrender() {... } // Draw this object on the canvasCopy the code

Because the game is a square, I don’t want to write 20, 50, 100 pixels every time, it looks too big. Define a minimum unit and use getWidth() to calculate the actual size, unit * size

2. The Particle classes

To control particle effects in the game, inherit from Sprite class, extension method:

This.life // The object life, which controls when the object is destroyedupdate() {// Update the object state and call the render method of the parent classif (new Date() - this.birth > this.life) {
  	this.alive = false;
  }
  this.posX += this.vx;
  this.posY += this.vy;
  this.render();
}
Copy the code

3. The Bullet class

Bullets in the game, responsible for rendering bullets in the game, inherited from the Sprite class, extension method:

constructor(scene) { super(); this.scene = scene; this.vx = 0; this.vy = -1; this.state = new Date(); this.particleCB = 500; this.type = 0; 0 enemy, 1 player}collide() {... } // Collision detectioncreateParticle() {... } // Bullet play example effectupdate() {... } // Update the object state and call the render method of the parent classCopy the code

Collision detection in the bullet class only determines the collision with the scene

Collide (w, h) {if (this.posX < 0 - this.getWidth() ||
    this.posX > w + this.getWidth() ||
    this.posY < 0 - this.getWidth() ||
    this.posY > h + this.getWidth()) {
    this.alive = false; }}Copy the code

Bullets have a particle effect while running, which is implemented in createParticle()

letp = new Particle({... }); An instance this. / / the new scene. Particles. Push (p); // Add the particle to the sceneupdate() {// Update the object stateif (new Date() - this.state >= this.particleCB) {
    this.createParticle();
    this.state = new Date();
  }
  this.posX += this.vx;
  this.posY += this.vy;
  this.render();
}
Copy the code

4. The Enemy classes

Enemy planes in the game, responsible for rendering the game enemy planes, inherit from Sprite class, extension method:

Constructor (scene) {super() this.scene = scene this.dx =.005 // x direction this.dy = 0 // y direction this The enemy plane changed direction CB this.canchange =true
  this.init()
}
init() {this.bulletcb = 2000 // CB this.attacktime = -1 // can attackTime, This. boomConfig = new Map([2, [1.5, 1, 0.5]], // Enemy size is 2, Split into 1.5 1 0.5 particle size [3, [2, 2, 1]] // Enemy plane size is 3, split into 3 3 1 particle size])}collide() {... } // Collision detectionattack() {... } // Fire the bulletboom() {... } // Enemy plane hitchangeX() {... } // Enemy AIchangeY() {... } // Enemy AIchange() {... } // Enemy AIupdate() {... } // Update the object state and call the render method of the parent classCopy the code
The reason for this configuration in boomConfig is that there are only three sizes to initialize enemy planes in the scene class. This is not considered flexible. You can add minSize and maxSize to the class to control the size of the enemy planes generated. // GameScene -> createEnemy() e.sizeX = Math.random() * 3 + 1 | 0;Copy the code

5. The Player class

The Player class is a lot more complicated. In addition to the Enemy class attribute method, you also need to register keyboard events.

  init() {
    this.keydowns = {};
    this.actions = {};
    this.bulletCB = 800;
    this.attackTime = -1;
    this.registAction('a', this.moveLeft.bind(this));
    this.registAction('d', this.moveRight.bind(this));
    this.registAction('w', this.moveUp.bind(this));
    this.registAction('s', this.moveDown.bind(this));
    this.bindEvent();
  }
  registAction(key, callback) {
    this.actions[key] = callback;
  }
  bindEvent() {
    window.addEventListener('keydown', ev => {
      this.keydowns[ev.key] = true;
    })
    window.addEventListener('keyup', ev => {
      this.keydowns[ev.key] = false; })}update() { this.edge(); // Boundary detection, limit the player in the scene this.damage(); // Damage detection, bullets, enemy aircraftlet actions = Object.keys(this.actions);
    actions.forEach(k => this.keydowns[k] && this.actions[k]())
    this.attack();
    this.render();
  }  
Copy the code

The key response registers the events using registAction by storing the key state and callback functions as objects, so the code looks much cleaner and just needs to be iterated through in update().

For boundary detection and injury detection, it is an interval judgment in essence. The difference is that boundary detection is limited in an interval, that is, if it is not in this interval, it needs to be limited (or corrected). And damage detection is if in this range then trigger the corresponding event, such as our bullet and the enemy aircraft collision, the enemy aircraft blow up, the bullet destruction.

Here’s a diagram of what simple collision detection looks like, like breaking bricks

6. Scene class

A base class, SceneBase, was extracted from the scene class. Considering that there would be the main scene of the game, the beginning scene, the setting scene and so on in the later expansion, a layer was removed from the scene class.

The SceneBase class doesn’t define much either. It has two static properties, WIDTH and HEIGHT, the size of the scene. Properties like this don’t have to be in each instance.

The GameScene class, which inherits from the SceneBase class and is also the engine of the game, does all the work.

The init method initializes the parameters of the game,

  init() { super.init(); this.setEnemyCD = 3000; CB this.createEnemyTime = -1 this. Bullets = []; // All bullets in the scene. Enemys = []; // All enemies in the scene this.particles = []; // The particle in the scene this.initPlayer(); this.initEnemy(); this.warn =false; // If the player is hit this.killCount = 0; }Copy the code

The update method handles updates to all entities in the scene uniformly

  update() {
    super.update();
    this.initEnemy();
    if (this.warn) {
      this.warning();
      this.warn = false;
    } else {
      ctx.clearRect(0, 0, SceneBase.WIDTH, SceneBase.HEIGHT);
    }
    this.player.update();
    this.updateBullet();
    this.updateParticle();
    this.updateEnemy();
    this.updateInfo();
  }
Copy the code

7. Stage classes

Responsible for managing all the scenarios,

  constructor() {
    this.manager = []
    this.init()
  }
  add() {... } // Add the scene to managerremove() {... } // Remove static from the Manager scenariofreeze() {... } // Freeze the manager scenario staticunfreeze() {... } // Unfreeze the manager scenariobindEvent() {... } // Some event responsesCopy the code

This thing is not much reflected in this case. I extracted it from the case of cascading ablation game and applied it in the switching of multiple scenes, such as the game play mode and the game custom editing mode.

You can check out algorithms for detecting and matching target patterns in cascade ablation games

Github: Canvas Plane battle (github Mountain is not going today, code is not submitted)

— — — — — — — — — — — — — — — — — — — — — end — — — — — — — — — — — — — — — — — — — — —