1、背景
多年java后台开发经验,前端略懂皮毛。多年游戏经验,一直在玩别人的游戏。有一天突然在想为啥不自己做个玩玩呢?重度的游戏开发不在考虑范围,技术和资源都不允许。原生手游是比较理想的类型,但除了需要补充游戏开发相关技术,还需要补充 ios 和安卓相关开发,这个作为一个长期目标。思前想后,比较务实的做法就是先弄个微信小游戏趟趟水。微信小游戏侧重创意,相对实现都比较简单,微信平台也提供了很多方便开发者实施的服务,非常适合用来练手。
2、搞起
刷微信小游戏开发的官方文档
https://developers.weixin.qq.com/minigame/dev/guide/
心得:微信官网文档迅速的刷一遍,注重看基本规则,api细节暂时不用太纠结。
刷的差不多了下载微信开发者工具,开发者工具会自带一个打飞机的示例项目,新手建议详读。这个示例虽然很简单,但却把游戏的基本框架展示了出来,可以帮助初学者建立基本的游戏开发的概念。笔者学习时打开两个窗口,一个窗口加载示例程序,一个窗口建一个空白项目,尝试着在空白项目上一点一点的复制实现demo的内容。
原始demo运行画面:
笔者尝试实现一些粗糙的想法,对程序改造后的效果:
曲线子弹
多行子弹效果:
核心改动代码:main.js
import BackGround from './background'
import Databus from './databus'
import Bullet from '../player/bullet'
let ctx = canvas.getContext('2d')
let databus = new Databus()
let bulletX = 100
let bylletAdd = 0 //可调整该值使子弹漂移
export default class Main{
constructor(){
// 维护当前requestAnimationFrame的id
this.aniId = 0
this.restart()
}
restart() {
databus.reset()
this.bg = new BackGround(ctx)
this.bindLoop = this.loop.bind(this)
this.hasEventBind = false
// 清除上一局的动画
window.cancelAnimationFrame(this.aniId);
this.aniId = window.requestAnimationFrame(
this.bindLoop,
canvas
)
}
/**
* canvas重绘函数
* 每一帧重新绘制所有的需要展示的元素
*/
render() {
ctx.clearRect(0, 0, canvas.width, canvas.height)
this.bg.render(ctx)
databus.bullets
.concat(databus.enemys)
.forEach((item) => {
item.drawToCanvas(ctx)
})
}
// 游戏逻辑更新主函数
update() {
if (databus.gameOver)
return;
// this.bg.update()
databus.bullets
.concat(databus.enemys)
.forEach((item) => {
item.update()
})
if (databus.frame % 20 === 0) {
this.shoot(0, () => -3)
this.shoot(-0.5, () => -3)
this.shoot(0.5, () => -3)
}
console.log(databus.bullets.length)
}
// 实现游戏帧循环
loop() {
databus.frame++
this.update()
this.render()
this.aniId = window.requestAnimationFrame(
this.bindLoop,
canvas
)
}
shoot(sx = 0, sy = () => -10 ){
bulletX += bylletAdd
if (bulletX < 100 || bulletX > 300){
bylletAdd *= -1
}
let bullet = databus.pool.getItemByClass('bullet', Bullet)
bullet.init(
bulletX,
window.innerHeight * 0.8,
sx,
sy
)
databus.bullets.push(bullet)
}
}
bullet.js
import Sprite from '../common/sprite'
import DataBus from '../main/databus'
const BULLET_IMG_SRC = 'images/bullet.png'
const BULLET_WIDTH = 16
const BULLET_HEIGHT = 30
const SCREEN_WIDTH = window.innerWidth
const SCREEN_HEIGHT = window.innerHeight
const __ = {
sx: Symbol('sx'),
sy: Symbol('sy')
}
let databus = new DataBus()
export default class Bullet extends Sprite {
constructor() {
super(BULLET_IMG_SRC, BULLET_WIDTH, BULLET_HEIGHT)
}
init(x, y, speedX, f_speedY) {
this.x = x
this.y = y
this[__.sx] = speedX
this[__.sy] = f_speedY
this.visible = true
}
// 每一帧更新子弹位置
update() {
this.x += this[__.sx]
this.y += this[__.sy](this.x)
// 超出屏幕外回收自身
if (this.y < -this.height || this.x < -this.width || this.x > SCREEN_WIDTH || this.y > SCREEN_HEIGHT)
databus.removeBullets(this)
}
}
databus.js
import Pool from '../common/pool'
let instance
/**
* 全局状态管理
*/
export default class Databus{
constructor(){
if(instance)
return instance
instance = this
this.pool = new Pool()
this.reset()
}
reset(){
this.frame = 0
this.score = 0
this.bullets = []
this.enemys = []
this.animation = []
this.gameover = false
}
/**
* 回收敌人
*/
removeEnemy(enemy){
let temp = this.enemys.shift()
temp.visible = false
this.pool.recover('enemy', enemy)
}
/**
* 回收子弹
* 原始的代码因为子弹是单条直线,最先发出的子弹最先消失,所以简单的使用了 shift 操作
*/
removeBullets(bullet){
bullet.visible = false
let index = this.bullets.indexOf(bullet)
this.bullets.splice(index, 1)
this.pool.recover('bullet', bullet)
}
}
小结:
官方提供的微信开发者工具非常难用,基本的快捷键都没有,笔者虽然使用了当时最新的版本(1.02.1910120),依然感觉像是一个非常粗糙的原型版本,不像是一个完成版本,写代码特别累。
刷完这个后,感觉直接用demo的这个原生框架改一改,再加上一些wx的api,应该能直接构建一些简单的小游戏了。特别是一些创意玩法的小游戏,不需要有太多的界面和交互。但做程序猿这么多年,知道各行各业都有一些主流框架的存在。所以在正式开始实现自己想法之前,决定先刷一下游戏开发的框架:游戏引擎
问题:大多数莫名其妙的问题,可以通过重启客户端解决(惊不惊喜)。
部分wx的api无法在测试项目上使用,比如广告组件,需要申请真实的id才能跑通。
部分wx的api在开发者工具上无法使用,但是通过预览二维码在手机端可以正常使用。比如游戏圈、分享