Ogre实现不同动画之间的混合

本文介绍了一个用于Ogre引擎的动画混合实现,通过三种不同的混合方式实现平滑的动画过渡效果。详细解析了AnimationBlender类的工作原理及其实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

英文原文: http://test.ogitor.org/tiki/AnimationBlender

动画混合 -- 实现两个动画的切换, 一个动画逐渐消逝, 另一个动画逐渐显示来实现. 主要通过动画状态的权重来实现
通过三种方式来实现两个动画的混合:
    - BlendSwitch - 直接切换至目标动画
    - BlendWhileAnimating - 混合的过程中目标动画也更新帧, 实现动画
    - BlendThenAnimate - 用源动画的当前帧混合目标动画的第一个帧

源码理解, 主要代码位于下面两个函数
AnimationBlender::blend函数 根据传入的参数设置新转换的源动画和目标动画
AnimationBlender::add函数则更新源动画和目标动画(如存在)的状态和权重

AnimationBlender::blend函数
1. 如果动画转换类型为 AnimationBlender::BlendSwitch
    直接将源动画转换成目标动画
2. 如果不是这个转换类型 AnimationBlender::BlendSwitch
    如果上次的动画转换还未完成
        如果新的目标动画等于上次转换的源动画
            则反向转换
        如果新的目标动画不等于上次转换的源动画和目标动画
            根据上次转换的剩余时间设置是显示上次转换的源动画还是目标动画, 并将其设置为新转换的源动画
            显示新转换的目标动画, 并设置其权重
    假设上次的动画转换已经完成了转换
        设置转换时间, 转换类型, 目标动画和其权重

AnimationBlender::addTime 函数
1. 如有动画在运行
    如果存在转换
        更新剩余时间
            如剩余时间小于0
                禁止源动画
                将目标动画设置为源动画, 继续运行
            如剩余时间不小于0
                更新源动画和目标动画的权重
                如转换类型为AnimationBlender::BlendWhileAnimating
                    更新目标动画
    判断动画是否完成
        更新源动画

完整代码和Demo代码

AnimationBlender.h

01 #ifndef __ANIMATION_BLENDER_H__
02 #define __ANIMATION_BLENDER_H__
03 #include <Ogre.h>
04 using namespace Ogre;
05 class AnimationBlender
06 {
07 public:
08     enum BlendingTransition
09     {
10         BlendSwitch,         // stop source and start dest
11         BlendWhileAnimating,   // cross fade, blend source animation out while blending destination animation in
12         BlendThenAnimate      // blend source to first frame of dest, when done, start dest anim
13     };
14 private:
15     Entity *mEntity;
16     AnimationState *mSource;
17     AnimationState *mTarget;
18     BlendingTransition mTransition;
19     bool loop;
20     ~AnimationBlender() {}
21 public
22     Real mTimeleft, mDuration;
23     bool complete;
24     void blend( const String &animation, BlendingTransition transition, Real duration, bool l=true );
25     void addTime( Real );
26     Real getProgress() { return mTimeleft/ mDuration; }
27     AnimationState *getSource() { return mSource; }
28     AnimationState *getTarget() { return mTarget; }
29     AnimationBlender( Entity *);
30     void init( const String &animation, bool l=true );
31 };
32 #endif

AnimationBlender.cpp

