XNA动画的实现2——动画片段类的实现

在上一篇文章XNA动画的实现1——基本原理中已经实现的动画的代码,这篇文章将要将这些代码封装成一个类,有了前一篇文章的基础,这个类实现起来是非常容易的。这个思路主要参考了《3D Graphics with XNA Game Studio 4.0》(本网站有电子书下载)的9.1 ObjectAnimation一节。

首先新建一个XNA Game项目,然后在项目中添加一个新的类,我命名为SimpleAnimationClip,代码如下:

/// <summary>
/// 动画片段类,这个类根据输入的开始时刻和结束时刻的位置、旋转、缩放和动画持续时间,自动计算此动画片段时间内各时刻的转换矩阵。 
/// </summary>
public class SimpleAnimationClip
{
    // 开始时刻和结束时刻的位置、旋转、缩放 
    Vector3 startPosition, endPosition, startRotation, endRotation,startScale, endScale; 
    Vector3 position,rotation,scale; // 当前时刻的位置、旋转和缩放
    TimeSpan duration; // 动画持续时间
    TimeSpan elapsedTime = TimeSpan.FromSeconds(0); // 动画播放已流逝的时间
    
     /// <summary>
     /// 是否循环播放动画
     /// </summary>
     public bool Loop{ get;set;}
     
     /// <summary>
     /// 变换矩阵
     /// </summary>
     public Matrix Transform{ get;set;}
     
     /// <summary>
     /// 创建一个动画片段,默认不循环播放
     /// </summary>
     public SimpleAnimationClip(Vector3 StartPosition, Vector3 EndPosition, Vector3 StartRotation, Vector3 EndRotation, Vector3 StartScale,Vector3 EndScale,TimeSpan Duration)
     {
         startPosition = StartPosition;
         endPosition = EndPosition;
         startRotation = StartRotation;
         endRotation = EndRotation; 
         startScale = StartScale;
         endScale = EndScale;
         
         position = startPosition; 
         rotation = startRotation;
         scale = startScale;
         
         duration = Duration;
         Loop = false;
     }
     
     public void Update(TimeSpan Elapsed)
     {
         // 更新动画播放的时间
         this.elapsedTime += Elapsed; 
         // 计算动画流逝的进度,这个值在0和1之间,0表示动画开始时刻,1表示动画结束时刻 
         float amt = (float)elapsedTime.TotalSeconds / (float)duration.TotalSeconds; 
         
         if (Loop) 
             while (amt > 1)
                 // 如果循环播放动画则重置amp的值
                 amt -= 1;
         else
             // 如果不循环播放动画则将amt的值截取为最后时刻的值
             amt = MathHelper.Clamp(amt, 0, 1); 
         
         // 通过插值运算更新当前的位置、旋转和缩放
         position = Vector3.Lerp(startPosition, endPosition, amt); 
         rotation = Vector3.Lerp(startRotation, endRotation, amt); 
         scale =Vector3 .Lerp (startScale, endScale, amt);
         
         Transform = Matrix.CreateScale(scale) * Matrix.CreateFromYawPitchRoll(rotation.Y, rotation.X, tation.Z) * Matrix.CreateTranslation(position);
     }
 }

原理在上一篇文章已经介绍过了,新的东西只有使用了Lerp方法进行了线性插值运算。

有了这个动画片段类,我们只需在游戏主类中添加这个对象:

SimpleAnimationClip animation; 

然后在Initialize方法中初始化这个类即可。要初始化这个类,你需要指定开始和结束时刻的位置、旋转和缩放以及播放时间。

protected override void Initialize()
{
    […]
    
    // 初始化动画类
    animation = new SimpleAnimationClip(Vector3.Zero, new Vector3(16, 0, 16), Vector3.Zero, new Vector3(0, MathHelper.ToRadians(180), 0), Vector3.One, new Vector3(3.0f, 3.0f, 3.0f), TimeSpan.FromSeconds(4)); 
    
    base.Initialize();
}

然后在Update方法中调用动画片段类的Update方法计算变换矩阵,然后将这个矩阵赋予你想施加动画的Bone.Transform。

protected override void Update(GameTime gameTime)

{
    […]
    // 更新动画类
  animation.Update(gameTime.ElapsedGameTime ); 

    model.Bones["Body"].Transform = animation.Transform * originalBodyTransform;
    //model.Bones["Head"].Transform = animation.Transform * originalHeadTransform;
    // 下面这行注释的代码效果与上一行代码相同,
    // 通过索引访问会比通过Bone名称访问稍微快一些
    //model.Bones[2].Transform = animation.Transform * originalHeadTransform; 
    
    base.Update(gameTime); 
}

这个代码实现的效果与上一篇文章是完全相同的,只不过使用起来更加方便。 如果你使用过Flash,你会发现这其实就是补间动画。如下图所示,我们可以看出,在Flash中我们只需在时间轴上创建两个关键帧,就可以实现这两个位置之间的直线运动。更强大的地方在于Flash还有一个动画编辑器,通过它你可以实现变速运动,曲线运动。

Flash补间动画1

如上图所示,如果要在Flash中让红色小球在时间中点改变运动方向,该怎么办?非常简单,在第15帧再设置一个关键帧即可,如下图所示:

Flash补间动画2

而目前我们的SimpleAnimationClip类只能实现匀速直线运动,因为本质上这个类相当于只能使用初末两个时刻的关键帧,要能中途转弯,必须要加以改进,方法和Flash中是一样的,即添加关键帧类,我们将在下一篇文章中加以讨论。

文件下载(已下载 1171 次)

发布时间:2011/8/30 下午2:11:57  阅读次数:6118

2006 - 2024,推荐分辨率 1024*768 以上,推荐浏览器 Chrome、Edge 等现代浏览器,截止 2021 年 12 月 5 日的访问次数:1872 万 9823 站长邮箱

沪 ICP 备 18037240 号-1

沪公网安备 31011002002865 号