1、什么是微信小游戏
微信小游戏是小程序的一个类目,小游戏是微信开放给小程序的更多的能力,让小程序开发者有了开发游戏的能力。微信小游戏是在小程序的基础上添加了游戏库API。微信小游戏只能运行在小程序环境中,微信小游戏没有小程序中WXSS、WXML、多页面等内容,所以微信小游戏开发时小程序的组件就无法使用;但增加了一些渲染、文件系统以及后台多线程的功能。
小游戏提供了CommonJS风格的模块API,可以将一些公共的代码抽离成为一个单独的JS文件,作为一个模块。可以通过module.exports或者exports导出模块,通过require引入模块。
2、接宝石箱子游戏
接宝石箱子小游戏运行后底部出现主角工人,屏幕上方不断掉落宝石箱子,主角工人每接住一个箱子积分增加1分,玩家通过触碰屏幕左侧或者右侧控制工人移动方向。本游戏运行界面如图1所示。
■ 图1 接宝石箱子小游戏运行效果
// common.js function sayHello(name) { console.log('Hello' + name + '!') } function sayGoodbye(name) { console.log('Goodbye'+ name+'!') } module.exports.sayHello = sayHello //提供对外的接口 module.exports.sayGoodbye = sayGoodbye //提供对外的接口
在game.js中require common.js,就可以调用common模块导出的接口。所有输出接口都会成为输入对象的属性。
var common = require('./js/libs/common.js') common.sayHello('xmj') common.sayGoodbye('xmj')
在模块化时,也可以使用export default命令和import命令。
使用import命令是用户需要知道所加载的变量或者函数名,否则无法加载,为了方便用户,使其不用预先知道就能加载模块,这是可以使用export default为模块指定默认输出。
// common2.js export default function test() { //当然也可以是一个类 console.log("大家好"); };
导入时可以如下:
import t from './js/libs/common2.js' t('xmj')
这时使用import导入时可以使用任意名称指定模块js文件的导出方法test()。本质上export default就是输出一个叫做Default的方法,只是系统容许为它任意取名。微信小游戏支持ES6,所以开发时可以使用export default class定义各种类模块,例如Bullet子弹类,Sprite精灵类,Enemy敌人类模块,使用import导入类模块。
import Player from './player/index' import Enemy from './npc/enemy'
小游戏并不能调用所有小程序的API,但是能调用到大部分的API。例如像wx.request,wx.chooseImage,wx.showToast等API,小游戏都可以调用到。更详细的API能力见微信官方API文档。
03、程序设计的步骤
使用微信开发者工具新建项目,左侧类别选择小游戏,右侧的项目名称输入“接宝石箱子游戏”,单击“新建”按钮,就新建一个新的小游戏。
在图2界面中的中间部分,为文件结构区域。右键文件结构区域上方+号,下拉菜单中选择“目录”,建立文件夹test/images结构,放入相关游戏图片(箱子,工人)。
■ 图2 放入相关游戏图片
准备工作完成后,修改game.js文件。本游戏不使用Adapter(适配器),使用微信wx API自行开发。
/*import './js/libs/weapp-adapter' import './js/libs/symbol' import Main from './js/main' new Main() */ const DROP_SPEED = 8 //箱子下移速度 const MOVE_SPEED = 4 //人物左右移速度 const canvas = wx.createCanvas() const context = canvas.getContext('2d') context.fillStyle = "#ffff00" context.font = "40px Arial"
加载工人图片,并设置设置位置坐标(imgX,imgY)。
const image = wx.createImage() //人物图片 let imgX = canvas.width / 2 - 18 //人物位置 let imgY = canvas.height - 80 image.onload = function () { context.drawImage(image, imgX, imgY) } image.src = 'test/images/player.png'
drawRect(x, y)函数清空屏幕,重绘宝石箱子、人物和积分文字。
let score = 0 const { windowWidth, windowHeight } = wx.getSystemInfoSync() //屏幕宽度和高度 function drawRect(x, y) { context.clearRect(0, 0, windowWidth, windowHeight) //重绘清空屏幕 context.drawImage(boxImage, x, y) //画宝石箱子 context.drawImage(image, imgX, imgY) //画人物 context.fillText("积分" + score, 30, 50) //绘制积分文字 }
设置宝石箱子坐标初始位置(imgX,imgY)为屏幕顶端中央。调用box(rectX, rectY)在指定位置(rectX, rectY)绘制箱子。
let rectX = canvas.width / 2 – 30 //宝石箱子坐标 let rectY = 0 const boxImage = wx.createImage() //宝石箱子图片 box(rectX, rectY) //调用box((rectX, rectY)在指定位置(rectX, rectY)绘制箱子 function box(x, y) { // Math.ceil(); 只要有小数总是向上取整数; 例如Math.ceil(1.1) 输出为2 let number = Math.ceil(Math.random() * 7) //随机产生1-7数字 let box_src = 'test/images/box' + number + '.png' boxImage.onload = function () { context.drawImage(boxImage, x, y) } boxImage.src = box_src }
以下触摸(碰)事件处理。判断触碰位置在人物的左侧则移动方向direction值为-1,这样x坐标可以不断减少。触碰位置在人物的右侧则移动方向direction值为1,这样x坐标可以不断增加。
var direction = 0 var touchX = imgX var touchY = imgY wx.onTouchStart(function (res) {//触碰事件 touchX = res.changedTouches[0].clientX - 18 if (touchX < imgX) { //触碰位置在人物的左侧 direction = -1 //向左移动 console.log("left") } else { //触碰位置在人物的右侧 direction = 1 //向右移动 console.log("right") } })
loop()是用来实现游戏帧循环达到动画效果。首先判断工人达到屏幕两侧边界,如果判断工人的位置到达屏幕最左侧,则改向右;到达屏幕最右侧,则改向左。箱子Y坐标不断,显示出下落效果,工人位置坐标根据移动方向不断修改,同时重新绘制箱子,工人,积分。
如果箱子到达底部区域,则判断与工人之间距离,如果小于50则认为碰撞(就是工人接到宝石箱子),进行加分处理。如果箱子出了屏幕底部,重新设置箱子图片并设置位于顶部。最后调用requestAnimationFrame(loop);起到循环渲染效果。
function loop() { if (imgX <= 10) { //人物位置到达屏幕最左侧,改向右 direction = 1 } else if (imgX >= canvas.width - 50) {//人物位置到达屏幕最右侧,改向左 direction = -1 } rectY += DROP_SPEED //箱子下移DROP_SPEED imgX += direction * MOVE_SPEED //改变人物位置坐标 drawRect(rectX, rectY) //重新绘制箱子,人物,积分 if (rectY > canvas.height - 100 && rectY <= canvas.height) {//达到底部 let range = Math.abs(rectX - imgX) //与人物之间距离 if (range <= 50) { //检测碰撞 rectY = canvas.height score++ //加分 } } if (rectY > canvas.height) { rectY = 0 let spwanX = Math.floor(Math.random() * (canvas.width - 80)) box(spwanX, rectY) //重新设置箱子位于顶部 rectX = spwanX } requestAnimationFrame(loop); //循环渲染 } loop()