We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
微信小游戏推出已有几天了,这个功能对小程序和小游戏的推动影响不用多说,大家赶紧摩拳擦掌往上撸就可以了。关于如何开发官方文档已经说明了,这篇则是对官方的打飞机demo一些小改造。
打飞机
微信开发者工具
HTML5
Adapter
wx
使用无AppID模式创建一个微信小游戏后可以看到官方demo,其中入口文件和配置文件:game.js和game.json。game.js引入并初始化包含整个打飞机的游戏场景、参与者(玩家飞机和敌方飞机)、游戏逻辑的主函数的main.js。在main.js中我们可以发现由于Adapter的存在,这里的代码和我们平常的代码写法没什么差异了。游戏的主逻辑如下图:
无AppID
game.js
game.json
main.js
在loop中,玩家每隔20帧射一次,每隔60帧生成新的敌机。每帧检查玩家和敌机是否死亡,玩家死亡游戏结束,敌机死亡分数+1。只有玩家可以射击,且射击方式固定,通过躲避敌机生存。接下来我们针对这些进行改造,提升游戏的可玩性和挑战性。
首先用编辑器打开player/index.js,将等级逻辑加入到玩家的类中。
player/index.js
export default class Player extends Sprite { constructor() { super(PLAYER_IMG_SRC, PLAYER_WIDTH, PLAYER_HEIGHT) // 玩家默认处于屏幕底部居中位置 this.x = screenWidth / 2 - this.width / 2 this.y = screenHeight - this.height - 30 // 用于在手指移动的时候标识手指是否已经在飞机上了 this.touched = false this.bullets = [] // 初始化事件监听 this.initEvent() this.playerLevel = 1; } get level () { return this.playerLevel; } set level (level) { this.playerLevel = Math.min(level, 3); }
接下来在main.js的update函数加入升级逻辑。
update
// 其他代码... update() { this.bg.update(); databus.bullets.concat(databus.enemys).forEach(item => { item.update(); }); this.enemyGenerate(); this.player.level = Math.max(1, Math.ceil(databus.score / 30)); this.collisionDetection(); } // 其他代码...
好的,到此玩家已经可以正常升级了。那么该给予玩家奖励品了。在player/index.js的shoot函数中我们修改射击的逻辑。玩家1级时只有中间的射击口,2级有左边和中间的射击口,3级有左中右三个射击口。
shoot
// ...其他代码 /** * 玩家射击操作 * 射击时机由外部决定 */ shoot() { for(let i = 0; i < this.level; i++) { const bullet = databus.pool.getItemByClass('bullet', Bullet); const middle = this.x + this.width / 2 - bullet.width / 2; const x = !i ? middle : (i % 2 === 0 ? middle + 30 : middle - 30); bullet.init( x, this.y - 10, 10 ) databus.bullets.push(bullet) } } // ...其他代码
武器的最终形态如图, 这时候的玩家已经可以为所欲为了<_<,实际上都不需要躲避了。。。:
为了对抗愚昧的玩家,不让他们为所欲为,最后没兴趣玩下去~~,敌机装备武器,反击开始。
首先敌机的子弹是向下,所以复制一份images/bullet.png,并颠倒保存为images/bullet-down.png, 然后我们重用js/player/bullet.js,在构造函数处增加敌机的子弹配置项,并修改敌人子弹更新逻辑。
images/bullet.png
images/bullet-down.png
js/player/bullet.js
const BULLET_IMG_SRC = 'images/bullet.png' const BULLET_DOWN_IMG_SRC = 'images/bullet-down.png' const BULLET_WIDTH = 16 const BULLET_HEIGHT = 30 const __ = { speed: Symbol('speed') } let databus = new DataBus() export default class Bullet extends Sprite { constructor({ direction } = { direction: 'up' }) { super(direction === 'up' ? BULLET_IMG_SRC : BULLET_DOWN_IMG_SRC, BULLET_WIDTH, BULLET_HEIGHT) this.direction = direction; // 其他代码... // 每一帧更新子弹位置 update() { if (this.direction === 'up') { this.y -= this[__.speed] // 超出屏幕外回收自身 if ( this.y < -this.height ) databus.removeBullets(this) } else { this.y += this[__.speed] // 超出屏幕外回收自身 if ( this.y > window.innerHeight + this.height ) databus.removeBullets(this) } } }
接着在js/npc/enemy.js结尾部分为敌人装备武器, 子弹速度为敌人自身速度+5
js/npc/enemy.js
+5
import Animation from '../base/animation' import DataBus from '../databus' import Bullet from '../player/bullet'; const ENEMY_IMG_SRC = 'images/enemy.png' // 其他代码... update() { this.y += this[__.speed] // 对象回收 if ( this.y > window.innerHeight + this.height ) databus.removeEnemey(this) } /** * 敌机射击操作 * 射击时机由外部决定 */ shoot() { const bullet = databus.pool.getItemByClass('bullet', Bullet); bullet.init( this.x + this.width / 2 - bullet.width / 2, this.y + 10, this[__.speed] + 5 ); databus.bullets.push(bullet); } }
接下来,在js/main.js中加入敌机的射击逻辑,敌机移动5次、60次时设计。
js/main.js
// 其他代码... let ctx = canvas.getContext("2d"); let databus = new DataBus(); const ENEMY_SPEED = 6; // 其他代码... /** * 随着帧数变化的敌机生成逻辑 * 帧数取模定义成生成的频率 */ enemyGenerate(playerLevel) { if (databus.frame % 60 === 0) { let enemy = databus.pool.getItemByClass("enemy", Enemy); enemy.init(ENEMY_SPEED); databus.enemys.push(enemy); } } // 其他代码... // 实现游戏帧循环 loop() { databus.frame++; this.update(); this.render(); if (databus.frame % 20 === 0) { this.player.shoot(); this.music.playShoot(); } databus.enemys.forEach(enemy => { const enemyShootPositions = [ -enemy.height + ENEMY_SPEED * 5, -enemy.height + ENEMY_SPEED * 60 ]; if (enemyShootPositions.indexOf(enemy.y) !== -1) { enemy.shoot(); this.music.playShoot(); } }); // 游戏结束停止帧循环 if (databus.gameOver) { this.touchHandler = this.touchEventHandler.bind(this); canvas.addEventListener("touchstart", this.touchHandler); this.gameinfo.renderGameOver(ctx, databus.score); return; } window.requestAnimationFrame(this.loop.bind(this), canvas); }
这时候我们发现,由于不明宇宙的干扰射线的影响,玩家和敌机的子弹不受控制的乱飞。接下来我们就来恢复世界的秩序吧 ;
经侦测发现是对象池pool的获取逻辑问题导致子弹不受控问题,我们需要区分获取玩家、每个敌机的子弹
pool
首先,对象获取我们加入对象属性的判断,当有传入对象属性时,我们获取所有属性值一致的已回收对象,若没有找到或者对象池为空时,则用属性创建新对象
/** * 根据传入的对象标识符,查询对象池 * 对象池为空创建新的类,否则从对象池中取 */ getItemByClass(name, className, properties) { let pool = this.getPoolBySign(name) if (pool.length === 0) return new className(properties); if (!properties) return pool.shift(); const index = pool.findIndex(item => { return Object.keys(properties).every(property => { return item[property] === properties[property]; }); }); return index !== -1 ? pool.splice(index, 1)[0] : new className(properties) }
相应的我们需要给每个子弹设置归属,在js/player/bullet.js中Bullet类修改constructor
Bullet
constructor
export default class Bullet extends Sprite { constructor({ direction, owner } = { direction: 'up' }) { super(direction === 'up' ? BULLET_IMG_SRC : BULLET_DOWN_IMG_SRC, BULLET_WIDTH, BULLET_HEIGHT) this.direction = direction; this.owner = owner; }
接着修改js/player/index.js的shoot,为其中创建的bullets提供归属
js/player/index.js
bullets
/** * 玩家射击操作 * 射击时机由外部决定 */ shoot() { for(let i = 0; i < this.level; i++) { const bullet = databus.pool.getItemByClass('bullet', Bullet, { direction: 'up', owner: this });
同样处理js/npc/enemy.js的shoot
/** * 敌机射击操作 * 射击时机由外部决定 */ shoot() { const bullet = databus.pool.getItemByClass('bullet', Bullet, { direction: 'down', owner: this });
最后处理js/databus.js中removeBullets的回收逻辑
js/databus.js
removeBullets
/** * 回收子弹,进入对象池 * 此后不进入帧循环 */ removeBullets(bullet) { const index = this.bullets.findIndex(b => b === bullet); bullet.visible = false this.bullets.splice(index, 1); this.pool.recover('bullet', bullet) } }
这时候敌我的子弹就恢复正常了。不过这时候玩家中弹并不会死亡,现在来让玩家Go Die吧。在js/main.js的collisionDetection我们判断增加每一颗子弹如果是敌方的,就判断其是否打中玩家,是则游戏结束。玩家的子弹判断保持不变。
Go Die
collisionDetection
// 全局碰撞检测 collisionDetection() { let that = this; databus.bullets.forEach(bullet => { for (let i = 0, il = databus.enemys.length; i < il; i++) { let enemy = databus.enemys[i]; if (bullet.owner instanceof Enemy) { databus.gameOver = this.player.isCollideWith(bullet); } else if (!enemy.isPlaying && enemy.isCollideWith(bullet)) { enemy.playAnimation(); that.music.playExplosion(); bullet.visible = false; databus.score += 1; break; } } });
到此整个简单改造计划就结束了,以后还可以添加武器系统,boss战等等。下面是改造后的游戏动图录屏
The text was updated successfully, but these errors were encountered:
No branches or pull requests
微信小游戏推出已有几天了,这个功能对小程序和小游戏的推动影响不用多说,大家赶紧摩拳擦掌往上撸就可以了。关于如何开发官方文档已经说明了,这篇则是对官方的
打飞机
demo一些小改造。开发预备式
微信开发者工具
(v1.02.1712280)HTML5
游戏轻松接入,官方提供了Adapter
。这个的作用就是提供HTML5
写法和wx
写法的全局转换层。打飞机小游戏
使用
无AppID
模式创建一个微信小游戏后可以看到官方demo,其中入口文件和配置文件:game.js
和game.json
。game.js
引入并初始化包含整个打飞机
的游戏场景、参与者(玩家飞机和敌方飞机)、游戏逻辑的主函数的main.js
。在main.js
中我们可以发现由于Adapter
的存在,这里的代码和我们平常的代码写法没什么差异了。游戏的主逻辑如下图:在loop中,玩家每隔20帧射一次,每隔60帧生成新的敌机。每帧检查玩家和敌机是否死亡,玩家死亡游戏结束,敌机死亡分数+1。只有玩家可以射击,且射击方式固定,通过躲避敌机生存。接下来我们针对这些进行改造,提升游戏的可玩性和挑战性。
玩家升级计划
首先用编辑器打开
player/index.js
,将等级逻辑加入到玩家的类中。接下来在
main.js
的update
函数加入升级逻辑。好的,到此玩家已经可以正常升级了。那么该给予玩家奖励品了。在
player/index.js
的shoot
函数中我们修改射击的逻辑。玩家1级时只有中间的射击口,2级有左边和中间的射击口,3级有左中右三个射击口。武器的最终形态如图, 这时候的玩家已经可以为所欲为了<_<,实际上都不需要躲避了。。。:
敌人的反击号角
为了对抗愚昧的玩家,不让他们为所欲为,最后没兴趣玩下去~~,敌机装备武器,反击开始。
首先敌机的子弹是向下,所以复制一份
images/bullet.png
,并颠倒保存为images/bullet-down.png
, 然后我们重用js/player/bullet.js
,在构造函数处增加敌机的子弹配置项,并修改敌人子弹更新逻辑。接着在
js/npc/enemy.js
结尾部分为敌人装备武器, 子弹速度为敌人自身速度+5
接下来,在
js/main.js
中加入敌机的射击逻辑,敌机移动5次、60次时设计。这时候我们发现,由于不明宇宙的干扰射线的影响,玩家和敌机的子弹不受控制的乱飞。接下来我们就来恢复世界的秩序吧 ;
经侦测发现是对象池
pool
的获取逻辑问题导致子弹不受控问题,我们需要区分获取玩家、每个敌机的子弹首先,对象获取我们加入对象属性的判断,当有传入对象属性时,我们获取所有属性值一致的已回收对象,若没有找到或者对象池为空时,则用属性创建新对象
相应的我们需要给每个子弹设置归属,在
js/player/bullet.js
中Bullet
类修改constructor
接着修改
js/player/index.js
的shoot
,为其中创建的bullets
提供归属同样处理
js/npc/enemy.js
的shoot
最后处理
js/databus.js
中removeBullets
的回收逻辑这时候敌我的子弹就恢复正常了。不过这时候玩家中弹并不会死亡,现在来让玩家
Go Die
吧。在js/main.js
的collisionDetection
我们判断增加每一颗子弹如果是敌方的,就判断其是否打中玩家,是则游戏结束。玩家的子弹判断保持不变。到此整个简单改造计划就结束了,以后还可以添加武器系统,boss战等等。下面是改造后的游戏动图录屏
The text was updated successfully, but these errors were encountered: