使用 Phaser3+Matter.js 實現“合成大西瓜”遊戲

前言

最近有一款“合成大西瓜”的小遊戲有點火,試玩了一下,玩法比較簡單,實現難度也不大,所以參照遊戲原型自己實現了一下,遊戲開發主要使用了 Phaser 遊戲框架,本文主要分享遊戲功能的具體實現,對框架使用的 API 不會做過多介紹。

玩法分析

首先簡單介紹下游戲的玩法:控制水果從上方掉落,兩個相同水果會合成一個更大的水果,最終合成一個大西瓜,效果展示:

遊戲的玩法在於合理控制下落的點避免空間的浪費,在頂部有一條“死亡線”,當水果超過這個高度就結束,有點像俄羅斯方塊,每合成一次水果都會得分,看誰能在遊戲結束前獲得更高的分數。

有多少種水果

遊戲總共會出現 11 種水果,經過觀察,前 5 種水果會隨機掉落,後面的水果都是合成纔會出現的

如何計算得分

每次合成新水果都會得分,按順序的話第一種是 1 分,第二種 2 分,第 10 種就是 10 分,最後合成大西瓜後是額外得 100 分:

快速開始

遊戲的基本玩法都已經清楚了,接下來就是開發了,首先我們通過Githubclone一個 phaser3 的腳手架[1]來進行開發,我們首選 Typescript 版本的,對於這種複雜的框架,類型提示真的非常方便。

#! /bin/bash

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

#啓動
$npm run watch

安裝依賴並啓動後,進入src/game.ts,把原來的一些示例代碼刪掉,結果如下:

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

  preload () {
  }

  create () {
  }
}

const config = {
  type: Phaser.AUTO,
  backgroundColor'#125555',
  width800,
  height600,
  scene: Demo
}

const game = new Phaser.Game(config)

preloadcreate都屬於框架的生命週期,preload主要用於預先下載資源,create用於創建對象或事件。

修改 config 參數

修改遊戲初始化參數,指定使用 Matter.js 物理引擎,縮放模式通常設置爲等比例縮放模式Phaser.Scale.FIT

const config = {
  type: Phaser.AUTO,
  backgroundColor'#ffe8a3'// 改爲遊戲的背景顏色
  mode: Phaser.Scale.FIT, // 縮放模式
  physics: {
    default'matter'// 使用matterjs物理引擎
    matter: {
      gravity: {
        y2
      },
      debugtrue // 開啓調試
    }
  },
  widthwindow.innerWidth,
  heightwindow.innerHeight,
  scene: Demo
}

加載資源

接下在preload函數中加載準備好的圖片, 前面我已經準備好了 11 中類型水果的圖片,爲了方便開發,分別命名爲 1-11.png

preload () {
  // 11種類型水果
  for (let i = 1; i <= 11; i++) {
    this.load.image(`${i}``assets/${i}.png`)
  }
  // 地板圖片
  this.load.image('ground''assets/ground.png')
}

新建水果

加載資源後,我們先來創建遊戲中最主要的對象水果,遊戲中水果出現的情況有兩種,一種是在頂部落下,一種是碰撞後生成,除了位置不同,還有狀態和類型也不同,用一個表示如下:

出現位置 狀態 類型
頂部 先靜止點擊後落下 前 5 種隨機
合成後的位置 非靜止 上一種+1

把不同的部分作爲參數,創建一個createFruite函數:

 /**
     * 添加一個水果
     * @param x 座標x
     * @param y 座標y
     * @param key 瓜的類型
     * @param isStatic 是否靜止
     */

  createFruite (x: number, y: number, isStatic = true, key?: string,) {
    if (!key) {
      // 頂部落下的瓜前5個隨機
      key = `${Phaser.Math.Between(15)}`
    }
    // 創建
    const fruit = this.matter.add.image(x, y, key)
    // 設置物理剛體
    fruit.setBody({
      type'circle',
      radius: fruit.width / 2
    }, {
      isStatic,
      label: key // 設置label 用於後續碰撞判斷是否同一類型
    })
    // 添加一個動畫效果
    this.tweens.add({
      targets: fruit,
      scale: {
        from0,
        to1
      },
      ease'Back',
      easeParams: [3.5],
      duration200
    })
    return fruit
  }

create函數中創建地板和生成水果

create(){

    //設置邊界
    this.matter.world.setBounds()
    //添加地面
    const groundSprite = this.add.tileSprite(WINDOW_WIDTH / 2, WINDOW_HEIGHT - 127 / 2, WINDOW_WIDTH, 127'ground')
    this.matter.add.gameObject(groundSprite, { isStatictrue })

    //初始化第一個一個水果
    const x = WINDOW_WIDTH / 2
    const y = WINDOW_HEIGHT / 10
    let fruit = this.createFruite(x, y)

}

