preface

Recently have a “synthetic big watermelon” little game with ignition, try once, style simple, implementation difficulty also is not big, so the reference prototype implementation and his own game development mainly use the Phaser framework, this paper share the functionality of the specific implementation, the framework USES the API will not do too much introduction.

Mechanics analysis

First of all, a brief introduction to the gameplay of the game: control the fruit to fall from the top, two identical fruits will be combined into a larger fruit, and finally a large watermelon, the effect is shown:

There is a “death line” at the top of the game. When the fruit passes this height, it ends. It is a bit like Tetris.

How many kinds of fruit are there

There will be a total of 11 kinds of fruit in the game. After observation, the first 5 fruits will fall randomly, and the following fruits will appear only after synthesis

How to calculate the score

Each time you combine a new fruit, you get 1 point for the first fruit, 2 points for the second fruit, 10 points for the 10th fruit, and an extra 100 points for the big watermelon:

Quick start

The basic gameplay is already clear, so development is in order. First we clone a Phaser3 scaffold on Github. We prefer Typescript, and type hints are really handy for such a complex framework.

#! /bin/bash

$git clone [email protected]:photonstorm/phaser3-typescript-project-template.git hexigua
$cd hexigua
$npm install

# start
$npm run watch

Copy the code

After installing the dependency and starting it, go to SRC /game.ts and delete some of the original sample code, resulting in the following:

import 'phaser'
export default class Demo extends Phaser.Scene {
  constructor () {
    super('demo')
  }

  preload () {
  }

  create () {
  }
}

const config = {
  type: Phaser.AUTO,
  backgroundColor: '# 125555'.width: 800.height: 600.scene: Demo
}

const game = new Phaser.Game(config)
Copy the code

Both Preload and Create are part of the framework’s life cycle, with Preload primarily used to pre-load resources and CREATE used to create objects or events.

Modifying config Parameters

Modify the game initialization parameters to specify the use of Matter. Js physics engine, Scale mode is usually set to equal Scale Scale mode phaser.scale.fit,

