XNA动画的实现8——动画混合

目前为止,我们的动画播放器类一次只能播放一个动画片段,如果要同时播放两个动画片段,一种方法是创建两个动画播放器类,但为了让代码更加易用,应该编写一个新的类将它们封装起来。

而且,在XNA动画的实现5——通过ContentManager加载动画数据一文中,我们在一个ModelAnimationData中实现了三个AnimationClip,一个是转头,一个是转身体,第三个是一边转头一边转身体,我们发现一边转头一边转身体动画实际上就是转头和转身体两个动画的组合,根本没必要单独设置。在实际开发中,美术师也是只制作单独的动画,然后在程序中将单独的动画组合起来形成一个新动画,比方说只制作行走、挥手、摇头动画,然后将行走和挥手动画组合起来实现一边行走一边挥手,将行走和摇头组合起来实现一边行走一边摇头,这样可以避免制作多余的动画。

ModelAnimationBlender类

ModelAnimation本身的原理很简单,只是封装了两个动画播放类,再添加了一些额外的控制代码。

在AnimationClassLibrary类库项目中新建一个类,代码如下:

using Microsoft.Xna.Framework; 
using Microsoft.Xna.Framework.Graphics; 

namespace AnimationClassLibrary
{
    public class ModelAnimationBlender
    {
        struct animationProperty 
        {
            public string animationName;
            public bool loop; 
            public float playBackRate;
            public int startIndex;
            public int endIndex;
        }
        
        ModelAnimationData animationData;  // 动画数据
        RigidAnimationPlayer[] rigidPlayers; // 播放器数组,包含两个播放器类 
        int animationClipCount; // 当前混合的动画片段数量
        animationProperty animationProperty0; 
        animationProperty animationProperty1; 
        
        public ModelAnimationBlender(ModelAnimationData animationData,ref Model model) 
        {
            // 初始化动画数据和播放器类
            this.animationData = animationData;
            animationClipCount = 0; 
            animationProperty0 = new animationProperty();
            animationProperty1 = new animationProperty(); 
            
            rigidPlayers = new RigidAnimationPlayer[2];
            rigidPlayers[0] = new RigidAnimationPlayer(ref model);
            rigidPlayers[1] = new RigidAnimationPlayer(ref model);
        }
        
        public void Blend(string animationClipName, bool loop, float playBackRate)
        {
            this.Blend(animationClipName, loop, playBackRate, 0,
            animationData.ModelAnimationClips animationClipName].Keyframes.Count - 1); 
        } 
        
        public void Blend(string newAnimationClipName, bool loop, float playBackRate, int startIndex, int endIndex) 
        {
            if (!animationData.ModelAnimationClips.ContainsKey(newAnimationClipName)) 
                return; 
                
            // 如果目前还没有播放动画,则使用第一个播放器播放新加入的动画片段 
            if (this.animationClipCount == 0) 
            {
                animationProperty0.animationName = newAnimationClipName;
                animationProperty0.loop = loop;
                animationProperty0.playBackRate = playBackRate; 
                animationProperty0.startIndex = startIndex;
                animationProperty0.endIndex = endIndex; 
                
                rigidPlayers[0].StartClip(animationData.ModelAnimationClips[newAnimationClipName], 
                loop, playBackRate, startIndex, endIndex); 
                animationClipCount++;
            } 
            
            // 如果目前播放了一个动画,则使用第二个播放器播放新加入的动画片段
            else if (this.animationClipCount == 1)
            {
                animationProperty1.animationName = newAnimationClipName; 
                animationProperty1.loop = loop;
                animationProperty1.playBackRate = playBackRate; 
                animationProperty1.startIndex = startIndex; 
                animationProperty1.endIndex = endIndex; 
                
                rigidPlayers[1].StartClip(animationData.ModelAnimationClips[newAnimationClipName], loop, 
                playBackRate, startIndex, endIndex); 
                animationClipCount++;
            }
            
            // 如果现在已经播放了两个动画片段 
            else if (this.animationClipCount == 2)
            {
                // 则在动画片段数组中移除第一个动画,将第二个动画作为第一个动画, 
                // 将新加入的动画作为第二个动画 
                animationProperty0.animationName = animationProperty1.animationName; 
                animationProperty0.loop = animationProperty1.loop; 
                animationProperty0.playBackRate = animationProperty1.playBackRate; 
                animationProperty0.startIndex = animationProperty1.startIndex; 
                animationProperty0.endIndex = animationProperty1.endIndex; 
                
                animationProperty1.animationName = newAnimationClipName; 
                animationProperty1.loop = loop;
                animationProperty1.playBackRate = playBackRate; 
                animationProperty1.startIndex = startIndex; 
                animationProperty1.endIndex = endIndex; 
                
                rigidPlayers[0].StartClip(animationData.ModelAnimationClips[animationProperty0.animationName],
                 animationProperty0.loop, animationProperty0.playBackRate,
                 animationProperty0.startIndex, animationProperty0.endIndex);
                rigidPlayers[1].StartClip(animationData.ModelAnimationClips[newAnimationClipName], 
                loop, playBackRate, startIndex, endIndex); 
            }
        }
        
        /// <summary>
        /// 更新动画数据
        /// </summary>
        public void Update(GameTime gameTime)
        {
            rigidPlayers[0].Update(gameTime); 
            rigidPlayers[1].Update(gameTime);
        }
    }
}

ModelAnimationBlender类的使用

使用这个类是很简单的,首先你需要定义这个类:

ModelAnimationBlender blender;

然后在LoadContent方法中进行初始化:

protected override void LoadContent()
{
    … 
    
    // 初始化动画播放器
    blender = new ModelAnimationBlender(modelAnimationData, ref model);
}

最后在Update方法中更新动画数据:

protected override void Update(GameTime gameTime) 
{
    … 
    
    // 播放一个非根节点、一个根节点动画 
    if (IsNewKeyPress(Keys.D1))
    {
        blender.Blend("TurnHat", false, 1.0f); 
        blender.Blend("BodyMoveX", false, 1.0f);
    }
    
    // 播放两个非根节点动画 
    if (IsNewKeyPress(Keys.D2))
    {
        blender.Blend("TurnHat",false ,1.0f); 
        blender.Blend("TurnHead", false, 1.0f); 
    }
    
    // 播放两个根节点动画会发生冲突 
    if (IsNewKeyPress(Keys.D3))
    {
        blender.Blend("BodyRotationY", true, 1.0f); 
        blender.Blend("BodyMoveX", false, 1.0f); 
    }
    
    blender.Update(gameTime); 
    base.Update(gameTime); 
}

程序截图如下:

程序截图

扩展

这个动画混合类是非常简陋的,有很大的改进空间,其实都没资格叫做混合类,只是机械地将两个动画同时播放,根本就没有混合的概念。如果想实现在播放一个动画之后0.1秒后再播放另一个动画或者动画的淡入淡出等效果,需要添加更多的代码,参考资料太少,我目前无法完成,不过这个类在后面的系列文章第10篇中足够用了。

文件下载(已下载 1035 次)

发布时间:2012/1/9 下午4:36:31  阅读次数:5885

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

沪 ICP 备 18037240 号-1

沪公网安备 31011002002865 号