英文原文: 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__ |
08 | enum BlendingTransition |
16 | AnimationState *mSource; |
17 | AnimationState *mTarget; |
18 | BlendingTransition mTransition; |
20 | ~AnimationBlender() {} |
22 | Real mTimeleft, mDuration; |
24 | void blend( const String &animation, BlendingTransition transition, Real duration, bool l= true ); |
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 ); |
AnimationBlender.cpp
001 | #include "AnimationBlender.h" |
002 | void AnimationBlender::init( const String &animation, bool l) |
005 | AnimationStateSet *set = mEntity->getAllAnimationStates(); |
006 | AnimationStateIterator it = set->getAnimationStateIterator(); |
007 | while (it.hasMoreElements()) |
009 | AnimationState *anim = it.getNext(); |
010 | anim->setEnabled( false ); |
012 | anim->setTimePosition(0); |
014 | mSource = mEntity->getAnimationState( animation ); |
015 | mSource->setEnabled( true ); |
016 | mSource->setWeight(1); |
023 | void AnimationBlender::blend( const String &animation, BlendingTransition transition, Real duration, bool l ) |
026 | if ( transition == AnimationBlender::BlendSwitch ) |
029 | mSource->setEnabled( false ); |
030 | mSource = mEntity->getAnimationState( animation ); |
031 | mSource->setEnabled( true ); |
032 | mSource->setWeight(1); |
033 | mSource->setTimePosition(0); |
038 | AnimationState *newTarget = mEntity->getAnimationState( animation ); |
042 | if ( newTarget == mTarget ) |
046 | else if ( newTarget == mSource ) |
051 | mTimeleft = mDuration - mTimeleft; |
057 | if ( mTimeleft < mDuration * 0.5 ) |
060 | mTarget->setEnabled( false ); |
061 | mTarget->setWeight(0); |
066 | mSource->setEnabled( false ); |
067 | mSource->setWeight(0); |
071 | mTarget->setEnabled( true ); |
072 | mTarget->setWeight( 1.0 - mTimeleft / mDuration ); |
073 | mTarget->setTimePosition(0); |
081 | mTransition = transition; |
082 | mTimeleft = mDuration = duration; |
084 | mTarget->setEnabled( true ); |
085 | mTarget->setWeight(0); |
086 | mTarget->setTimePosition(0); |
090 | void AnimationBlender::addTime( Real time ) |
100 | mSource->setEnabled( false ); |
101 | mSource->setWeight(0); |
103 | mSource->setEnabled( true ); |
104 | mSource->setWeight(1); |
110 | mSource->setWeight(mTimeleft / mDuration); |
111 | mTarget->setWeight(1.0 - mTimeleft / mDuration); |
112 | if (mTransition == AnimationBlender::BlendWhileAnimating) |
113 | mTarget->addTime( time ); |
116 | if (mSource->getTimePosition() >= mSource->getLength()) |
124 | mSource->addTime( time ); |
125 | mSource->setLoop(loop); |
128 | AnimationBlender::AnimationBlender( Entity *entity ) : mEntity(entity) |
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 |
09 | AnimationBlender* mAnimationBlender; |
10 | bool walking, jumping; |
12 | AnimationBlenderDemoFrameListener(RenderWindow* mWin, Camera* mCam, Entity* ent) |
13 | : ExampleFrameListener(mWin, mCam, false , false ), mNinjaEnt(ent) |
15 | mAnimationBlender = new AnimationBlender(mNinjaEnt); |
16 | mAnimationBlender->init( "Walk" , true ); |
20 | virtual ~AnimationBlenderDemoFrameListener() |
27 | bool frameRenderingQueued( const FrameEvent& evt) |
29 | if (!ExampleFrameListener::frameRenderingQueued(evt)) |
35 | mAnimationBlender->blend( "Walk" ,AnimationBlender::BlendWhileAnimating, 0.2, true ); |
38 | if (mKeyboard->isKeyDown( OIS::KC_SPACE ) && !jumping) |
41 | mAnimationBlender->blend( "Jump" ,AnimationBlender::BlendWhileAnimating, 0.2, false ); |
45 | if (mAnimationBlender->complete) |
47 | mAnimationBlender->blend( "Idle1" ,AnimationBlender::BlendWhileAnimating, 0.02, true ); |
51 | mAnimationBlender->addTime(evt.timeSinceLastFrame); |
55 | class AnimationBlenderDemoApp : public ExampleApplication |
58 | AnimationBlenderDemoApp() {} |
62 | void createFrameListener() |
64 | mFrameListener = new AnimationBlenderDemoFrameListener(mWindow, mCamera, mNinjaEnt); |
65 | mRoot->addFrameListener(mFrameListener); |
AnimationBlenderDemo.cpp
01 | <P>#include "AnimationBlenderDemo.h" |
02 | #include <OgreStringConverter.h> |
03 | void AnimationBlenderDemoApp::createScene() |
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); |
13 | #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32 |
14 | #define WIN32_LEAN_AND_MEAN |
16 | INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE , LPSTR strCmdLine, INT ) |
18 | int main( int argc, char **argv) |
21 | AnimationBlenderDemoApp app; |
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 ); |
28 | std::cerr << "An exception has occured: " << e.getFullDescription(); |
不过由于Ninja.mesh的动画时间太小, 很难看出混合效果, 以上代码适合Ogre1.6.5