const config = {
  type: Phaser.AUTO,
  backgroundColor: '#ffe8a3'.// Change the background color of the game
  mode: Phaser.Scale.FIT, // Zoom mode
  physics: {
    default: 'matter'.// Use the MatterJS physics engine
    matter: {
      gravity: {
        y: 2
      },
      debug: true // Enable debugging}},width: window.innerWidth,
  height: window.innerHeight,
  scene: Demo
}
Copy the code

Load resources

Next, load the prepared images in the preload function. I have prepared the images of 11 types of fruits before, and named them 1-11.png for the convenience of development

preload () {
  // 11 types of fruit
  for (let i = 1; i <= 11; i++) {
    this.load.image(`${i}`.`assets/${i}.png`)}// Floor image
  this.load.image('ground'.'assets/ground.png')}Copy the code

New fruit

After loading resources, we will first create the main object fruit in the game. There are two kinds of fruit in the game, one is falling at the top and the other is generated after collision. In addition to different positions, there are also different states and types, which can be expressed as follows:

A position state type
At the top of the Pause click and then drop The first five are random
The position after synthesis The static A + 1

Create a createFruite function with the different parts as arguments:

 /** * Add a fruit *@param X coordinate x star@param Y coordinate y star@param Type of key melon *@param IsStatic Indicates whether to be static */
  createFruite (x: number, y: number, isStatic = true, key? : string,) {if(! key) {// The first 5 melons that fall from the top are random
      key = `${Phaser.Math.Between(1.5)}`
    }
    / / create
    const fruit = this.matter.add.image(x, y, key)
    // Set the physical rigidbody
    fruit.setBody({
      type: 'circle'.radius: fruit.width / 2
    }, {
      isStatic,
      label: key // Set label for subsequent collisions to determine whether the type is the same
    })
    // Add an animation effect
    this.tweens.add({
      targets: fruit,
      scale: {
        from: 0.to: 1
      },
      ease: 'Back'.easeParams: [3.5].duration: 200
    })
    return fruit
  }
Copy the code

Create the floor and generate the fruit in the create function

create(){

    // Set boundaries
    this.matter.world.setBounds()
    // Add ground
    const groundSprite = this.add.tileSprite(WINDOW_WIDTH / 2, WINDOW_HEIGHT - 127 / 2, WINDOW_WIDTH, 127.'ground')
    this.matter.add.gameObject(groundSprite, { isStatic: true })

    // Initialize the first fruit
    const x = WINDOW_WIDTH / 2
    const y = WINDOW_HEIGHT / 10
    let fruit = this.createFruite(x, y)

}
Copy the code

Bind the click screen event

The next step is to add the event that the fruit will drop when the screen is clicked and a new fruit will be generated at the point one second after the drop

create(){...// Bind pointerDown events
    this.input.on('pointerdown'.(point) = > {
        if (this.enableAdd) {
            this.enableAdd = false
            // First move the X-axis to the point where the finger presses
            this.tweens.add({
                targets: fruit,
                x: point.x,
                duration: 100.ease: 'Power1'.onComplete: () = > {
                    // Cancel the rest state and drop the object
                    fruit.setStatic(false)
                    // New fruit is generated after 1s
                    setTimeout(() = > {
                        fruit = this.createFruite(x, y)
                        this.enableAdd = true
                    }, 1000); }})}}}Copy the code

Object collision event

Once the fruit is generated, the next step is to add collision events. In phaser we can use this.matter.world.on(‘ Collisionstart ‘,fn) to listen for collision events. Fn returns two colliding object objects, Based on the label value set earlier, we can determine whether the group is the same and perform subsequent operations

create(){...this.matter.world.on('collisionstart'.(event, bodyA, bodyB) = > {
      constnotXigua = bodyA.label ! = ='11'   // Non-large watermelon
      const same = bodyA.label === bodyB.label // Same fruit
      constlive = ! bodyA.isStatic && ! bodyB.isStatic/ / not static
      if (notXigua && same && live) {
          // Set it to Static so that objects can be positioned to overlap
          bodyA.isStatic = true
          bodyB.isStatic = true
          const { x, y } = bodyA.position
          const lable = parseInt(bodyA.label) + 1
          // Add an animation that combines two animations
          this.tweens.add({
              targets: bodyB.position,
              props: {
                  x: { value: x, ease: 'Power3' },
                  y: { value: y, ease: 'Power3'}},duration: 150.onComplete: () = > {
                  // Object destruction
                  bodyA.gameObject.alpha = 0
                  bodyB.gameObject.alpha = 0
                  bodyB.destroy()
                  bodyA.destroy()
                  // Synthesize new fruit
                  this.createFruite(x, y, false.`${lable}`}})}})}Copy the code

At this point we have basically completed the core of the game. Let’s take a look at the results:

After composition, it is just a simple destruction of the object. If you have time, you can add some frame animation and so on. It will be better.

The end of the judgment

Mentioned earlier, when the falling ball over specify the height of the game is over, we still use a collision detection, create a rectangular object as our “end of the line,” said when rectangular touching objects that space is not enough game over, it is also need special handling is when we click the fruit drop is line will face, need to filter out the collision

create(){...// The line is created 200px below the fruit
const endLineSprite = this.add.tileSprite(WINDOW_WIDTH / 2, y + 200, WINDOW_WIDTH, 8.'endLine'  )
// Set it to hide
endLineSprite.setVisible(false)
// Set the physics effect
this.matter.add.gameObject(endLineSprite, {
  / / static
  isStatic: true.// Sensor mode, which can detect collisions, but has no effect on object products
  isSensor: true.// Object collision callback,
  onCollideCallback: () = > {
     // The line does not trigger when falling
     if(this.enableAdd){
        // Game over
        console.log('end')}})})}Copy the code

score

The scoring logic is actually relatively simple, after the successful composition of the code

let score = parseInt(bodyA.label)
this.score += score
// Add 100 points to synthetic watermelon
if (score === 10) {
    this.score += 100
}
this.scoreText.setText(this.score)
//
create(){
    // Create a Text
    this.scoreText = this.add.text(30.20.`The ${this.score}`, { font: '90px Arial Black'.color: '#ffe325' }).setStroke('#974c1e'.16)}Copy the code

The last

Phaser is an easy framework to use if you are new to H5 game development. The API design is friendly, and there are plenty of demos to learn. You could be the next big hit.

This project source has been released to github repository, interested can view

Refer to the article

How to synthesize big watermelon conveniently, handle 1000 points? Hand damage must see the high score strategy is coming!

Phaser