“This article has participated in the good article call order activity, click to see: back end, big front end double track submission, 20,000 yuan prize pool for you to challenge!”

Step into Interactive Marketing II: Implementing a snake with Phaserjs

This article completes the complete code of the project address

Nervous about realizing a little game? Duck don’t have to! This is not to learn, you hit me! Today is still the primary article, hand in hand with you to achieve a snake. Big guy bypass. You can get:

  • Simple use of Phaserjs
  • A game template repository phaser + TS + WebPack5
  • A lovely snake

No more nonsense, let’s do it!

1. Analyze needs (Don’t panic, young man!)

The game can be broken down into the following modules:

  • A lovely snake
  • Randomly generated fruit
  • Wasd keyboard controls up, down, left and right
  • Eating fruit

Ii. Infrastructure Construction (TS+ Phaser)

Basic framework

It’s cleaner and there’s nothing. I’ve prepared a clean template for you in advance. Phaser + TS + WebPack5 project template

The required material

Just two pictures of the squares, which you can download in the full project. The complete code

Material configuration and preloading images

More basic, no longer tautology, before the article has talked about. Portal into interactive marketing 1: Using the Canvas Engine Phaser to achieve a push box H5 game combat

Three, the game scene

Let’s break it down:

The snake

Snakes are made up of segments, and each segment is a Sprite. So the snake should be an array.

fruit

Each fruit is also a node. To make it easier to expand later, we also initialize the fruit as an array

The direction of

Wasd has four directions: up, down, left and right. Let’s write it in terms of vectors. Of course you can use x,y can also be used. We’ll talk about that later.

Create the main sceneMainScene;

New scenes/SRC/mainScene. Ts

import FpsText from '.. /objects/fpsText'
export default class MainScene extends Phaser.Scene {
  fpsText
  /** Mouse event */
  public cursors: Phaser.Types.Input.Keyboard.CursorKeys
  Snake snake / * * * /
  private snakes: Phaser.GameObjects.Sprite[] = []
  Blended / * * * /
  private fruits: Phaser.GameObjects.Sprite[] = []
  /** 方向 */
  private moveVec: Phaser.Math.Vector2
  private updateDelay: number = 0
  constructor() {
    super({ key: 'MainScene'})}create() {
    this.fpsText = new FpsText(this)
    this.moveVec = new Phaser.Math.Vector2(0.0)
    this.initGame()
  }

  /* initialize game */
  initGame() {}

  /** Restart the level */
  resetGame() {
    this.initGame()
  }

  /** Determine whether to terminate */
  private gameOver(){}Frame / * * * /
  update() {
    this.fpsText.update()
  }
}

Copy the code

At this point, not surprisingly, you see a blank scene with an FPS in the top left corner.

4. Draw a small snake

Let’s define the width and height BOX_SIZE of each section. And initialize a SNAKE_START_LENGTH snake.

New SRC/constants/config. Ts


export const CONFIG = {
  /** Grid size */
  BOX_SIZE: 40./** Initial snake length */
  SNAKE_START_LENGTH: 4
}
Copy the code

Let’s add two new methods.

  • initSnake: Initializes snake. cycleSNAKE_START_LENGTHTo generate coordinates, and then to call the following
  • genneratorSnakeItem: Generates a snake item based on coordinates
 /** Initialize snake snake */
  private initSnake() {
    // Initialize the snake 4 elements, starting at 4, 4
    for (var i = 0; i < CONFIG.SNAKE_START_LENGTH; i++) {
      this.snakes[i] = this.genneratorSnakeItem({ x: (4 + i) * CONFIG.BOX_SIZE, y: 4 * CONFIG.BOX_SIZE })
    }
  }

  /** Generate snake item */
  private genneratorSnakeItem(pos: IPOS) {
    return this.add.sprite(pos.x, pos.y, 'snake').setDisplaySize(CONFIG.BOX_SIZE, CONFIG.BOX_SIZE).setOrigin(0.0)}Copy the code

InitGame = this.initSnake(); initGame = this.initSnake();

Five, let the snake snake run

You can’t just leave the snake lying there. How do you get it to move?

  • Listening to thewasdThe direction key
  • The snake moves according to the direction

Say first direction

So whatever bond wasd is, we’re going to use the coordinates x,y to indicate the direction.

  • wUp, so {x: 0, y: -1}
  • aTo the left, so {x: -1, y: 0}
  • sDown, so {x: 0, y: 1}
  • dUp, so {x: 1, y: -0}

In the frame event we add the coordinates of the snake to the corresponding orientation coordinates. The little snake can move.

The snake mobile

Again, the last item in the array is the head of the snake. For example, we press d to move the snake to the right. All we need to do is move the tail of the snake. The new coordinates of the tail are the coordinates of the head + the upper direction vector

Here’s a wonderful example of this:

Let’s start with the code: first create life cycle to initialize the keyboard event listener and direction vector.

create() {
    this.fpsText = new FpsText(this)
    this.cursors = this.input.keyboard.createCursorKeys()
    this.input.keyboard.on('keydown'.this.onListenerKeyDown.bind(this))
    this.moveVec = new Phaser.Math.Vector2(0.0)
    this.initGame()
}