001 #include "AnimationBlender.h"
002 void AnimationBlender::init(const String &animation, bool l)
003 {
004     // 初始化, 将所有的动画禁止, 只允许参数中的动画运行.
005     AnimationStateSet *set = mEntity->getAllAnimationStates();
006     AnimationStateIterator it = set->getAnimationStateIterator();
007     while(it.hasMoreElements())
008     {
009         AnimationState *anim = it.getNext();
010         anim->setEnabled(false);
011         anim->setWeight(0);
012         anim->setTimePosition(0);
013     }
014     mSource = mEntity->getAnimationState( animation );
015     mSource->setEnabled(true);
016     mSource->setWeight(1);
017     mTimeleft = 0;
018     mDuration = 1;
019     mTarget = 0;
020     complete = false;
021     loop = l;
022
023 void AnimationBlender::blend( const String &animation, BlendingTransition transition, Real duration, bool l )
024 {
025     loop = l;
026     if( transition == AnimationBlender::BlendSwitch )
027     {
028         if( mSource != 0 )
029             mSource->setEnabled(false);
030         mSource = mEntity->getAnimationState( animation );
031         mSource->setEnabled(true);
032         mSource->setWeight(1);
033         mSource->setTimePosition(0);
034         mTimeleft = 0;
035     
036     else 
037     
038         AnimationState *newTarget = mEntity->getAnimationState( animation );
039         if( mTimeleft > 0 )
040         {
041             // oops, weren't finished yet
042             if( newTarget == mTarget )
043             {
044                 // nothing to do! (ignoring duration here)
045             }
046             else if( newTarget == mSource )
047             {
048                 // going back to the source state, so let's switch
049                 mSource = mTarget;
050                 mTarget = newTarget;
051                 mTimeleft = mDuration - mTimeleft; // i'm ignoring the new duration here
052             }
053             else
054             {
055                 // ok, newTarget is really new, so either we simply replace the target with this one, or
056                 // we make the target the new source
057                 if( mTimeleft < mDuration * 0.5 )
058                 {
059                     // simply replace the target with this one
060                     mTarget->setEnabled(false);
061                     mTarget->setWeight(0);
062                 }
063                 else
064                 {
065                     // old target becomes new source
066                     mSource->setEnabled(false);
067                     mSource->setWeight(0);
068                     mSource = mTarget;
069                 
070                 mTarget = newTarget;
071                 mTarget->setEnabled(true);
072                 mTarget->setWeight( 1.0 - mTimeleft / mDuration );
073                 mTarget->setTimePosition(0);
074             }
075         }
076         else
077         {
078             // assert( target == 0, "target should be 0 when not blending" )
079             // mSource->setEnabled(true);
080             // mSource->setWeight(1);
081             mTransition = transition;
082             mTimeleft = mDuration = duration;
083             mTarget = newTarget;
084             mTarget->setEnabled(true);
085             mTarget->setWeight(0);
086             mTarget->setTimePosition(0);
087         }
088     }
089 }
090 void AnimationBlender::addTime( Real time )
091 {
092     if( mSource != 0 )
093     {
094         if( mTimeleft > 0 )
095         {
096             mTimeleft -= time;
097             if( mTimeleft < 0 )
098             {
099                 // finish blending
100                 mSource->setEnabled(false);
101                 mSource->setWeight(0);
102                 mSource = mTarget;
103                 mSource->setEnabled(true);
104                 mSource->setWeight(1);
105                 mTarget = 0;
106             }
107             else
108             {
109                 // still blending, advance weights
110                 mSource->setWeight(mTimeleft / mDuration);
111                 mTarget->setWeight(1.0 - mTimeleft / mDuration);
112                 if(mTransition == AnimationBlender::BlendWhileAnimating)
113                     mTarget->addTime(time);
114             }
115         }
116         if (mSource->getTimePosition() >= mSource->getLength())
117         {
118             complete = true;
119         }
120         else
121         {
122             complete = false;
123         }
124         mSource->addTime(time);
125         mSource->setLoop(loop);
126     }
127 }
128 AnimationBlender::AnimationBlender( Entity *entity ) : mEntity(entity) 
129 {
130 }

AnimationBlenderDemo.h

01 <P>#ifndef __ANIMATIONBLENDER_DEMO_H__
02 #define __ANIMATIONBLENDER_DEMO_H__
03 #include "ExampleApplication.h"
04 #include "AnimationBlender.h"
05 class AnimationBlenderDemoFrameListener : public ExampleFrameListener
06 {
07 protected:
08     Entity* mNinjaEnt;
09     AnimationBlender* mAnimationBlender;
10     bool walking, jumping;
11 public:
12     AnimationBlenderDemoFrameListener(RenderWindow* mWin, Camera* mCam, Entity* ent)
13         : ExampleFrameListener(mWin, mCam, falsefalse), mNinjaEnt(ent)
14     {
15         mAnimationBlender = new AnimationBlender(mNinjaEnt);
16         mAnimationBlender->init("Walk"true);
17         walking = false;
18         jumping = false;
19     }
20     virtual ~AnimationBlenderDemoFrameListener()
21     {
22         if(mAnimationBlender)
23         {
24 //          delete mAnimationBlender;
25         }
26     }
27     bool frameRenderingQueued(const FrameEvent& evt)
28     {
29         if (!ExampleFrameListener::frameRenderingQueued(evt))
30         {
31             return false;
32         }
33         if (!walking)
34         {
35             mAnimationBlender->blend( "Walk",AnimationBlender::BlendWhileAnimating, 0.2, true );
36             walking=true;
37         }
38         if (mKeyboard->isKeyDown( OIS::KC_SPACE ) && !jumping)
39         {
40             jumping=true;
41             mAnimationBlender->blend("Jump",AnimationBlender::BlendWhileAnimating, 0.2, false );
42         }
43         if (jumping)
44         {
45             if (mAnimationBlender->complete)
46             {
47                 mAnimationBlender->blend( "Idle1",AnimationBlender::BlendWhileAnimating, 0.02, true );
48                 jumping=false;
49             }
50         }
51         mAnimationBlender->addTime(evt.timeSinceLastFrame);
52         return true;
53     }
54 };
55 class AnimationBlenderDemoApp : public ExampleApplication
56 {
57 public:
58     AnimationBlenderDemoApp() {}
59 protected:
60     Entity* mNinjaEnt;
61     void createScene();
62     void createFrameListener()
63     {
64         mFrameListener = new AnimationBlenderDemoFrameListener(mWindow, mCamera, mNinjaEnt);
65         mRoot->addFrameListener(mFrameListener);
66     }
67 };
68 #endif</P>

AnimationBlenderDemo.cpp

01 <P>#include "AnimationBlenderDemo.h"
02 #include <OgreStringConverter.h>
03 void AnimationBlenderDemoApp::createScene()
04 {
05 mSceneMgr->setAmbientLight(ColourValue(1.0, 1.0, 1.0));
06 mNinjaEnt = mSceneMgr->createEntity("Ninja""ninja.mesh");
07 SceneNode* node = mSceneMgr->getRootSceneNode()->createChildSceneNode();
08 node->attachObject(mNinjaEnt);
09 }
10 #ifdef __cplusplus
11 extern "C" {
12 #endif
13 #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
14 #define WIN32_LEAN_AND_MEAN
15 #include "windows.h"
16 INT WINAPI WinMain( HINSTANCE hInst, HINSTANCELPSTR strCmdLine, INT )
17 #else
18 int main(int argc, char **argv)
19 #endif
20 {
21 AnimationBlenderDemoApp app;
22 try {
23     app.go();
24 catch( Ogre::Exception& e ) {
25 #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
26     MessageBox( NULL, e.getFullDescription().c_str(), "An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL );
27 #else
28     std::cerr << "An exception has occured: " << e.getFullDescription();
29 #endif
30 }
31 return 0;
32 }
33 #ifdef __cplusplus
34 }
35 #endif</P>

不过由于Ninja.mesh的动画时间太小, 很难看出混合效果,  以上代码适合Ogre1.6.5

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值