轉自
http://www.guibian.com/article.asp?id=50很多商業引擎都支持骨骼動畫融合這個功能。
融合可以讓你的動畫自然過渡到下一個動畫,你可以精確控制下一個動畫播放時間等信息。
Ogre其實也支持動畫的融合,只是Ogre并沒有給出代碼級的支持。
PS:Ogre很有意思,他天然支持很多東西,但是都沒有實現出來,需要你來實現。
好的,下面來通過代碼看看如果進行一個簡單的骨骼動畫融合。

程序代碼
class AnimationBlender
{
public:
enum BlendingTransition //不同的混合方式
{
BlendSwitch, // 直接切換到目標動畫
BlendWhileAnimating, // 交叉淡入淡出(源動畫比例縮小,同時目標動畫比例增大)
BlendThenAnimate // 淡出源動畫到目標動畫第一幀,然后開始目標動畫
};
private:
Entity *mEntity;
AnimationState *mSource;
AnimationState *mTarget;
BlendingTransition mTransition;
bool loop; //是否循環
~AnimationBlender() {}
public:
Real mTimeleft, mDuration; //持續時間
bool complete;
void blend( const String &animation, BlendingTransition transition, Real duration, bool l );
void addTime( Real );
Real getProgress() { return mTimeleft/ mDuration; }
AnimationState *getSource() { return mSource; }
AnimationState *getTarget() { return mTarget; }
AnimationBlender( Entity *);
void init( const String &animation );
};
實現的代碼

程序代碼
void AnimationBlender::init(const String &animation)
{
//初始化所有動作的AnimationState
AnimationStateSet *set = mEntity->getAllAnimationStates();
AnimationStateIterator it = set->getAnimationStateIterator();
while(it.hasMoreElements())
{
AnimationState *anim = it.getNext();
anim->setEnabled(false);
anim->setWeight(0);
anim->setTimePosition(0);
}
//初始化mSource
mSource = mEntity->getAnimationState( animation );
mSource->setEnabled(true);
mSource->setWeight(1);
mTimeleft = 0;
mDuration = 1;
mTarget = 0;
complete=false;
}
void AnimationBlender::blend( const String &animation, BlendingTransition transition, Real duration, bool l )
{
loop=l; //設置是否需要循環
if( transition == AnimationBlender::BlendSwitch )
{//如果混合方式為直接切換,改變mSource 即可
if( mSource != 0 )
mSource->setEnabled(false);
mSource = mEntity->getAnimationState( animation );
mSource->setEnabled(true);
mSource->setWeight(1);
mSource->setTimePosition(0);
mTimeleft = 0;
}
else
{
//先取得新的動畫狀態
AnimationState *newTarget = mEntity->getAnimationState( animation );
if( mTimeleft > 0 ) //前一次的混合尚未結束
{
if( newTarget == mTarget )
{
// 新的目標就是正在混合中的目標,什么也不做
}
else if( newTarget == mSource )
{
// 新的目標是源動畫,直接go back
mSource = mTarget;
mTarget = newTarget;
mTimeleft = mDuration - mTimeleft;
}
else
{
// 現在newTarget是真的新的動畫了
if( mTimeleft < mDuration * 0.5 ) //上一次的混合進度還未超過一半
{
// 簡單替換Target就行了
mTarget->setEnabled(false);
mTarget->setWeight(0);
}
else //如果已經過半,舊的target成為新的source
{
mSource->setEnabled(false);
mSource->setWeight(0);
mSource = mTarget;
}
mTarget = newTarget;
mTarget->setEnabled(true);
mTarget->setWeight( 1.0 - mTimeleft / mDuration );
mTarget->setTimePosition(0);
}
}
else //上次的混合已經結束,當前未處于混合狀態中
{
mTransition = transition;
mTimeleft = mDuration = duration;
mTarget = newTarget;
mTarget->setEnabled(true);
mTarget->setWeight(0);
mTarget->setTimePosition(0);
}
}
}
void AnimationBlender::addTime( Real time )
{
if( mSource != 0 ) //若無AnimationState則不進行操作
{
if( mTimeleft > 0 ) //兩個動畫仍在混合過程中
{
mTimeleft -= time;
if( mTimeleft < 0 )
{
// 混合完畢,切換到目標動畫
mSource->setEnabled(false);
mSource->setWeight(0);
mSource = mTarget;
mSource->setEnabled(true);
mSource->setWeight(1);
mTarget = 0;
}
else
{
// 仍然處于混合狀態中,改變兩個動畫的權值
mSource->setWeight(mTimeleft / mDuration);
mTarget->setWeight(1.0 - mTimeleft / mDuration);
//在這種混合方式下,需要為目標動畫增加時間
if(mTransition == AnimationBlender::BlendWhileAnimating)
mTarget->addTime(time);
}
}
if (mSource->getTimePosition() >= mSource->getLength())
{
complete=true;
}
else
{
complete=false;
}
mSource->addTime(time);
mSource->setLoop(loop);
}
}
AnimationBlender::AnimationBlender( Entity *entity ) : mEntity(entity){}
哈哈,相信你已經看出來,這段代碼有很多很多的不足。比如只有骨骼動畫的融合,
而且還是寫死的,必須0.5。
我現在已經擴展了這些功能,包括做到了骨骼動畫與定點動畫的融合,變形動畫的融合等等。而且可以精確控制融合的時機。你有什么好想法也歡迎與我討論