JSGF游戏开发课程(三):开始一个2D游戏示例
在正式开始前,我们需要先解释一下JSGF坐标系与方向表示。
JSGF中物体的坐标一般以直角坐标来表示。其坐标系以左上方的顶点为原点,右方向为X轴正方向,下方向为Y轴正方向,坐标单位均为像素(px)。
常用的JSGF直角坐标系有两种:一种为屏幕坐标系,以浏览器屏幕内的左上顶点为原点建立;
另一种为画布坐标系,是以当前画布的左上顶点为原点建立。在JSGF中,物体的坐标是基于所在“容器”的坐标系表示的。如下图:画布是绘制在document对象上的,所以画布的坐标是基于屏幕坐标系的。
精灵是绘制在画布中的,所以精灵的坐标是基于画布坐标系的。
再举一个例子。假设有a,b两个精灵分别绘制在A,B两个画布上。如果想比较a,b的坐标,最好的方式将a,b的坐标都转换到
同一个坐标系下(如:屏幕坐标系)后再比较。
一个点除了可以用( x , y )方式的直角坐标表示外,还可以用极坐标来表示:(与原点的距离,与原点及X轴正向夹角的弧度)。
有些情况下用直角坐标来运算非常复杂,而转换成极坐标来运算则极为简便。
JSGF游戏中方向以弧度来表示,该值可以看作该方向与X轴正方向的夹角的弧度值。
需要注意的是,弧度是一个有方向的量,JSGF规定其正方向为顺时针方向。
接下来,我们开始用JSGF写一个魔兽争霸2的游戏示例。
1. 创建游戏主类
定义一个游戏主类,继承自js.game.Game类。
var SYS = js.lang.System,
D = js.core.Dom,
$ = js.core.Dom.$,
MT = js.math.MathTool,
F = js.phys.Formulas;
SYS.namespace('js.game.demo.warcraft2');
js.game.demo.warcraft2.Game = function(config){
js.game.demo.warcraft2.Game.superclass.constructor.apply(this, arguments);
};
SYS.extend(js.game.demo.warcraft2.Game, js.game.Game, {
......
});
2. 创建精灵类
定义一个魔兽战士Warrior类,继承自js.game.Sprite类。
js.game.demo.warcraft2.Warrior = function(config){
js.game.demo.warcraft2.Warrior.superclass.constructor.apply(this, arguments);
};
SYS.extend(js.game.demo.warcraft2.Warrior, js.game.Sprite, {
......
});
3. 精灵的帧序列
我们先定义魔兽战士的状态有:站立、行走、攻击、死亡。
再定义八个方向的编号(0-7):从弧度0开始,每隔四分之一PI的弧度定义一个方向。
精灵某个时刻的帧画面取决于它的状态与方向。于是,我们按照状态+方位的组合定义一个帧序列。帧序列的名称即:状态名 + 方位编号。
比如,站立状态的八个帧序列名为stand0 ~ stand7:
每个帧序列的数据是一个数组,包含了相关联的数个帧的数据。而每个帧的数据也是一个数组:第1项为该帧在图像资源中的X轴偏移量;第2项为该帧在图像资源中的Y轴偏移量。
站立状态的帧序列比较特殊,都只包含一个帧。站立状态的八个帧序列数据如下:
frameSeqs:{
stand6:[[1,1]]
,stand7:[[66,1]]
,stand0:[[131,1]]
,stand1:[[196,1]]
,stand2:[[261,1]]
,stand3:[[326,1]]
,stand4:[[391,1]]
,stand5:[[456,1]]
}
更多状态的帧序列数据,你可以右键点击页面查看示例的源代码。
4. 创建精灵实例
我们给Game类新增一个方法,用于创建一个魔兽战士实例。
_newWarrior: function(config){
return new js.game.demo.warcraft2.Warrior({
side:config['side'],state:config['state']||'stand'
,imageSrc:'../../images/warcraft2/'+config['side']+'/'+config['name']+'.png'
,x:config['x'],y:config['y'],dir:config['dir'],width:72,height:72
,frameSeqs:{
.......
});
}
战士类的基本属性:
side: 所属阵营。人族(human)或兽族(orc)。
state: 初始状态。取值范围是:"stand" | "walk" | "fight" | "die",缺省值为"stand"。
x,y: 初始坐标。
z: 对应的DOM元素的z-index值。
width,height: 图像的宽,高度。
dir: 方向(弧度值)。
5. 绘制精灵的全部方位
为Game类添加一个函数,绘制兽人的某状态的八个方位图像:
_paint8Orc: function(center, r, state){
for(var i=0;i<8;i++){
var xy = F.round(i, [center[0]+r,center[1]], center, r*MT.RADIAN_2),
player = this._newWarrior({
side:'orc',name:'axethrower'
,x:xy[0],y:xy[1],dir:i*MT.RADIAN_2,state:state
});
player.init(this);
this._players.push(player);
}
}
6. 运行游戏实例
编写一个GameApp类,在main方法中创建一个游戏实例:
game = new js.game.demo.warcraft2.Game({
id:'warcraft2',x:200,y:900,background:'black',width:500,height:700,fpsMax:12
});
game.watchFPS(true);
game.init();
我们设定这个游戏实例的最大帧速为12,以免精灵动作看起来过快。
同时在Game类的init方法中添加“ended”事件监听代码:在游戏结束时擦除画布。
this.subscribe('ended', function(){
this.getCanvas().destory();
})