《Windows Phone开发实战体验(应用+游戏)》详细介绍及源代码下载:
http://www.devdiv.com/%E3%80%8AWindows_Phone%E5%BC%80%E5%8F%91%E5%AE%9E%E6%88%98%E4%BD%93%E9%AA%8C_%E5%BA%94%E7%94%A8_%E6%B8%B8%E6%88%8F_%E3%80%8B%E5%BC%80%E5%94%AE-thread-123714-1-1.html
18.1 播放游戏精灵动画
在第16章中介绍了如何用SpriteBatch显示2D纹理。其中的Sprite(精灵),在游戏编程里指一个对象的原型,例如一个战士、一个怪物等。精灵不可能不做动作,就拿简单的走动来说,手脚一定是会动的,那么如何在屏幕上真实地呈现各种动作呢?在2D游戏编程里,通常把人物的动作分解为一帧帧图片,通过连续播放产生动画的效果,与早期的二维动画片制作原理一样。
1)单次播放动画
图18-1是一个小兵的行走动画,可以看到由12张图片组成,那么如何能将这些图片连续地循环播放呢?
图18-1 小兵行走动画分解图
一提到循环,很容易想到for循环。把这12张图片放到一个Texture2D[ ] 数组里,通过for循环实现动画效果代码如下:
for(int i=0;i<SoldierTextures.Count;i++) //伪代码
{
spriteBatch.Draw(SoldierTextures[i]);
}
2)循环播放动画
用for循环播放动画只能播放一遍,如何才能循环播放呢?可以用之前提到的XNA里的Update函数实现。它可以按固定的时间间隔反复执行,可以把这个函数看做一个特殊的for循环。
使用Update函数实现动画循环播放还需要借助一个全局计数器FrameCount,代码如下:
int FrameCount=0;
void Update() //在Update函数里改变计数器
{
FrameCount++;
if(FrameCount>SoilderTextures.Length-1)//如果播完最后一帧
{
FrameCount=0; //就回到第一帧
}
}
void Draw() //在Draw函数里绘制出纹理
{
spriteBatch.Draw(SoilderTextures[FrameCount]);
}
3)编程提示
以上代码都是伪代码,是为了便于大家理解,实际编程中的代码比这里要复杂些。
① 在GameMainScreen类添加构造函数:
Texture2D[] soilderTextures;
public GameMainScreen()
{
soilderTextures=new Texture2D[12];//初始化小兵纹理数组
}
② 把12张小兵跑动图片放到Content项目下的Enemy/Run文件夹中,如果18-2所示。
图18-2 图片存放位置
③ 用for循环加载这12张图片,代码如下:
public override voidLoadContent()
{
base.LoadContent();
playerTexture = ScreenManager.Game.Content.Load<Texture2D>("Player/1");
for(int i = 0; i < 12;i++ )
{
soilderTextures[i]=ScreenManager.Game.Content.Load<Texture2D>("Enemy/Run/"+(i+1));
}
}
④ 在Draw里绘制出当前帧,代码如下:
public override voidDraw(GameTime gameTime)
{
ScreenManager.SpriteBatch.Begin();
ScreenManager.SpriteBatch.Draw(soilderTextures[FrameCount], newVector2(100, 200), Color.White);
ScreenManager.SpriteBatch.End();
}
⑤ 在Update里更新当前帧的位置,依次向后播放,代码如下:
public override voidUpdate(GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen)
{
base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
floatelapsedTime = (float)gameTime.ElapsedGameTime.TotalSeconds;
FrameCount++;
if(FrameCount > soilderTextures.Length - 1)//如果播完最后一帧
{
FrameCount = 0; //就回到第一帧
}
}
在模拟器中的最后运行效果如图18-3所示。
图18-3 运行效果
4)动画频率调整
在模拟器里执行上面写好的代码,可以发现能够循环播放动画了,不过出现了新问题:小兵的跑动频率太快,像超人一样在飞速奔跑。
之所以会出现这种情况,是因为人物动画帧播放的时间间隔其实是远长于Update函数执行的时间间隔的,例如人物动画是1秒播放一帧,而Update函数是1/30秒(0.03333秒)播放一帧。那么如何处理这个问题呢?
回到前面的代码,其实只要控制“FrameCount++”语句执行的时间间隔就可以了。为此需要用到另外一个计数器updateCount,代码如下:
updateCount++;
if(updateCount>TimeSpan) //伪代码,TimeSpan为时间间隔
{
FrameCount++;
updateCount=0;
}
通过修改TimeSpan的值就能控制FrameCount++执行的时间间隔了。假如TimeSpan=30, 那么Update函数要执行30次,FrameCount++才执行一次。也就说FrameCount++执行的时间间隔为 30*0.033333秒=1秒。
经过反复调整,我们发现TimeSpan=2时,小兵的动作最协调,代码如下:
int updateCount=0,timeSpan=2;
public override void Update(GameTimegameTime, bool otherScreenHasFocus, bool coveredByOtherScreen)
{
base.Update(gameTime,otherScreenHasFocus, coveredByOtherScreen);
float elapsedTime =(float)gameTime.ElapsedGameTime.TotalSeconds;
updateCount++;
if (updateCount > timeSpan) // timeSpan为时间间隔
{
FrameCount++;
updateCount = 0;
}
if (FrameCount >soilderTextures.Length - 1)//如果播完最后一帧
{
FrameCount = 0; //就回到第一帧
}
}
运行该段代码,发现精灵动画能够自如地播放了。不过这个小兵精灵并不能前后移动,仍然无法满足游戏需要。这个问题留待下一节解决。