HTML5 2D游戏引擎研发系列 第五章 <Canvas技术篇-画布技术-纹理集复杂动画>
作者:HTML5游戏开发者社区-白泽
转载请注明出处:http://html5gamedev.org/
目录
- HTML5 2D游戏引擎研发系列 第一章 <一切的开始>
- HTML5 2D游戏引擎研发系列 第二章 <磨剑>
- HTML5 2D游戏引擎研发系列 第三章 <Canvas技术篇-画布技术-显示图片>
- HTML5 2D游戏引擎研发系列 第四章 <Canvas技术篇-画布技术-基于手动切片动画>
- HTML5 2D游戏引擎研发系列 第五章 <Canvas技术篇-画布技术-纹理集复杂动画>
- HTML5 2D游戏引擎研发系列 第六章 <Canvas技术篇-画布技术-混色特效和粒子>
- HTML5 2D游戏引擎研发系列 第七章 <Canvas技术篇-画布技术-鼠标侦听器>
- HTML5 2D游戏引擎研发系列 第八章 <Canvas技术篇-基于顶点绘制与变形>
- HTML5 2D游戏引擎研发系列 第八章 <Canvas技术篇-基于顶点绘制与变形>
- WEBGL 2D游戏引擎研发系列 第一章 <新的开始>
- WEBGL 2D游戏引擎研发系列 第二章 <显示图片>
- WEBGL 2D游戏引擎研发系列 第三章 <正交视口>
- WEBGL 2D游戏引擎研发系列 第四章 <感想以及矩阵>
- WEBGL 2D游戏引擎研发系列 第五章 <操作显示对象>
- WEBGL 2D游戏引擎研发系列 第六章 <第一次封装>
- WEBGL 2D游戏引擎研发系列 第七章 <混色>
- WEBGL 2D游戏引擎研发系列 第八章 <批处理(影分身之术)>
- WEBGL 2D游戏引擎研发系列 第九章 <基于UV的模拟切片>
- WEBGL 2D游戏引擎研发系列 第十章 <纹理集动画>
- WEBGL 2D游戏引擎研发系列 第十一章 <着色器-shader>
- WEBGL 2D游戏引擎研发系列 第十二章 <粒子发射器>
- WEBGL 2D游戏引擎研发系列 第十三章 <Shader分享>
- 游戏技法 2D游戏引擎研发系列 第一章 <优化技巧>
- 游戏技法 2D游戏引擎研发系列 第二章 <计时器>
- 游戏技法 2D游戏引擎研发系列 第三章 <基于UV的无缝图像滚动>
- 游戏技法 2D游戏引擎研发系列 第四章 <基于形状的碰撞检测>
上一章节我们介绍了如何基于切片的动画,实际上那很难满足我的大部分动画的需求,而且必须要求宽度和高度是已知的,这样会浪费很多空白区域而且极其的不方便,我们很多情况下是需要播放下面的这几种动画的.
看到了吗,这些丰富的动画实际上每一帧的尺寸都不一样,没有人愿意知道他们的宽度是多少,然后像上一章节一样去一个一个数,所以现在,我们需要工具去导出这种动画,如果你电脑上没有,可以下一个FLASH CS6,你只需要对动画右键>生成Sprite表,就会看到这样一个画面.
它会自动计算你有多少重复的帧和消除空白区域,现在你只需要点击导出,你就会在你选择的位置看到一个png图片和一个XML配置表。
当然,我考虑了你可能还没来得及下这个软件,所以你可以用我的图片和XML,图片查看原图保存吧,
XML地址 :http://jfy19771224.sinaapp.com/course/demo_3/donghua.xml
现在,如果都准备好了,你可以打开这个XML,它看来是这样的格式.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<?
xml
version
=
"1.0"
encoding
=
"UTF-16"
?>
<
TextureAtlas
imagePath
=
"donghua.png"
>
<!-- Created with Adobe Flash CS6 version 12.0.0.481 -->
<
SubTexture
name
=
"fashi0000"
x
=
"206"
y
=
"340"
width
=
"47"
height
=
"93"
frameX
=
"-19"
frameY
=
"-5"
frameWidth
=
"70"
frameHeight
=
"103"
/>
<
SubTexture
name
=
"fashi0001"
x
=
"154"
y
=
"340"
width
=
"51"
height
=
"94"
frameX
=
"-18"
frameY
=
"-1"
frameWidth
=
"70"
frameHeight
=
"103"
/>
<
SubTexture
name
=
"fashi0002"
x
=
"1"
y
=
"405"
width
=
"54"
height
=
"95"
frameX
=
"-16"
frameY
=
"0"
frameWidth
=
"70"
frameHeight
=
"103"
/>
<
SubTexture
name
=
"fashi0003"
x
=
"1"
y
=
"405"
width
=
"54"
height
=
"95"
frameX
=
"-16"
frameY
=
"0"
frameWidth
=
"70"
frameHeight
=
"103"
/>
<
SubTexture
name
=
"fashi0004"
x
=
"56"
y
=
"405"
width
=
"65"
height
=
"76"
frameX
=
"0"
frameY
=
"-27"
frameWidth
=
"70"
frameHeight
=
"103"
/>
<
SubTexture
name
=
"fashi0005"
x
=
"122"
y
=
"435"
width
=
"59"
height
=
"75"
frameX
=
"0"
frameY
=
"-28"
frameWidth
=
"70"
frameHeight
=
"103"
/>
<
SubTexture
name
=
"fashi0006"
x
=
"182"
y
=
"435"
width
=
"59"
height
=
"73"
frameX
=
"0"
frameY
=
"-30"
frameWidth
=
"70"
frameHeight
=
"103"
/>
<
SubTexture
name
=
"fashi0007"
x
=
"182"
y
=
"435"
width
=
"59"
height
=
"73"
frameX
=
"0"
frameY
=
"-30"
frameWidth
=
"70"
frameHeight
=
"103"
/>
<
SubTexture
name
=
"fashi0008"
x
=
"182"
y
=
"435"
width
=
"59"
height
=
"73"
frameX
=
"0"
frameY
=
"-30"
frameWidth
=
"70"
frameHeight
=
"103"
/>
<
SubTexture
name
=
"niao0000"
x
=
"1"
y
=
"140"
width
=
"75"
height
=
"149"
frameX
=
"-19"
frameY
=
"0"
frameWidth
=
"121"
frameHeight
=
"149"
/>
<
SubTexture
name
=
"niao0001"
x
=
"104"
y
=
"112"
width
=
"84"
height
=
"137"
frameX
=
"-31"
frameY
=
"-4"
frameWidth
=
"121"
frameHeight
=
"149"
/>
<
SubTexture
name
=
"niao0002"
x
=
"104"
y
=
"1"
width
=
"108"
height
=
"110"
frameX
=
"-13"
frameY
=
"-17"
frameWidth
=
"121"
frameHeight
=
"149"
/>
<
SubTexture
name
=
"niao0003"
x
=
"189"
y
=
"112"
width
=
"63"
height
=
"117"
frameX
=
"-7"
frameY
=
"-32"
frameWidth
=
"121"
frameHeight
=
"149"
/>
<
SubTexture
name
=
"niao0004"
x
=
"1"
y
=
"290"
width
=
"60"
height
=
"114"
frameX
=
"-10"
frameY
=
"-31"
frameWidth
=
"121"
frameHeight
=
"149"
/>
<
SubTexture
name
=
"niao0005"
x
=
"189"
y
=
"230"
width
=
"64"
height
=
"109"
frameX
=
"-4"
frameY
=
"-32"
frameWidth
=
"121"
frameHeight
=
"149"
/>
<
SubTexture
name
=
"niao0006"
x
=
"77"
y
=
"250"
width
=
"76"
height
=
"105"
frameX
=
"0"
frameY
=
"-39"
frameWidth
=
"121"
frameHeight
=
"149"
/>
<
SubTexture
name
=
"niao0007"
x
=
"1"
y
=
"1"
width
=
"102"
height
=
"138"
frameX
=
"-13"
frameY
=
"-8"
frameWidth
=
"121"
frameHeight
=
"149"
/>
</
TextureAtlas
>
|
仔细看看,这个XML对应了我们PNG的2个动画,分别为niao和fashi,然后看到后面的序号了吗?0001这也就是我们动画的帧编号,再往后面看看,哇,这不是我们上一章使用过的属性吗,裁切XY,裁切宽度和高度,而后面的4个参数则是我们本章的动画关键,帧偏移信息和动画最大宽度和高度,其实看到这里,后面的逻辑就很简单了,我们只需要把XML解析出来,然后让我们的动画系统根据这些参数切片就好了.现在,我们可能需要2个数据结构来存储他们,把下面的代码添加到DisplayerObject.js中,一个结构动画信息,它用于存储动画的名称和帧的序列,也就是一个数组.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
//动画信息
function
QuadData()
{
/**
* 动画名称
*/
this
.name=
""
;
/**
* 动画片段
*/
this
.quadFrameLst;
};
|
下一个就是具体每帧动画的切片信息.
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
//帧信息
function
QuadFrame()
{
/**
* 帧名称
*/
this
.name=
""
;
/**
* 帧X坐标
*/
this
.x = 0;
/**
* 帧Y坐标
*/
this
.y = 0;
/**
* 帧宽度
*/
this
.width = 0;
/**
* 帧高度
*/
this
.height = 0;
/**
* 帧X偏移坐标
*/
this
.frameX = 0;
/**
* 帧Y偏移坐标
*/
this
.frameY = 0;
/**
* 帧最大宽度
*/
this
.frameWidth = 0;
/**
* 帧最大高度
*/
this
.frameHeight = 0;
};
|
现在定义好了2个结构体然后是读取XML信息和解析.
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
|
this
.AnimationXML=
function
()
{
this
.quadFrameList;
this
.quadDataList;
this
.loadComplete =
null
;
loadTarget=
this
;
this
.createXMLHttpRequest=
function
(){
if
(window.ActiveXObject){
xmlHttp =
new
ActiveXObject(
"Microsoft.XMLHTTP"
);
}
else
if
(window.XMLHttpRequest){
xmlHttp =
new
XMLHttpRequest(
"Msxml2.XMLHTTP.3.0"
);
};
};
//发请请求
this
.doSearch=
function
(url){
this
.createXMLHttpRequest();
xmlHttp.onreadystatechange =
this
.handleStateChange;
xmlHttp.open(
"GET"
,url,
"true"
);
xmlHttp.send(
null
);
};
//处理响应
this
.handleStateChange=
function
(){
if
(xmlHttp.readyState == 4){
if
(xmlHttp.status == 200){
loadTarget.parseResults();
};
};
};
//取得URL
this
.createURL=
function
(text){
this
.doSearch(text);
};
//加载完毕回调侦听器
this
.addEventListener=
function
(fun)
{
this
.loadComplete = fun;
};
/**
* 删除侦听器
* @param event
*/
this
.removeEventListener=
function
(event)
{
switch
(event)
{
case
EVENT_COMPLETE:
this
.loadComplete =
null
;
break
;
};
};
this
.parseResults=
function
(){
var
results = xmlHttp.responseXML;
var
textureAtlas = results.getElementsByTagName(
"SubTexture"
);
this
.quadDataList=[];
this
.quadFrameList=[];
for
(
var
i = 0; i< textureAtlas.length; i++){
subTexture = textureAtlas[i];
if
(name!=subTexture.getAttribute(
"name"
).substr(0,subTexture.getAttribute(
"name"
).length-4))
{
this
.quadFrameList=[];
var
quadData=
new
QuadData();
quadData.name=subTexture.getAttribute(
"name"
).substr(0,subTexture.getAttribute(
"name"
).length-4);
quadData.quadFrameLst=
this
.quadFrameList;
this
.quadDataList.push(quadData);
};
var
cacheframeWidth;
var
cacheframeHeight;
var
cacheWidth;
var
cacheHeight
var
quadFrame=
new
QuadFrame();
quadFrame.name=subTexture.getAttribute(
"name"
);
var
replace=subTexture.getAttribute(
"x"
);
if
(replace!=
null
)
{
quadFrame.x=replace;
};
replace=subTexture.getAttribute(
"y"
);
if
(replace!=
null
)
{
quadFrame.y=replace;
};
replace=subTexture.getAttribute(
"width"
);
if
(replace!=
null
)
{
quadFrame.width=replace;
cacheWidth=replace;
};
replace=subTexture.getAttribute(
"height"
);
if
(replace!=
null
)
{
quadFrame.height=replace;
cacheHeight=replace;
};
replace=subTexture.getAttribute(
"frameX"
);
if
(replace!=
null
)
{
quadFrame.frameX=subTexture.getAttribute(
"frameX"
);
}
else
{
quadFrame.frameX=0;
}
replace=subTexture.getAttribute(
"frameY"
);
if
(replace!=
null
)
{
quadFrame.frameY=replace;
}
else
{
quadFrame.frameY=0;
}
replace=subTexture.getAttribute(
"frameWidth"
);
if
(replace!=
null
)
{
quadFrame.frameWidth=replace;
cacheframeWidth=replace;
}
else
{
if
(cacheframeWidth!=
null
)
{
quadFrame.frameWidth=cacheframeWidth;
}
else
{
quadFrame.frameWidth=cacheWidth;
}
}
replace=subTexture.getAttribute(
"frameHeight"
);
if
(replace!=
null
)
{
quadFrame.frameHeight=replace;
cacheframeHeight=replace;
}
else
{
if
(cacheframeHeight!=
null
)
{
quadFrame.frameHeight=cacheframeHeight;
}
else
{
quadFrame.frameHeight=cacheHeight;
}
}
this
.quadFrameList.push(quadFrame);
name=subTexture.getAttribute(
"name"
).substr(0,subTexture.getAttribute(
"name"
).length-4);
}
if
(
this
.loadComplete!=
null
){
this
.loadComplete(
this
);
};
};
};
|
然后回到Main.js定义一个需要读取的XML队列.
1
|
var
xmlAddress=[
"donghua.xml"
];
|
和已经处理完毕的数据队列.
1
|
var
xmlStart=[];
|
最后是开始读取资源.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
var
xmlid=0;
this
.loadXml=
function
()
{
var
load=
new
AnimationXML();
this
.initer=
function
(e)
{
xmlStart.push(load);
xmlid++;
if
(xmlid<xmlAddress.length)
{
loadXml();
}
if
(xmlid==xmlAddress.length)
{
init();
alert(
"XML加载完毕"
+xmlStart.length);
}
}
load.addEventListener(EVENT_COMPLETE,
this
.initer);
load.createURL(xmlAddress[xmlid]);
}
|
还记得我们上一章节的读取图片的队列加载器吗?现在我们需要整合一下,可以选择先加载图片还是XML,整合后的代码如下:
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
|
//需要加载的图像资源
var
imageAddress=[
"donghua.png"
,
"map.png"
];
//需要加载的XML对象池
var
xmlAddress=[
"donghua.xml"
];
//已经加载好的图像资源池
var
imageStart=[];
//XML已经加载好的对象池
var
xmlStart=[];
//当前加载资源的ID
var
imageId=0;
//XML加载ID
var
xmlid=0;
//加载回调函数
this
.loadImag=
function
()
{
var
imageObj=
new
Image();
imageObj.src=imageAddress[imageId];
imageObj.onload=
function
(){
if
(imageObj.complete==
true
){
imageStart.push(imageObj);
imageId++;
if
(imageId<imageAddress.length)
{
loadImag();
}
if
(imageId==imageAddress.length)
{
loadXml();
}
}
}
}
this
.loadXml=
function
()
{
var
load=
new
AnimationXML();
this
.initer=
function
(e)
{
xmlStart.push(load);
xmlid++;
if
(xmlid<xmlAddress.length)
{
loadXml();
}
if
(xmlid==xmlAddress.length)
{
init();
}
}
load.addEventListener(
this
.initer);
load.createURL(xmlAddress[xmlid]);
}
window.onload=
function
(){
this
.loadImag();
}
//定义场景管理器
var
stage2d;
//初始化函数
function
init () {
//创建场景管理器
stage2d=
new
Stage2D();
//启用场景逻辑
stage2d.init();
}
|
好像代码有点多了,但其实逻辑很简单,我们把加载好的数据存放在
1
|
xmlStart
|
可以通过id访问我们加载的第几个数据比如
1
|
xmlStart[id]
|
然后通过来访问数据里的帧信息
1
|
xmlStart[id].quadDataList
|
现在万事俱备,我们需要动画系统支持XML所以要在DisplayerObject.js中稍作改动,现在遇到了一个设计问题,我们之前有2种方式显示图片的某个区域,通过isPlay来控制是否手动个切片,现在又多了一个XML控制,现在我们需要把isPlay变成一个int类型,然后修改3个地方,来支持我们以后的扩展,其实我是有点恋旧情怀.来看看我们修改后的代码吧.
在MovieClip2D类的构造函数里加入第二个参数data,我们的动画数据就是通过第二参数传递进来的
1
|
function
MovieClip2D(img,data)
|
然后是动画更新逻辑部分
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
26
27
28
29
30
31
32
33
|
//动画更新逻辑
this
.upFrameData=
function
()
{
switch
(
this
.isPlay)
{
case
1:
this
.mcY=
this
.frameHeadY*
this
.frameHeight;
this
.mcX=
this
.frameHeadX*
this
.frameWidth+
this
.currentFrame*
this
.frameWidth;
this
.currentFrame++;
if
(
this
.currentFrame>=
this
.totalFrames)
{
this
.currentFrame=0;
}
break
;
case
2:
this
.width=data[
this
.nameId].quadFrameLst[
this
.currentFrame].width;
this
.height=data[
this
.nameId].quadFrameLst[
this
.currentFrame].height
this
.mcX=data[
this
.nameId].quadFrameLst[
this
.currentFrame].x;
this
.mcY=data[
this
.nameId].quadFrameLst[
this
.currentFrame].y;
this
.frameX=data[
this
.nameId].quadFrameLst[
this
.currentFrame].frameX;
this
.frameY=data[
this
.nameId].quadFrameLst[
this
.currentFrame].frameY;
this
.frameWidth=data[
this
.nameId].quadFrameLst[
this
.currentFrame].frameWidth;
this
.frameHeight=data[
this
.nameId].quadFrameLst[
this
.currentFrame].frameHeight;
this
.totalFrames=data[
this
.nameId].quadFrameLst.length;
this
.currentFrame++;
if
(
this
.currentFrame>=
this
.totalFrames)
{
this
.currentFrame=0;
}
break
;
}
}
|
之后在paint重绘函数中做如下修改:
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
//动画类的重绘函数
this
.paint=
function
()
{
if
(
this
.visible)
{
this
.upFrameData();
//保存画布句柄状态
context.save();
//更改画布句柄的透明度,从这以后绘制的图像都会依据这个透明度
context.globalAlpha=
this
.alpha;
//设置画布句柄的位置,实际上是设置的图像的位置
context.translate(
this
.x,
this
.y);
//设置画布旋转角度实际上是设置了图像的角度,参数是弧度,所以我们必须把角度转换为弧度
context.rotate(
this
.rotation*Math.PI/180);
//设置画布句柄的比例,实际上是设置了图像的比例
context.scale(
this
.scaleX,
this
.scaleY);
switch
(
this
.isPlay)
{
case
1:
context.drawImage(img,
this
.mcX,
this
.mcY,
this
.frameWidth,
this
.frameHeight,-
this
.frameWidth/2,-
this
.frameHeight/2,
this
.frameWidth,
this
.frameHeight);
break
;
case
2:
//增加了帧信息的偏移量,和最后一段的动画实际宽度和高度
context.drawImage(img,
this
.mcX,
this
.mcY,
this
.width,
this
.height,
-(
this
.frameX)-
this
.frameWidth/2,
-(
this
.frameY)-
this
.frameHeight/2
,
this
.width,
this
.height);
break
default
:
context.drawImage(img,
this
.mcX,
this
.mcY,
this
.frameWidth,
this
.frameHeight,-
this
.frameWidth/2,-
this
.frameHeight/2,
this
.frameWidth,
this
.frameHeight);
break
;
}
//最后返回画布句柄的状态,因为画布句柄是唯一的,它的状态也是唯一的,当我们使用之后方便其他地方使用所以
//需要返回上一次保存的状态,就好像什么事情都没有发生过
context.restore();
}
}
|
好了,是时候加入新的切片代码了.现在我们是时候用到MovieClip2D类添加一些新成员.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
//动画实际宽度
this
.width=0;
//动画实际高度
this
.height=0;
//帧偏移X信息
this
.frameX=0;
//帧偏移Y信息
this
.frameY=0;
//动画ID
this
.nameId=0;
|
我们保留了上一章节的动画系统,也兼容了这一章的复杂动画,现在让我们回到Main.js中的init初始化函数添加如下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
//初始化函数
function
init () {
//创建场景管理器
stage2d=
new
Stage2D();
//启用场景逻辑
stage2d.init();
var
mc=
new
MovieClip2D(imageStart[0],xmlStart[0].quadDataList);
mc.isPlay=2;
mc.x=500;
mc.y=500;
stage2d.addChild(mc);
}
|
如果没有出任何问题的画,现在你可以测试页面,它应该是这个样子
动画播放起来了,但是有个很严重的问题,动画的播放速度跟抽筋了一样,早在第一章节我们就介绍过动画的帧率一般是24帧和30,但是现在我们播放的速度是几乎为0延迟的播放,所以我们现在需要加入动画帧率的概念,现在我们先让我们在MovieClip2D里加入2个新的成员变量.
1
2
3
4
5
|
//动画播放速度
this
.animationSpeed=24;
//用于计算过去的时间
this
.animationTime=0;
|
然后修改upFrameData更新函数如下:
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
26
27
28
29
30
31
32
33
34
35
|
//动画更新逻辑
this
.upFrameData=
function
()
{
switch
(
this
.isPlay)
{
case
1:
this
.mcY=
this
.frameHeadY*
this
.frameHeight;
this
.mcX=
this
.frameHeadX*
this
.frameWidth+
this
.currentFrame*
this
.frameWidth;
break
;
case
2:
this
.width=data[
this
.nameId].quadFrameLst[
this
.currentFrame].width;
this
.height=data[
this
.nameId].quadFrameLst[
this
.currentFrame].height
this
.mcX=data[
this
.nameId].quadFrameLst[
this
.currentFrame].x;
this
.mcY=data[
this
.nameId].quadFrameLst[
this
.currentFrame].y;
this
.frameX=data[
this
.nameId].quadFrameLst[
this
.currentFrame].frameX;
this
.frameY=data[
this
.nameId].quadFrameLst[
this
.currentFrame].frameY;
this
.frameWidth=data[
this
.nameId].quadFrameLst[
this
.currentFrame].frameWidth;
this
.frameHeight=data[
this
.nameId].quadFrameLst[
this
.currentFrame].frameHeight;
this
.totalFrames=data[
this
.nameId].quadFrameLst.length;
break
;
}
var
date=
new
Date();
if
((date.getTime()-
this
.animationTime>=1000/
this
.animationSpeed))
{
this
.animationTime=date.getTime();
this
.currentFrame++;
}
if
(
this
.currentFrame>=
this
.totalFrames)
{
this
.currentFrame=0;
}
}
|
看到了吗,我们只是把下一帧的循环逻辑放在了最后并且加上了一个限制,我们通过date.getTimer()来访问1970年到现在的毫秒数,然后让它和上一次记录的时间相减,这样就能求出动画运行的时间是多少了,如果大于1000/速度的时间就开始下一帧播放,我们希望用户输入的是帧率而不是毫秒 ,所以我们在底层除以了1000,现在试试新的动画系统吧,把帧率设置成24帧,漂亮的动画就这样出来了,最后,我们还有一些辅助功能需要完成,比如动画的播放和停止,跳转到某个帧并且播放和停止,切换动画等等,下面是代码片段:
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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
|
//逻辑更新开关
this
.logicUpData=
true
;
//跳转到某一帧并且播放
this
.gotoAndPlay=
function
(value)
{
this
.currentFrame=value;
this
.logicUpData=
true
;
}
//跳转到某一帧并且停止
this
.gotoAndStop=
function
(value)
{
this
.currentFrame=value;
this
.logicUpData=
false
;
}
//开始播放动画
this
.play=
function
()
{
this
.logicUpData=
true
;
}
//停止播放动画
this
.stop=
function
()
{
this
.logicUpData=
false
;
}
//查询名字
this
.queryName=
function
(name)
{
for
(
var
i=0;i<data.length;i++)
{
if
(data[i].name==name)
{
return
i;
};
};
return
0;
};
//设置场景,可以是数字或者名称
this
.scene=
function
(name)
{
if
(isNaN(name))
{
this
.nameId=
this
.queryName(name);
}
else
{
this
.nameId=name;
};
};
//动画更新逻辑
this
.upFrameData=
function
()
{
switch
(
this
.isPlay)
{
case
1:
this
.mcY=
this
.frameHeadY*
this
.frameHeight;
this
.mcX=
this
.frameHeadX*
this
.frameWidth+
this
.currentFrame*
this
.frameWidth;
break
;
case
2:
this
.width=data[
this
.nameId].quadFrameLst[
this
.currentFrame].width;
this
.height=data[
this
.nameId].quadFrameLst[
this
.currentFrame].height
this
.mcX=data[
this
.nameId].quadFrameLst[
this
.currentFrame].x;
this
.mcY=data[
this
.nameId].quadFrameLst[
this
.currentFrame].y;
this
.frameX=data[
this
.nameId].quadFrameLst[
this
.currentFrame].frameX;
this
.frameY=data[
this
.nameId].quadFrameLst[
this
.currentFrame].frameY;
this
.frameWidth=data[
this
.nameId].quadFrameLst[
this
.currentFrame].frameWidth;
this
.frameHeight=data[
this
.nameId].quadFrameLst[
this
.currentFrame].frameHeight;
this
.totalFrames=data[
this
.nameId].quadFrameLst.length;
break
;
}
if
(
this
.logicUpData)
{
var
date=
new
Date();
if
((date.getTime()-
this
.animationTime>=1000/
this
.animationSpeed))
{
this
.animationTime=date.getTime();
this
.currentFrame++;
}
if
(
this
.currentFrame>=
this
.totalFrames)
{
this
.currentFrame=0;
}
}
}
|
现在你可以回到Main.js去生成2个动画了.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
//初始化函数
function
init () {
//创建场景管理器
stage2d=
new
Stage2D();
//启用场景逻辑
stage2d.init();
var
mc=
new
MovieClip2D(imageStart[0],xmlStart[0].quadDataList);
mc.isPlay=2;
mc.x=100;
mc.y=500;
mc.scene(
"niao"
)
stage2d.addChild(mc);
var
mc2=
new
MovieClip2D(imageStart[0],xmlStart[0].quadDataList);
mc2.isPlay=2;
mc2.x=500;
mc2.y=500;
mc2.scene(0)
stage2d.addChild(mc2);
}
|
如果没有出问题的话,应该就出现下面的画面了
哇,终于结束了,现在我们离游戏又进了一步了,总结一下,我们这一章节除了代码多还是代码多,其实知识点非常得少,基本的知识点都是上2章的内容,我们现在只不过是借助了工具和XML记录了切片信息而已,现在这个动画系统还不是很完善,你可以自己添加帧事件,帧标签,动画延迟(打击感强的游戏都会用到这个技术,比如当挥砍的动作触碰到怪物时可以让这个动画在一定时间段之类,等等,如果有兴趣欢迎来群里交流,最后我得想想下一章的内容了。
转载请注明:HTML5游戏开发者社区 » HTML5 2D游戏引擎研发系列 第五章