现在的位置: 首页 > 综合 > 正文

Ogre中的动画混合(AnimationBlender)

2013年08月19日 ⁄ 综合 ⁄ 共 6818字 ⁄ 字号 评论关闭

 网上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,代码就自动整理好了。

抱歉!评论已关闭.