c语言打飞机小程序,微信小游戏体验之打飞机改造计划

dab7b575cc089444a6446454cfb1d286.png

微信小游戏推出已有几天了,这个功能对小程序和小游戏的推动影响不用多说,大家赶紧摩拳擦掌往上撸就可以了。关于如何开发官方文档已经说明了,这篇则是对官方的 打飞机 demo一些小改造。

开发预备式

下载最新版本的 微信开发者工具 (v1.02.1712280)

根据官方文档说明, 目前不提供公开注册 。因此目前只能使用 无AppID 模式进行体验

为了让 HTML5 游戏轻松接入,官方提供了 Adapter 。这个的作用就是提供 HTML5 写法和 wx 写法的全局转换层。

打飞机小游戏

使用 无AppID 模式创建一个微信小游戏后可以看到官方demo,其中入口文件和配置文件: game.js 和 game.json 。 game.js 引入并初始化包含整个 打飞机 的游戏场景、参与者(玩家飞机和敌方飞机)、游戏逻辑的主函数的 main.js 。在 main.js 中我们可以发现由于 Adapter 的存在,这里的代码和我们平常的代码写法没什么差异了。游戏的主逻辑如下图:

在loop中,玩家每隔20帧射一次,每隔60帧生成新的敌机。每帧检查玩家和敌机是否死亡,玩家死亡游戏结束,敌机死亡分数+1。只有玩家可以射击,且射击方式固定,通过躲避敌机生存。接下来我们针对这些进行改造,提升游戏的可玩性和挑战性。

玩家升级计划

玩家初始等级为1,玩家可通过击杀敌机升级,每击落30敌机升级一次

玩家每升级一次,增加一个射击口

玩家最多升级两次

首先用编辑器打开 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() {

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() {

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)

}

}

// ...其他代码

武器的最终形态如图, 这时候的玩家已经可以为所欲为了<_>

e04a899e46fbc4716547e82f6325cb05.png

(此图片来源于网络,如有侵权,请联系删除! )

敌人的反击号角

为了对抗愚昧的玩家,不让他们为所欲为,最后没兴趣玩下去~~,敌机装备武器,反击开始。

首先敌机的子弹是向下,所以复制一份 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

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次时设计。

// 其他代码...

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 的获取逻辑问题导致子弹不受控问题,我们需要区分获取玩家、每个敌机的子弹

首先,对象获取我们加入对象属性的判断,当有传入对象属性时,我们获取所有属性值一致的已回收对象,若没有找到或者对象池为空时,则用属性创建新对象

/**

* 根据传入的对象标识符,查询对象池

* 对象池为空创建新的类,否则从对象池中取

*/

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

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 提供归属

/**

* 玩家射击操作

* 射击时机由外部决定

*/

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 的回收逻辑

/**

* 回收子弹,进入对象池

* 此后不进入帧循环

*/

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 我们判断增加每一颗子弹如果是敌方的,就判断其是否打中玩家,是则游戏结束。玩家的子弹判断保持不变。

// 全局碰撞检测

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战等等。下面是改造后的游戏动图录屏

1fd46981c0fbf46a8872497ebcff4920.png

(此图片来源于网络,如有侵权,请联系删除! )

免责声明:本站所有文章和图片均来自用户分享和网络收集,文章和图片版权归原作者及原出处所有,仅供学习与参考,请勿用于商业用途,如果损害了您的权利,请联系网站客服处理。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值