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(); })