网上Ogre的中文资料真的是很少啊,把自己学到的分享出来,希望能对初学者有所帮助。
Ogre中实现骨骼动画真的是再简单不过了,就两个函数:getAnimationState,addTime。OK,人物就可以动起来了。这个AnimationBlender类可以实现以多种方式从一个动画淡入到另一个动画,来自于Ogre网站上的一篇文章。但那位老外只贴出了代码,没有讲解,甚至代码中也没有详尽的注释,像我这样的菜鸟就只能慢慢品味了。
下面是AnimationBlender类的定义:
#pragma once
#ifndef AnimationBlender_Incl
#define AnimationBlender_Incl
#include <Ogre.h>
using namespace 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 );
};
#endif
这里定义了几种混合方式,所产生的不同效果运行后就能看到了。构造函数接受一个Entity的指针,以后我们就可以操作这个Entity中的Animation了。
AnimationBlender类的实现:
#include "AnimationBlender.h"
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){}
来试着用一下这个AnimationBlender类
先声明一个全局的blender实例
AnimationBlender * blender;
在createScene中我们来初始化它
//实例化一个blender并将Entity传入
blender=new AnimationBlender(ninjaHead);
//设置一个初始动作
blender->init("Idle1");
在frameStarted中为blender加时间
blender->addTime(evt.timeSinceLastFrame);
现在ninja已经可以动起来了。
然后我们用键盘来控制动画:
在FrameListener中覆盖父类的键盘输入函数
virtual bool processUnbufferedKeyInput(const FrameEvent& evt)
{
if (mInputDevice->isKeyDown(KC_T))
{
blender->blend("Attack1",AnimationBlender::BlendWhileAnimating,1.0,false);
}
if (mInputDevice->isKeyDown(KC_J))
{
blender->blend("Jump",AnimationBlender::BlendWhileAnimating,1.0,false);
}
if (mInputDevice->isKeyDown(KC_I))
{
blender->blend("Idle1",AnimationBlender::BlendWhileAnimating,1.0,false);
}
//记得调用父类的键盘处理,以使WASD仍然可用
return ExampleFrameListener::processUnbufferedKeyInput(evt);
}
可以看到效果了,你还可以试一下别的混合方式。
完整代码如下:
#ifndef __AnimBlender_h_
#define __AnimBlender_h_
#include "ExampleApplication.h"
#include "../AnimationBlender.h"
AnimationBlender * blender;
class AnimBlenderFrameListener : public ExampleFrameListener
{
private:
SceneManager* mSceneMgr;
public:
AnimBlenderFrameListener(SceneManager *sceneMgr, RenderWindow* win, Camera* cam)
: ExampleFrameListener(win, cam),
mSceneMgr(sceneMgr)
{
}
bool frameStarted(const FrameEvent& evt)
{
bool ret = ExampleFrameListener::frameStarted(evt);
blender->addTime(evt.timeSinceLastFrame);
return ret;
}
//覆盖父类的键盘输入函数
virtual bool processUnbufferedKeyInput(const FrameEvent& evt)
{
if (mInputDevice->isKeyDown(KC_T))
{
blender->blend("Attack1",AnimationBlender::BlendWhileAnimating,1.0,false);
}
if (mInputDevice->isKeyDown(KC_J))
{
blender->blend("Jump",AnimationBlender::BlendWhileAnimating,1.0,false);
}
if (mInputDevice->isKeyDown(KC_I))
{
blender->blend("Idle1",AnimationBlender::BlendWhileAnimating,1.0,false);
}
//记得调用父类的键盘处理,以使WASD仍然可用
return ExampleFrameListener::processUnbufferedKeyInput(evt);
}
};
class AnimBlenderApp : public ExampleApplication
{
public:
AnimBlenderApp()
{}
~AnimBlenderApp()
{
}
protected:
virtual void createCamera(void)
{
// Create the camera
mCamera = mSceneMgr->createCamera("PlayerCam");
// Position it at 500 in Z direction
mCamera->setPosition(Vector3(0,0,180));
// Look back along -Z
mCamera->lookAt(Vector3(0,0,-300));
mCamera->setNearClipDistance(5);
}
// Just override the mandatory create scene method
virtual void createScene(void)
{
Entity* ninjaHead = mSceneMgr->createEntity("ninja", "ninja.mesh");
SceneNode* ninjaNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
ninjaNode->attachObject(ninjaHead);
// Set ambient light
mSceneMgr->setAmbientLight(ColourValue(0.5, 0.5, 0.5));
// Create a light
Light* l = mSceneMgr->createLight("MainLight");
l->setPosition(20,80,50);
//实例化一个blender并将Entity传入
blender=new AnimationBlender(ninjaHead);
//设置一个初始动作
blender->init("Idle1");
}
// Create new frame listener
void createFrameListener(void)
{
mFrameListener= new AnimBlenderFrameListener(mSceneMgr, mWindow, mCamera);
mRoot->addFrameListener(mFrameListener);
}
};
#endif // #ifndef __AnimBlender_h_
csdn竟然不支持插入C++代码,导致代码贴得很乱,气愤ing……。如果想直接拷贝代码,粘贴到VS中后,记得按下ALT+F8,代码就自动整理好了。