綁定點擊屏幕事件

接下來就是添加事件點擊屏幕的時候水果往下掉,並生成一個新的水果,新水果生成的時間點就設在落下後一秒鐘

create(){
     ...
    //綁定pointerdown事件
    this.input.on('pointerdown', (point) => {
        if (this.enableAdd) {
            this.enableAdd = false
            //先x軸上移動到手指按下的點
            this.tweens.add({
                targets: fruit,
                x: point.x,
                duration100,
                ease'Power1',
                onComplete() => {
                    //取消靜止狀態,讓物體掉落
                    fruit.setStatic(false)
                    //1s後生成新的水果
                    setTimeout(() => {
                        fruit = this.createFruite(x, y)
                        this.enableAdd = true
                    }, 1000);
                }
            })
        }
    }
}

物體碰撞事件

完成水果生成後,下一步就是添加碰撞事件,在phaser中我們可以使用this.matter.world.on('collisionstart',fn)來監聽物體的碰撞事件,fn中會返回兩個相互碰撞的物體對象,我們根據前面設置的label值就能判斷是否同一組,並進行後續操作

create(){
  ...
  this.matter.world.on('collisionstart', (event, bodyA, bodyB) => {
      const notXigua = bodyA.label !== '11'   //非大西瓜
      const same = bodyA.label === bodyB.label //相同水果
      const live = !bodyA.isStatic && !bodyB.isStatic //非靜態
      if (notXigua && same && live) {
          //設置爲Static,這樣可以調整物體位置,使物體重合
          bodyA.isStatic = true
          bodyB.isStatic = true
          const { x, y } = bodyA.position
          const lable = parseInt(bodyA.label) + 1
          //添加兩個動畫合併的動畫
          this.tweens.add({
              targets: bodyB.position,
              props: {
                  x: { value: x, ease'Power3' },
                  y: { value: y, ease'Power3' }
              },
              duration150,
              onComplete() => {
                  // 物體銷燬
                  bodyA.gameObject.alpha = 0
                  bodyB.gameObject.alpha = 0
                  bodyB.destroy()
                  bodyA.destroy()
                  //合成新水果
                  this.createFruite(x, y, false`${lable}`)

              }
          })
      }
  })
}

到這一步我們就基本完成了遊戲的核心部分,先看下效果:

合成後只是簡單的銷燬物體,有時間的話可以加入一些幀動畫之類的效果會更好,這裏就不加了,接下來繼續加上結束判定和得分。

結束判斷

前面提到,當落下的球超過指定的高度遊戲即結束,我們還是使用一個碰撞檢測來實現,創建一個矩形物體作爲我們的“結束線”,當矩形碰到物體的時候即表示空間已經不夠遊戲結束,還有一點需要特殊處理的是當我們點擊水果落下時是會碰到線的,這次碰撞需要過濾掉

create(){
...
//線創建在水果200px下的位置
const endLineSprite = this.add.tileSprite(WINDOW_WIDTH / 2, y + 200, WINDOW_WIDTH, 8'endLine'  )
//設爲隱藏
endLineSprite.setVisible(false)
//設置物理效果
this.matter.add.gameObject(endLineSprite, {
  //靜止
  isStatictrue,
  //傳感器模式,可以檢測到碰撞,但是不會對物體產品效果
  isSensortrue,
  //物體碰撞回調,
  onCollideCallback() => {
     //落下時碰到線不觸發
     if(this.enableAdd){
        // 遊戲結束
        console.log('end')
     }
  })
 })
}

得分

得分的邏輯其實比較簡單了,在合成成功後加入代碼

let score = parseInt(bodyA.label)
this.score += score
//合成西瓜額外加100分
if (score === 10) {
    this.score += 100
}
this.scoreText.setText(this.score)
//
create(){
    //創建一個Text
    this.scoreText = this.add.text(3020`${this.score}`, { font'90px Arial Black'color'#ffe325' }).setStroke('#974c1e'16)
}

最後

到這裏遊戲的基礎玩法就開發結束了,藉助 Phaser 框架基本算能快速的開發遊戲的原型,如果你是新手對 H5 遊戲開發感興趣的話,那麼 Phaser 是一個非常容易上手的框架,api 的設計也比較友好,還有大量的 demo 可以學習,或許下一個爆款遊戲就出自於你呢。

本項目源碼[2]已經發布到 github 倉庫,感興趣的可以自行查看

參考文章

如何隨手合成大西瓜,把把 1000 分?手殘必看的高分攻略來了!

Phaser[3]

註釋

[1]

腳手架: https://github.com/photonstorm/phaser3-typescript-project-template

[2]

源碼: https://github.com/eijil/hexigua

[3]

Phaser: https://phaser.io/


本文分享自微信公衆號 - 凹凸實驗室(AOTULabs)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章