前言

最近有一款“合成大西瓜”的小游戏有点火,试玩了一下,玩法比较简单,实现难度也不大,所以参照游戏原型本人实现了一下,游戏开发次要应用了 Phaser 游戏框架,本文次要分享游戏性能的具体实现,对框架应用的 API 不会做过多介绍。

玩法剖析

首先简略介绍下游戏的玩法:管制水果从上方掉落,两个雷同水果会合成一个更大的水果,最终合成一个大西瓜,成果展现:

游戏的玩法在于正当管制着落的点防止空间的节约,在顶部有一条“死亡线”,当水果超过这个高度就完结,有点像俄罗斯方块,每合成一次水果都会得分,看谁能在游戏完结前或得更高的分数。

有多少种水果

游戏总共会呈现 11 种水果,通过察看,前 5 种水果会随机掉落,前面的水果都是合成才会呈现的

如何计算得分

每次合成新水果都会得分,按程序的话第一种是 1 分,第二种 2 分,第 10 种就是 10 分,最初合成大西瓜后是额定得 100 分:

疾速开始

游戏的根本玩法都曾经分明了,接下来就是开发了,首先咱们通过Githubclone一个 phaser3 的脚手架来进行开发,咱们首选 Typescript 版本的,对于这种简单的框架,类型提醒真的十分不便。

#! /bin/bash$git clone git@github.com: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',  width: 800,  height: 600,  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: {        y: 2      },      debug: true // 开启调试    }  },  width: window.innerWidth,  height: window.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(1, 5)}`    }    // 创立    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: {        from: 0,        to: 1      },      ease: 'Back',      easeParams: [3.5],      duration: 200    })    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, { isStatic: true })    //初始化第一个一个水果    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,                duration: 100,                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' }              },              duration: 150,              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, {  //静止  isStatic: true,  //传感器模式,能够检测到碰撞,然而不会对物体产品成果  isSensor: true,  //物体碰撞回调,  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(30, 20, `${this.score}`, { font: '90px Arial Black', color: '#ffe325' }).setStroke('#974c1e', 16)}

最初

到这里游戏的根底玩法就开发完结了,借助 Phaser 框架根本算能疾速的开发游戏的原型,如果你是老手对 H5 游戏开发感兴趣的话,那么 Phaser 是一个非常容易上手的框架,api 的设计也比拟敌对,还有大量的 demo 能够学习,或者下一个爆款游戏就出自于你呢。

本我的项目源码曾经公布到 github 仓库,感兴趣的能够自行查看

参考文章

如何顺手合成大西瓜,把把 1000 分?手残必看的高分攻略来了!

Phaser


欢送关注凹凸实验室博客:aotu.io

或者关注凹凸实验室公众号(AOTULabs),不定时推送文章。