前言
最近有一款“合成大西瓜”的小游戏有点火,试玩了一下,玩法比较简单,实现难度也不大,所以参照游戏原型本人实现了一下,游戏开发次要应用了 Phaser 游戏框架,本文次要分享游戏性能的具体实现,对框架应用的 API 不会做过多介绍。
玩法剖析
首先简略介绍下游戏的玩法:管制水果从上方掉落,两个雷同水果会合成一个更大的水果,最终合成一个大西瓜,成果展现:
游戏的玩法在于正当管制着落的点防止空间的节约,在顶部有一条“死亡线”,当水果超过这个高度就完结,有点像俄罗斯方块,每合成一次水果都会得分,看谁能在游戏完结前或得更高的分数。
有多少种水果
游戏总共会呈现 11 种水果,通过察看,前 5 种水果会随机掉落,前面的水果都是合成才会呈现的
如何计算得分
每次合成新水果都会得分,按程序的话第一种是 1 分,第二种 2 分,第 10 种就是 10 分,最初合成大西瓜后是额定得 100 分:
疾速开始
游戏的根本玩法都曾经分明了,接下来就是开发了,首先咱们通过Github
上clone
一个 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)
preload
和create
都属于框架的生命周期,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),不定时推送文章。