摘要
我们已经从最基础的画线填充、cavans2d context状态变换,做出了绘制封装类(Figure)以及动画类(Animation),就目前而言,这几个简单的类已经可以做简单的游戏了。这期就做个一简单的小鸟飞跃障碍的游戏,用来验证我们之前的代码。该游戏前几年好像还挺多人玩:就是一个小鸟在丛林里飞,速度会随着时间退役越来越快,一旦碰到树桩游戏就结束。
本期内容依旧是在微信小游戏上进行实现的。由于内容以及代码都承接以前文章,如果你没有阅读过,可以从这里开始。
本文不允许任何形式的转载!
阅读提示
本系列文章不适合以下人群阅读,如果你无意点开此文,请对号入座,以免浪费你宝贵的时间。
- 想要学习利用游戏引擎开发游戏的朋友。本文不会涉及任何第三方游戏引擎。
- 不具备面向对象编程经验的朋友。本文的语言主要是Javascript(ECMA 2016),如果你不具备JS编程经验倒也无妨,但没有面向对象程序语言经验就不好办了。
关注我的微信公众号,回复“源代码3”可获得本文示例代码下载地址,谢谢各位了!
Spirit类实现
我不知道为什么要叫Spirit,都这么命名的那我也这样来吧。Spirit可以展示动画,并能移动旋转缩,我们之前的Figure类和Animation类就已经实现了这些功能,并且在前一期的开始我给出了一个FigureImage类,一个可以绘制图片的类,所以我决定从这个类继承然后进行扩展:
import FigureImage from "./FigureImage.js";
export default class Spirit extends FigureImage {
constructor(p) {
super(p);
}
}
我们先测试一下这个类,绘制个图片,这个图片是一个小鸟飞行动作图片,一共有4个动作,每个动作图片的宽度和高度都是一致的:
我们将这幅图利用Spirit类绘制在canvas上:
import Graph from "./example/Graph.js";
import Spirit from "./example/Spirit.js";
let graph = new Graph(wx.createCanvas());
let birdsSpirit = new Spirit();
birdsSpirit.left = 10;
birdsSpirit.top =100;
birdsSpirit.width = 400;
birdsSpirit.height = 50;
graph.addChild(birdsSpirit);
// 微信小游戏创建image的方法
// 如果想要适配到web上你可以自己建个Image类
let image = wx.createImage();
image.onload = function(evt){
// 载入成功后把image对象给spirit并绘制
birdsSpirit.img = evt.target;
drawImage();
}
image.src = './example/images/birds_spirit.png';
function drawImage(){
graph.refresh();
}
显示在界面是的结果如下:
如果我们想要实现小鸟飞行动作的动画效果(拍打翅膀),我们可以利用ctx.drawImage方法中指定source图片的bounds(源图片的位置以及大小,可以看下上一期文章最开始,FigureImage类已经封装好了),结合我们之前的Animation类一帧一帧的绘制上述图片中小鸟动作。
首先要确定源图片的大小,如果我们按照从左到右的顺讯给每个动作安排上索引,由于每个动作图片大小都是相同的,很简单的就能计算出每个索引对应图片的bounds,同时我们引入一个新的专门管理图片资源的类,ImageManager:
let instance;
let _imageMap = Symbol('存储的图片列表');
export default class ImageManager {
constructor() {
if (instance) {
return instance;
}
instance = this;
this[_imageMap] = [];
}
static getInstance() {
if (!instance) new ImageManager();
return instance;
}
get images() {
let imgs = [];
for (let key in this[_imageMap]) {
imgs.push(this[_imageMap][key]);
}
return imgs;
}
registerImage(name, src, properties, callback) {
var image = wx.createImage();
var that = this;
image.data = properties;
image.onload = function (evt) {
that[_imageMap][name] = {
image: evt.target, property: evt.target.data};
if (callback) {
callback(evt);
}
}
image.src = src;
}
getImage(name, index) {
var image = this[_imageMap][name].image;
if (index == undefined)
return image;
var property = this[_imageMap][name].property;
if (!property)
return image;
var column = property.column;
var row = property.row;
var total = column * row;
if (index < 0 || index >= total)
return image;
var width = image.width;
var height = image.height;
var perWidth = width / column;
var perHeight = height / row;
var vIndex = Math.floor(index / column);
var hIndex = Math.floor(index % column);
var srcLeft = hIndex * perWidth;
var srcTop = vIndex * perHeight;
var srcBounds = {
left: srcLeft, top: srcTop, width: perWidth, height: perHeight};
return {
image: image, bounds: srcBounds};
}
}
这个类的调用如下所示:
let graph = new Graph(wx.createCanvas());
let bird = new Spirit();
bird.left = 10;
bird.top = 100;
bird.width = 100;
bird.height = 70;
graph.addChild(bird);
ImageManager.getInstance().registerImage('birds',
'./example/images/birds_spirit.png',
{
column: 4,
row: 1
}, function (evt) {
// 图片注册成功绘制bird spirit中的第1个
let imageInfo = ImageManager.getInstance().getImage('birds', 1);
bird.img = imageInfo.image;
bird.srcLeft = imageInfo.bounds.left;
bird.srcTop = imageInfo.bounds.top;
bird.srcWidth = imageInfo.bounds.width;
bird.srcHeight = imageInfo.bounds.height;
graph.refresh();
});
可以看到这个类是一个单例类,我们首先要将图片注册进去让它维护,并且给出该图片一共分成几行几列,然后通过它的getImage方法指定图片名(注册图片时给的唯一名称)以及想要的索引,就可以得到该索引所对应的原图片中的位置以及大小(我叫它bounds)。
既然我们可以利用ImageManager获取原图片中不同索引的bounds,那我们就可以在我们的Spirit中加入一个imageIndex属性(用于指定原图片中第几个图)和imageName属性(原图片注册时的标识名称)。通过设置这两个属性就可以绘制原图片中不同位置:
import FigureImage from "./FigureImage";
import ImageManager from "./utils/ImageManager";
export default class Spirit extends FigureImage {
constructor(p) {
p = p || {
};
super(p);
this.imageIndex = 0;
this.imageName = p['imageName'];
}
drawSelf(ctx){
if(this.imageName == undefined) return;
// imageIndex必须取整!!!
let imageInfo = ImageManager.getInstance().getImage(this.imageName, Math.