通过上面几节的学习,慢慢的掌握了精灵的一些基本常识,但是我们知道游戏中游戏精灵都是富于表现能力的,并且通过不同的动作或者动画去构造一个游戏。

这篇文章将学习如何使用系列图为游戏精灵添加动画效果,以下面这一系列图为例,将其分割显示:

--这个图片是偷 深蓝 的

 

    这是一张png图片,我们还是需要把它加载到纹理图形中,可以考虑如何在精灵位图上轮流获得独立的精灵帧。以下编写这个精灵帧所要先得到的信息:

  • 精灵位图中每个单独图像(帧)的宽和高
  • 精灵位图的行与列的总数
  • 指示接下来精灵位图中将要绘制精灵帧在精灵位图中所处的行与列的位置索引

上面的那张精灵位图中,每个单独精灵帧的宽和高都是150像素,有10行1列。所以我们要绘制它显示在窗口上,就得从第一个精灵帧开始绘制。而这时候我们就要编写下面的三行代码用来控制精灵帧的切换:

 

 Point frameSize 
=
 
new
 Point(
150
150
);    
//
每帧的长与高
        Point currentFrame 
=
 
new
 Point(
0
0
);   
//
初始化第一帧
        Point sheetSize 
=
 
new
 Point(
10
1
);     
//
定义一个10 列1行的point ,本图片一共有一列10个小人
                                                  
//
如果有多列则相应改动后面的1

     上篇文章我们有使用SpriteBatch.Draw 的重载方法,其中参数三是一个Rectangle 对象,上篇我们不设置矩形范围,所以给定一个NULL。而本便我们需要使用一个 Rectangle 对象来算出该位置的源矩形。添加下面的代码:

 

 spriteBatch.Begin(SpriteSortMode.FrontToBack, BlendState.AlphaBlend);
            spriteBatch.Draw(enemy,
//
纹理图像
                
new
 Vector2((graphics.GraphicsDevice.Viewport.Width
/
2
)
-
(frameSize.X
/
2
), 
                    (graphics.GraphicsDevice.Viewport.Height
/
2
)
-
(frameSize.Y
/
2
)), 
//
将系列图放在屏幕中间播放
                
new
 Rectangle(
                    currentFrame.X 
*
 frameSize.X,
//
当前的帧点的x 轴乘以每行移动的宽度得到该矩形从屏幕哪个坐标画
                    currentFrame.Y 
*
 frameSize.Y, 
//
当前的帧点的Y 轴乘以每行移动的高度得到该矩形从屏幕哪个坐标画 
                    frameSize.X,
//
需要画该矩形块的宽度为系列图每格小人物的宽度
                    frameSize.Y),
//
需要画该矩形块的高度为系列图每格小人物的高度
                    Color.White,
                    
0
,  
//
旋转图像,按角度旋转图像,0为不旋转,依次类似1、2、3、4、5等
                    Vector2.Zero,
//
指定旋转的参照点
                    
1
,  
//
缩放比例,这里是按默认比例绽放
                    SpriteEffects.None, 
//
不翻转图像
                    
0
   
//
纹理层深度
                    );
            spriteBatch.End();

  如果按照上面的代码写在Draw 里面的话,还是没有动画效果的,因为我们一直重复的上面这张精灵位图的第一帧,为了产生动画,还必须在Update 里面完成状态的更新:

 

 
++
currentFrame.X;   
//
将下标要画的X坐标剃增,即改变该列的位置向下个位置转移
                
if
 (currentFrame.X 
>=
 sheetSize.X)    
//
如果下标大于或者等于系列图的当前列的数量
                {
                    currentFrame.X 
=
 
0
//
重新将下标初始化为0
                    
++
currentFrame.Y;   
//
将将下标要画的Y坐标剃增,即改变该列的位置向下个位置转移
                    
if
 (currentFrame.Y 
>=
 sheetSize.Y)    
//
如果当前的要跳的行大于或等于该系列图的总行数
                    {
                        currentFrame.Y 
=
 
0
;     
//
重新将其初始化为0
                    }
                }

 

这个时候,可以ctrl+F5 运行游戏,看看效果。

尽管游戏看起来动画效果不错。但有没有发现这个动画的轮换速度也太快了,因为游戏每秒更新30次状态这个己经是很快的速度了。为了使其能够按照我们要求的速度进行精灵位图切换,我们可以在Update 的时候做下小手脚,使其动画速度为可控状态,看代码:

 
int
 timeSinceLastFrame 
=
 
0
;             
//
用来追踪上一帧之后经过多少时间
        
int
 millsecondPreFrame 
=
 
100
;        
//
用来指定在移动当前的帧索引之前想要等待的时间隔

 

修改后的Update 方法,应该是这样子的:

 

 
protected
 
override
 
void
 Update(GameTime gameTime)
        {
            
//
 Allows the game to exit
            
if
 (GamePad.GetState(PlayerIndex.One).Buttons.Back 
==
 ButtonState.Pressed)
                
this
.Exit();
            timeSinceLastFrame 
+=
 gameTime.ElapsedGameTime.Milliseconds;    
//
每次累加游戏的时间
            
if
 (timeSinceLastFrame 
>
 millsecondPreFrame)    
//
如果大于需要等待的时间
            {
                timeSinceLastFrame 
-=
 millsecondPreFrame;       
//
重新加累加的时间初始化
                
++
currentFrame.X;   
//
将下标要画的X坐标剃增,即改变该列的位置向下个位置转移
                
if
 (currentFrame.X 
>=
 sheetSize.X)    
//
如果下标大于或者等于系列图的当前列的数量
                {
                    currentFrame.X 
=
 
0
//
重新将下标初始化为0
                    
++
currentFrame.Y;   
//
将将下标要画的Y坐标剃增,即改变该列的位置向下个位置转移
                    
if
 (currentFrame.Y 
>=
 sheetSize.Y)    
//
如果当前的要跳的行大于或等于该系列图的总行数
                    {
                        currentFrame.Y 
=
 
0
;     
//
重新将其初始化为0
                    }
                }
            }
           
            
//
 TODO: Add your update logic here
            
base
.Update(gameTime);
        }

 

这时,我们再来看看这个效果:

 
下一篇将会学习到关于用户输入和碰撞检测方面的知识。