/** Listen for keyboard events */
private onListenerKeyDown(e) {
    if (!Object.values(KEY_DIR).includes(e.key)) return
    let vec = new Phaser.Math.Vector2(0.0)
    switch(e? .key) {case KEY_DIR.UP:
        vec.y = -1
        break
      case KEY_DIR.LEFT:
        vec.x = -1
        break
      case KEY_DIR.DOWN:
        vec.y = 1
        break
      case KEY_DIR.RIGHT:
        vec.x = 1
        break
    }
    this.moveVec = vec
}
Copy the code

Then comes the key code. To control the speed, we add an updateDelay number. Plus 1 per frame. Then it only really renders once at 20 frames. This 20 can then be expanded to be the speed. Add the following code to the update event

  update(dt) {
    this.fpsText.update()
    this.updateDelay++
    if (this.moveVec.x === 0 && this.moveVec.y === 0) return
    if (this.updateDelay % 20= = =0) {
      this.updateDelay = 0;
      // The last item in the array is the snake head
      let snakesFirst = this.snakes[this.snakes.length - 1]
      let snakesLast = this.snakes.shift()
      // Get the point coordinates of the pre-movesnakesLast! .x = snakesFirst.x +this.moveVec.x * CONFIG.BOX_SIZE snakesLast! .y = snakesFirst.y +this.moveVec.y * CONFIG.BOX_SIZE
      // Add to the header
      // this.physics.add.overlap(snakesLast, this.fruits, this.collectFruit, null, this)
      this.snakes.push(snakesLast!) }}Copy the code

At this point, your little snake should be able to move. This part of the code is relatively long, if not easy to write, you can refer directly to the source code: complete code

Eat fruit

Produce fruit

When we generate fruit, all we do is place a fruit at random coordinates. The science of random coordinates is:

  • The coordinates need to be the same as the snake’s grid. We need to figure out what the whole screen isx,yHow many pieces of fruit can be placedmaxX,maxY. And then I’m going to take a random number in here.
  • Newly generated fruit cannot be generated inSnakes,. If it falls on the snake, continue to recurse to a new coordinate.

Add genneratorFruit and get random coordinate getNewFruitPos. To eat the first fruit, remember to call this.genneratorfruit () once in the initGame method.

 /** Generate fruit location, do not appear on the snake */
  private getNewFruitPos():IPOS {
    let maxX = Math.floor(+this.game.config.width / CONFIG.BOX_SIZE)
    let maxY = Math.floor(+this.game.config.height / CONFIG.BOX_SIZE / 3 * 2)
    let x = Phaser.Math.Between(1, maxX) * CONFIG.BOX_SIZE
    let y = Phaser.Math.Between(1, maxY) * CONFIG.BOX_SIZE
    let isOnSnake = this.snakes.some(item= > item.x === x && item.y === y)
    return isOnSnake ? this.getNewFruitPos() : { x, y }
  }

  /** Random fruit */
  private genneratorFruit() {
    let pos = this.getNewFruitPos()
    let fruit = this.add
      .sprite(pos.x, pos.y, 'fruit')
      .setDisplaySize(CONFIG.BOX_SIZE, CONFIG.BOX_SIZE).setOrigin(0.0)
    this.fruits.push(fruit)
  }
Copy the code

Eat fruit

We’ve already unified the grid coordinates of the fruit and the snake, so it’s easy to eat the fruit here.

  • 1. JudgeThe snakeIs there anyTouching the fruit.
  • 2, in the fruit coordinates here, create a new oneThe snake's item.
  • 3, destroy the current fruit, create a new one

The update method detects if the fruit this.collectFruit() is encountered. Create the collectFruit method

/** Eat fruit */
  private collectFruit() {
    this.fruits.map((_fruit, index) = > {
      if (_fruit.x === this.snakes[this.snakes.length - 1].x && _fruit.y === this.snakes[this.snakes.length - 1].y) {
        // Add a new item based on the current fruit coordinates
        this.snakes.unshift(this.genneratorSnakeItem({ x: _fruit.x, y: _fruit.y }))
        // Destroy the fruit
        this.fruits.splice(index)[0]
        _fruit.destroy()
        // Generate the next fruit
        this.genneratorFruit()
      }
    })
  }
Copy the code

Open up your browser? Your snake is just not done!

Hit yourself and hit the wall

We need to add two new checks in the update method, because the snake head is in the first place, we only need to determine the coordinate of the element snake head.

  • You can’t touch yourselfthis.checkSelf(snakesFirst)
  • Can’t hit the wallthis.checkWall(snakesFirst)
/** Do you touch yourself */
  private checkSelf(snakesFirst) {
    let isCheckSelf = this.snakes.filter(item= > item.x === snakesFirst.x && item.y === snakesFirst.y);
    // Select the element whose coordinates are the same as the head of the snake, so match List>1
    if(isCheckSelf.length > 1) alert('I met myself.')}/* whether to hit the wall */
  private checkWall(snakesFirst) {
    let isCheckWall = snakesFirst.x >=  this.game.config.width  || snakesFirst.x < 0 || snakesFirst.y >= this.game.config.height || snakesFirst.y < 0;
    // Select the element whose coordinates are the same as the head of the snake, so match List>1
    if(isCheckWall) alert('Hit the wall')}Copy the code

conclusion

A simple little game is done. Collision detection encapsulated in PhaserJS. You can use it directly. Because the collision here is easy, we’re going to do it ourselves.

If you are interested in mobile terminal, interactive marketing, H5 games, click like, click follow. The level of difficulty will continue to increase.