刚吃了晚饭,看了会儿成龙的《重案组》,继续研究MCDX.
今天把原来的泡泡堂人物素材用MCDX绘制看看,并仔细研究了一下CDXAnimation 类,发现运行结果并不是我想要的;首先我还是这样编写了代码:
playerTile
=
new
CDXTile(cdxControl1.Screen,
"
player1.bmp
"
,
48
,
57
,
16
, MemTypes.SystemOnly);//16个图像帧
playerTile.ColorKey
=
Color.FromArgb(
255
,
0
,
255
); //透明色=紫色

player
=
new
CDXSprite(playerTile,
-
1
);

player.Animation
=
new
CDXAnimation();
//增加每个方向的动画关键帧

int
[] animUp
=
new
int
[
6
]
{8,9,10,11,10,9}
;

int
[] animDown
=
new
int
[
6
]
{0,1,2,3,2,1}
;

int
[] animLeft
=
new
int
[
6
]
{4,5,6,7,6,5}
;

int
[] animRight
=
new
int
[
6
]
{12,13,14,15,14,13}
;
//添加到管理器
player.Animation.AddAnimation(
3
,
0
,
false
, animUp);
player.Animation.AddAnimation(
3
,
0
,
false
, animDown);
player.Animation.AddAnimation(
3
,
0
,
false
, animLeft);
player.Animation.AddAnimation(
3
,
0
,
false
, animRight);
看似一切正常;图像素材的结构为:4x4 ,分别是 下、左、上、右 ,索引为 0~15 共16帧画面;这个是原图:
那么按键盘DOWN的时候按我的设置值应该是从 0 播放到 3 ,再从3 退回到 1 ;但是最终结果总是发现第0帧不显;检查MCDX自带的sample的素材可以看出第0帧就是空的;这个是MCDX的素材:

,看样子是刻意这样处理的,那我的元素显然不符合MCDX的标准了,但总不至于要我修改素材吧,那可是件痛苦的事情,还是修改MCDX的CDXTile类吧;
虽然修改很容易,但还是先得了解它机制;先看一下它是如何切分图形元素的;
int
i, x, y;

this
.blockWidth
=
blockWidth;
this
.blockHeight
=
blockHeight;
this
.blockCount
=
blockCount;

if
(
this
.blockCount
==
0
)
this
.blockCount
=
(Width
/
blockWidth)
*
(Height
/
blockHeight);

blockRects
=
new
System.Drawing.Rectangle[
this
.blockCount];

for
(i
=
0
, x
=
0
, y
=
0
; i
<
this
.blockCount; i
++
, x
+=
blockWidth)

{
if(x > Width - blockWidth)

{
x = 0;
y += blockHeight;
}

blockRects[i] = new System.Drawing.Rectangle(x, y, blockWidth, blockHeight);
}
这里可以看到它按指定的数目切割,并把Rect保存到blockRects数组内;现在可以看看Draw的方法实现:
if
(tile
==
0
||
tile
>
blockCount)
return
;

if
(tile
<
0
)
tile
=
animation.GetAnimationTile(tile);

base
.Draw(surface, x, y, blockRects[tile], bltType);
哦,原来在这里已经屏蔽掉0了;修改一下就行:
//
if(tile == 0 || tile > blockCount) return;
if
(tile
>
blockCount)
return
;
这里的Draw是调用的父类的方法,归根结底就是
Surface.DrawFast(destX, destY, surface, srcRect, DrawFastFlags.Wait | DrawFastFlags.SourceColorKey);
经过修改已经可以正确显示我的素材图像了;
虽然目前已经有了‘良好’的开始,但还有几个小细节需要注意;还是回头看看先的代码:
player = new CDXSprite(playerTile,-1);
参数2是TileNumber,为什么是-1 ,还有TileNumber究竟有什么用,什么地方用到?
刚开始我自己都摸不着头脑,看它自己带的CHM发现基本上什么都没说,只是告诉你,这个是一个名为TileNumber的int类型参数;但是它的源码倒是有稍微‘详细’点的注解(可以用VS.net自带的“生成注解WEB页”功能把这些信息提取出来),不过要彻底明白还是得看源码;
首先还是打开CDXSprite类,看看这个tileNumber究竟传给谁;原来还是 Draw 方法用到:
int
tileNum
=
tileNumber;

