1、ogre中级教程Ogre中级教程(一): 动画, 两点间移动, 和四元数基础 分类: Ogre 2010-03-27 23:48 968人阅读 评论(1) 收藏 举报 Ogre中级教程中级教程一 来自 Ogre wiki 动画, 两点间移动, 和四元数基础 作者: Culver. 内容 目录 隐藏 1 介绍 2 前期准备 3 准备开始 4 设置场景 5 动画 6 移动角色 7 巩固练习 7.1 简单问题 7.2 中级问题 7.3 困难问题 7.4 专家问题 介绍 这个教程里包括怎么样得到一个模型,并添加模型动画,最后让模型可以在两个预先定义的点之间走动。在此将讲述如何用基本的四元数方法保持模型
2、移动的时候正面一直朝着我们指定的方向。你必须一点点的将代码加入到你的项目中,并在每次加入新代码后编译并察看demo运行的结果。 本课的最终代码在这里。 前期准备 首先,这个指南假设你已经知道如何设置Ogre的项目环境以及如何正确编译项目。该例子同样使用STL 中的queue数据结构。那么预先了解如何使用queue是必要的,至少你需要知道什么是模版。如果你不熟悉STL,那么我像你推荐STL参考ISBN 0596005563,它可以帮助你在将来花费更少的时间。 准备开始 首先,你需要为这个Demo创建一个新项目,在项目中添加一个名为MoveDemo.cpp的文件并加入如下代码: #include
3、ExampleApplication.h#include using namespace std;class MoveDemoListener : public ExampleFrameListenerpublic: MoveDemoListener(RenderWindow* win, Camera* cam, SceneNode *sn, Entity *ent, deque &walk) : ExampleFrameListener(win, cam, false, false), mNode(sn), mEntity(ent), mWalkList( walk ) / MoveDemo
4、Listener /* This function is called to start the object moving to the next position in mWalkList. */ bool nextLocation( ) return true; / nextLocation( ) bool frameStarted(const FrameEvent &evt) return ExampleFrameListener:frameStarted(evt); protected: Real mDistance; / The distance the object has le
5、ft to travel Vector3 mDirection; / The direction the object is moving Vector3 mDestination; / The destination the object is moving towards AnimationState *mAnimationState; / The current animation state of the object Entity *mEntity; / The Entity we are animating SceneNode *mNode; / The SceneNode tha
6、t the Entity is attached to std:deque mWalkList; / The list of points we are walking to Real mWalkSpeed; / The speed at which the object is moving;class MoveDemoApplication : public ExampleApplicationprotected:public: MoveDemoApplication() MoveDemoApplication() protected: Entity *mEntity; / The enti
7、ty of the object we are animating SceneNode *mNode; / The SceneNode of the object we are moving std:deque mWalkList; / A deque containing the waypoints void createScene(void) void createFrameListener(void) mFrameListener= new MoveDemoListener(mWindow, mCamera, mNode, mEntity, mWalkList); mFrameListe
8、ner-showDebugOverlay(true); mRoot-addFrameListener(mFrameListener); ;#if OGRE_PLATFORM = OGRE_PLATFORM_WIN32#define WIN32_LEAN_AND_MEAN#include windows.hINT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )#elseint main(int argc, char *argv)#endif / Create application object MoveDe
9、moApplication app; try app.go(); catch( Exception& e ) #if OGRE_PLATFORM = OGRE_PLATFORM_WIN32 MessageBox( NULL, e.getFullDescription().c_str(), An exception has occured!, MB_OK | MB_ICONERROR | MB_TASKMODAL);#else fprintf(stderr, An exception has occured: %s/n, e.getFullDescription().c_str();#endif
10、 return 0;在我们继续讲解之前,你可以编译这部分代码看下效果。 设置场景 在我们开始之前,需要注意的是已经在MoveDemoApplication中预先定义的三个变量。我们创建的entity实例保存在变量mEntity中,我们创建的node实例保存在mNode中,另外mWalkList包含了所有我们希望对象行走到的节点。 定位到MoveDemoApplication:createScene函数并且加入以下代码。首先,我们来设置环境光(ambient light)到最大,这样可以让我们看到我们放在场景中的所有对象。 / Set the default lighting. mSceneMg
11、r-setAmbientLight( ColourValue( 1.0f, 1.0f, 1.0f ) );接下来我们来在屏幕上创建一个可以使用的机器人。要做到这点我们需要在创建SceneNode之前先为机器人创建一个entity使得我们可以对其进行旋转。 / Create the entity mEntity = mSceneMgr-createEntity( Robot, robot.mesh ); / Create the scene node mNode = mSceneMgr-getRootSceneNode( )- createChildSceneNode( RobotNode, V
12、ector3( 0.0f, 0.0f, 25.0f ) ); mNode-attachObject( mEntity );以上这些都是非常基础的,所以我认为不需要再对以上的描述做任何解释。在接下来的代码片断,我们将开始告诉机器人那些地方是它需要到达的。这里需要你们了解一些STL的知识,deque对象是一个高效的双端对列。我们只需要使用它的几个简单的方法。push_front和push_back方法分别将对象放入队列的前端和后端,front和back方法分别返回当前队列前端和后端的元素(PS:注意,这里最好有判空的习惯,用if( empty() ) )pop_front和pop_back两个方法
13、分别从队列两端移除对象。最后,empty方法返回该队列是否为空。下面这些代码添加了两个Vector到队列中,在后面我们移动robot的时候会用到它们。 / Create the walking list mWalkList.push_back( Vector3( 550.0f, 0.0f, 50.0f ) ); mWalkList.push_back( Vector3(-100.0f, 0.0f, -200.0f ) );接下来,我们在场景里放置一些物体,以标记这个机器人应该朝哪走去。这样使我们能看见机器人在场景里相对于其它物体进行移动。注意它们的位置的负Y部分,这些物体被放在机器人移动目的地
14、的正下方,当它到达指定地点时,它就站在这些物体上面。 / Create objects so we can see movement Entity *ent; SceneNode *node; ent = mSceneMgr-createEntity( Knot1, knot.mesh ); node = mSceneMgr-getRootSceneNode( )-createChildSceneNode( Knot1Node, Vector3( 0.0f, -10.0f, 25.0f ) ); node-attachObject( ent ); node-setScale( 0.1f, 0.
15、1f, 0.1f ); ent = mSceneMgr-createEntity( Knot2, knot.mesh ); node = mSceneMgr-getRootSceneNode( )-createChildSceneNode( Knot2Node, Vector3( 550.0f, -10.0f, 50.0f ) ); node-attachObject( ent ); node-setScale( 0.1f, 0.1f, 0.1f ); ent = mSceneMgr-createEntity( Knot3, knot.mesh ); node = mSceneMgr-getR
16、ootSceneNode( )-createChildSceneNode( Knot3Node, Vector3(-100.0f, -10.0f,-200.0f ) ); node-attachObject( ent ); node-setScale( 0.1f, 0.1f, 0.1f );最后,我们要创建一个摄像机从适合的角度来观察它。我们来把摄像机移动到更多的位置。 / Set the camera to look at our handywork mCamera-setPosition( 90.0f, 280.0f, 535.0f ); mCamera-pitch( Degree(-30
17、.0f) ); mCamera-yaw( Degree(-15.0f) );现在编译并运行代码。你应该能看到这个样子: 1 在进入下一个部分之前,注意一下MoveDemoListener的构造器,它在MoveDemoApplication:createFrameListener方法里的第一行被调用。除了传入BaseFrameListener的标准参数,还有场景节点、实体、双端队列。 动画 现在我们来设置一些基本的动画。在Ogre里动画是非常简单的。要做的话,你需要从实体对象里获取AnimationState,设置它的选项,并激活它。这样就能使动画活动起来,但你还必须在每一帧后给它添加时间,才能
18、让动画动起来。我们设置成每次移动一步。首先,找到MoveDemoListener的构造器,并添加以下代码: / Set idle animation mAnimationState = ent-getAnimationState(Idle); mAnimationState-setLoop(true); mAnimationState-setEnabled(true);第二行从实体中获取到了AnimationState。第三行我们调用setLoop( true ),让动画不停地循环。而在一些动画里(比如死亡动画),我们可能要把这个设置为false。第四行才把这个动画真正激活。但等等.我们从哪里
19、获取的“Idle”?这个魔术般的常量是怎么飞到这里来的?每个mesh都有它们自己定义的动画集。为了能够查看某个mesh的全部动画,你需要下载OgreMeshViewer才能看到。 现在,如果我们编译并运行这个demo,我们看见了.nothing! 这是因为我们还需要在每一帧里根据时间来更新这个动画的状态。找到MoveDemoListener:frameStarted方法,在方法的开头添加这一行: mAnimationState-addTime(evt.timeSinceLastFrame);现在来编译并运行程序。你应该可以看了一个机器人正在原地踏步了。 移动角色 现在我们执行棘手的任务,开始让
20、这个机器人从一点走到另一点。在我们开始之前,我想介绍一下保存在MoveDemoListener类里的成员变量。我们将使用4个变量来完成移动机器人的任务。首先,我们把机器人移动的方向保存到mDirection里面。我们再把当前机器人前往的目的地保存在mDestination里。然后在mDistance保存机器人离目的地的距离。最后,在mWalkSpeed里我们保存机器人的移动速度。 首先清空MoveDemoListener构造器,我们会用稍微不同的代码来替换。我们要做的第一件事是设置这个类的变量。我们将把行走速度设为每秒35个单位。有一个大问题要注意,我们故意把mDirection设成零向量,因
21、为后面我们会用它来判断机器人是否正在行走。 / Set default values for variables mWalkSpeed = 35.0f; mDirection = Vector3:ZERO;好了,搞定了。我们要让机器人动起来。为了让机器人移动,我们只须告诉它改变动画。然而,我们只想要若存在另一个要移动到的地点,就让机器人开始移动。为了这个目的,我们调用nextLocation 函数。把代码加到MoveDemoListener:frameStarted方法的顶部,在调用AnimationState:addTime之前: if (mDirection = Vector3:ZERO)
22、 if (nextLocation() / Set walking animation mAnimationState = mEntity-getAnimationState(Walk); mAnimationState-setLoop(true); mAnimationState-setEnabled(true); 如果你现在编译并运行,这个机器人将原地行走。这是由于机器人是以ZERO方向出发的,而我们的MoveDemoListener:nextLocation函数总是返回true。在后面的步骤中,我们将给MoveDemoListener:nextLocation函数添加更多的一点智能。 现
23、在,我们准备要真正在场景里移动机器人了。为了这样做,我们需要在每一帧里让我移动一点点。找到MoveDemoListener:frameStarted方法,我们将在调用AnimationState:addTime之前,我们先前的if语句之后,添加以下代码。这段代码将处理当机器人实际移动的情况;mDirection != Vector3:ZERO。 else Real move = mWalkSpeed * evt.timeSinceLastFrame; mDistance -= move;现在,我们要检测一下我们是否“走过”了目标地点。即,如果现在mDistance小于0,我们需要“跳”到这点上
24、,并设置移动到下一个地点。注意,我们把mDirection设置成零向量。如果nextLocation方法不改变mDirection(即没有其它地方可去),我们就不再四处移动了。 if (mDistance setPosition(mDestination); mDirection = Vector3:ZERO;现在我们移动到了这个点,我们需要设置运动到下一个点。只要我们知道有否需要移动到下一个地点,我们就能设置正确的动画;如果有其它地点要去,就行走。如果没有其它目的地,则停滞。 / Set animation based on if the robot has another point to
25、 walk to. if (! nextLocation() / Set Idle animation mAnimationState = mEntity-getAnimationState(Idle); mAnimationState-setLoop(true); mAnimationState-setEnabled(true); else / Rotation Code will go here later 注意,如果queue里已经没有更多的地点要走的话,我们没有必要再次设置行走动画。既然机器人已经在行走了,没有必要再告诉他这么做。然而,如果机器人还要走向另一个地点,我们就要把它旋转以面
26、对那个地点。现在,我们在else括号旁边留下注释占位符;记住这个地点,因为我们后面还要回来。 这里考虑的是当我们离目标地点很近的时候。现在我们需要处理一般情况,当我们正在到达而没有到达的时候。为此,我们在机器人的行走方向上对它进行平移,用move变量指定的值。通过添加以下代码来实现: else mNode-translate(mDirection * move); / else / if我们差不多做完了,除了还要设置运动需要的变量。如果我们正确地设置了运动变量,我们的机器人就会朝它该去的方向行走。看看MoveDemoListener:nextLocation方法,如果我们用完了所有的地点,它返
27、回false。这是函数的第一行。(注意你要保留函数底部的return true语句) if (mWalkList.empty() return false;现在我们来设置变量。首先我们从双端队列里取出一个向量。通过目标向量减去场景节点的当前向量,我们得取方向向量。然而我们仍有一个问题,还记得我们要在frameStarted方法里用mDirection乘以移动量吗?如果我们这么做,我们必须把方向向量转换成单位向量(即,它的长度等于一)。normalise函数为我们做了这些事,并返回向量的原始长度。唾手可得,我们需要设置到目的地的距离。 mDestination = mWalkList.front()
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1