Canvas 射击小游戏详解系列(四)

写着写着内容有点多了,不过还是觉得这些东西记下来还是很有用的。下面开始介绍项目中对象的设计。

项目中主要对象的设计

整个项目中最重要的是游戏中的三个对象元素:飞机,怪兽,子弹。

元素分析

飞机支持在画布的底部进行左右的连续移动,移动时依赖玩家的操作,并仅支持左右键的操作。飞机还可以进行射击,玩家点击空格键,上键或Enter键会可以让飞机射击出子弹,并且支持连续射击。

上面总结的飞机行为可以得到,飞机有渲染,移动,临界判断跟射击的功能。需要的参数包括渲染时的画布对象,坐标,飞机图片,飞机尺寸,移动的速度,临界判断的画布上渲染的最大最小x坐标及射击的子弹长度跟移动速度参数。

怪兽只是在画布上部的一个区域进行从上到下蛇形自动移动。被子弹击中会有爆炸效果并消失。

上面总结的怪兽行为可以得到,怪兽有渲染,移动,临界判断的功能。需要的参数包括渲染时的画布对象,坐标,怪兽图片,爆炸图片,怪兽尺寸,移动的速度,临界判断的画布上渲染的最大最小x坐标。怪兽的下移移动不在对象中实现。

子弹通过飞机射击射出,保持一定速度向上移动,如果碰到怪兽会消失,如果一直到达画布顶部也会自动消失。

上面总结的子弹行为可以得到,子弹有渲染,移动,临界判断,清除的功能。需要的参数包括渲染时的画布对象,坐标,子弹长度,移动的速度。临界判断不在对象中设置方法。

对象设计

通过对三个元素的分析,飞机跟怪兽有可抽象成一致的属性(options属性及context画布)跟方法drawing()move()translate()

options传入的参数为JSON对象,包括了大量的参数,如果对象所渲染到画布上的图片,图片渲染的坐标点,图片尺寸,在画布上的可渲染的最大最小x坐标,移动速度等。Plane父类跟Enemy子类的options传入的参数会有差异,但是做了抽象统一。

两个元素的移动可以统一实现,所以我让怪兽的构造函数继承飞机的构造函数。

Enemy类虽然继承了Plane类,但是还是有一些差异点。Plane类中还有一个创建子弹的shoot()方法,这个方法也有被Enemy类继承,但是当前项目中Enemy类的实例并没有使用这个类,是存在代码冗余的问题。但是也支持了项目进一步优化,Enemy类的实例可以进一步提高难度,增加项目的趣味。

Enemy类的方法drawing()做了重写,因为怪兽有一个爆炸的渲染判断,涉及判断是否存活(属性isLive)。

子弹的行为跟前两者不同,虽然也是一样的属性,但是方法基本需要重写,所以不做继承。

键盘对象设计

因为飞机的移动跟射击都是支持连续,如果只是在Game对象中实现键盘事件监听,实际的移动跟射击都会有卡顿感,所以把键盘事件转换成点击状态,效果上会更加顺畅。

键盘对象包括了左右上键,空格键,Enter键的状态,并把document.onkeydowndocument.onkeyup都指向了自身的方法,在放在中判断键盘按键是否按下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* @constructor KeyBoard
* @description 键盘监听对象
* @see The <a href="#">KeyBoard</a >.
* @example
* this.keyBoard = new KeyBoard();
*/
function KeyBoard () {
document.onkeydown = this.keydown.bind(this);
document.onkeyup = this.keyup.bind(this);
}

KeyBoard.prototype = {
pressedLeft: false, // 是否点击左键
pressedRight: false, // 是否点击右键
pressedUp: false, // 是否按了上报
pressedSpace: false, // 是否按了上报
pressedEnter: false, // 是否按了上报
keydown: function (event) {
// 判断哪个按键按下,修改按键状态
},
keyup: function (event) {
// 判断哪个按键松开,修改按键状态
}
};

其他

除了这几个对象的设计,因为还涉及对象的继承,所以还另外齐起了一个文件common.js放处理继承父类原型对象的函数inheritPrototype()。具体的介绍可以会看上一篇关于继承的介绍。

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* @description 继承父类原型对象的函数
* @param {Object} subType 子类对象
* @param {Object} subType 父类对象
*/
var inheritPrototype = function (subType, superType) {
// 把父类对象的原型对象赋值给proto
var protoType = Object.create(superType.prototype);
// proto的constructor指向子类对象,进行重置
protoType.constructor = subType;
// 把子类的原型指向原型
subType.prototype = protoType;
}

关于对象中设计到的Canvas的部分,下一篇将介绍。