if
(tileNum
==
0
||
tileNum
>
tile.BlockCount)
return
;

//
Only calculate the tile animation if we are NOT using the
//
Tile's animations.
if
(tileNum
<
0
&&
!
useTileAnimation)
tileNum
=
animation.GetAnimationTile(tileNum);

tile.Draw(x
+
posX, y
+
posY, surface, bltType, tileNum);
看来这段代码中还是屏蔽了0;暂且不管这点先,至少 tile支持Draw索引0了;现在不用再看tile.Draw了,因为上面已经看过也改过了;看看animation.GetAnimationTile方法;来自CDXAnimation 类,具体代码如下:
if
(tile
>
0
)
return
tile;

tile
=
(
-
tile)
-
1
;
if
(tile
>=
animationData.Count)
return
0
;

AnimationData animation
=
(AnimationData)animationData[tile];

int
pos
=
(frameCounter
/
animation.frameRate)
%
(animation.anim.Length
+
animation.pause);
if
( pos
>=
animation.anim.Length) pos
=
0
;

return
animation.anim[pos];
哦,原来tile被传递过来进行了翻译;就是这句:
tile
=
(
-
tile)
-
1
;
最终通过tile作为索引从名为animationData得ArrayList中取回关键帧得结构AnimationData;结构描述如下:
public
struct
AnimationData

{

/**//// <summary>The animation frame rate.</summary>
public int frameRate;

/**//// <summary>The length between frames.</summary>
public int pause;

/**//// <summary>Whether or not the animation uses a ping-pong cycle</summary>
public bool pingpong;

/**//// <summary>An array of tile indexes that comprise the animation frames.</summary>
public int[] anim;
}
这个animationData就是由AddAnimation方法维护得
这个animationData就是由AddAnimation方法维护得
public
void
AddAnimation(
int
frameRate,
int
pause,
bool
pingpong,
int
[] anim)

{
}
综上;现在控制播放动画没问题了;可以结合键盘控制来完成这个sprite程序了;
input.Update();
bool
canDrawNextFrame
=
true
;


if
(input.KeyState(MCDX.Keys.UpArrow)
==
KeyStates.Press
||
input.KeyState(MCDX.Keys.UpArrow)
==
KeyStates.Repeat)
{
player.TileNumber = -1;
player.PosY -- ;

}
else
if
(input.KeyState(MCDX.Keys.DownArrow)
==
KeyStates.Press
||
input.KeyState(MCDX.Keys.DownArrow)
==
KeyStates.Repeat)
{
player.TileNumber = -2;
player.PosY ++ ;

}
else
if
(input.KeyState(MCDX.Keys.LeftArrow)
==
KeyStates.Press
||
input.KeyState(MCDX.Keys.LeftArrow)
==
KeyStates.Repeat)
{
player.TileNumber = -3;
player.PosX -- ;

}
else
if
(input.KeyState(MCDX.Keys.RightArrow)
==
KeyStates.Press
||
input.KeyState(MCDX.Keys.RightArrow)
==
KeyStates.Repeat)
{
player.TileNumber = -4;
player.PosX ++ ;

}
else
{
canDrawNextFrame =false;
}


if
(canDrawNextFrame
&&
player.Animation
!=
null
)
{
player.NextFrame();
player.Animation.NextFrame();
}
canDrawNextFrame是为了避免没有按键得情况下它还自动播放动画,不控制是不对的。
虽然搞清楚了这些细节,但从使用角度来说是不是有点麻烦;何不干脆提供一个SetAnimationStartPos之类得方法直接制定播放起始关键帧呢?真还不知道里面还有多少弯弯道道等着去找……
上回发的这个MCDX笔记,有兄弟说无法正常运行;我还是帖源码上来,不过我的MCDX做过部分修改,不保证到贵鸡里正常运行了;
点这里下载