随笔-378  评论-37  文章-0  trackbacks-0
OgreOde创建一个会走动的角色

原文http://www.game798.com/html/2007-12/5156.htm

第一版 by SuperMegaMau

这个教程包括的代码和算法是作者自己的经验,也许不正确或不怎么准确,如果发现问题请纠正。

 

内容

  • 1 介绍
  • 2 创建物理模型  
    • 2.1 创建角色  
    • 2.2 获取 AABB
    • 2.3 创建新空间  
    • 2.4 创建球  
    • 2.5 创建椭球  
    • 2.6 创建关节  
  • 3 移动角色  
    • 3.1 前后移动
    • 3.2 左右旋转  
  • 4 让角色爬起来
  • 5 问题

介绍

我相信我不是第一人自问如何用OgreOde创建一个运动角色。搜索论坛和wiki后,我意识到这是一个很有用的信息。这个教程解释了如何创建一个可以在地形上行走的运动角色(包括其它meshes,如树和房屋)。

创建物理模型

我按照在Monster的方法用下图代表一个角色:

下面,我假设你对OgreSceneNodes, meshes AlignedBoxes都有所了解,并且会用SceneManager创建地形。

创建角色

首先创建一个SceneNode来放角色的mesh,在这个例子中我用了Ogre例子中的忍者模型。创建两个SceneNode并把它们连在一起。后面我会解释为什么是两个Node

Entity* ninja = mSceneMgr->createEntity("ninja","ninja.mesh");

SceneNode* ninjaNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("ninja");

SceneNode* modelNode = ninjaNode->createChildSceneNode("ninja_model");

modelNode->attachObject(ninja);

ninjaNode->setScale(0.05,0.05,0.05);

RootSceneNode

ninjaNode

modelNode

也许你已经注意到,ninjaNode被缩放得很小,这是因为如果mesh很大的话渲染的速度就变很慢(不知道为什么)。

获取 AABB(AxisAlignedBox, 轴对齐包围盒)

现在用AxisAlignedBox获取mesh的大小。

AxisAlignedBox aab = modelNode->getAttachedObject("ninja")->getBoundingBox();

Ogre::Vector3 min = aab.getMinimum()*ninjaNode->getScale();

Ogre::Vector3 max = aab.getMaximum()*ninjaNode->getScale();

Ogre::Vector3 center = aab.getCenter()*ninjaNode->getScale();

Ogre::Vector3 size(fabs(max.x-min.x),fabs(max.y-min.y),fabs(max.z-min.z));

float radius = (size.x>size.z)?size.z/2.0f:size.x/2.0f;

 

创建一个新空间

我们需要创建一个新空间把角色放在其中,并且取消内部碰撞检测。

OgreOde::SimpleSpace* dollSpace = new OgreOde::SimpleSpace(_world->getDefaultSpace());

dollSpace->setInternalCollisions(false);

创建球体(腿部)

现在有了两个SceneNodes "ninjaNode" "modelNode"ninjaNode是代表你角色的节点,modelNode是你真正贴mesh的地方。这么做是因为mesh的中心总是在OgreOde::Body的中心,所以我们用ninjaNode来创建碰撞体的位置,然后根据ninjaNodeOgreOde::Body来获得mesh的正确位置。

左边图是用一个SceneNode所得到的效果,右边是用两个SceneNode。你可以注意到,左边的角色悬浮在半空中。下面代码创建了一个球体代表角色的脚部。我们需要一个SphereGeometry和一个TransformGeometry 将球体放到正确位置。

译注:注释为我的猜想,具体不知道步骤这么复杂,欢迎纠正。

(1)        创建一个碰撞体,命名为feet

(2)        设置碰撞体为球形物体,半径为AABB获得的半径

(3)        SphereGeometry,半径为AABB获得的半径

(4)        TransformGeometry,空间为刚才创建的空间, TransformGeometry似乎是为了包含某特定形状的几何体

(5)        改变modelNode相对于ninjiaNode的位置,以便让脚占到地上

(6)        TransformGeometry包含一个OgreOde::Body和一个几何体

(7)        Ogre::Body粘到ninjaNode

怀疑创建SphereGeometry是否只是让Ogre::Body具象化

 

OgreOde::Body* dollFeetBody = new OgreOde::Body("feet");  

dollFeetBody->setMass(OgreOde::SphereMass(70*2.5,radius));

OgreOde::SphereGeometry* feetGeom = new OgreOde::SphereGeometry(radius);

OgreOde::TransformGeometry* feetTrans = new OgreOde::TransformGeometry(dollSpace);

modelNode->translate(Vector3(0,-radius/ninjaNode->getScale().y,0));

feetTrans->setBody(dollFeetBody);

feetTrans->setEncapsulatedGeometry(feetGeom);

ninjaNode->attachObject(dollFeetBody);

创建椭球体

对于角色的上半身用一个椭球体来表示。

译注:和上面一样。

(1)        创建Ogre::Body

(2)        设置Ogre::Body形状,另外设置不被重力影响,

(3)        创建TransformGeometry,空间为刚才创建的空间

(4)        创建CapsuleGeometry,半径为AABB获得半径

(5)        设置CapsuleGeometry位置和方向和阻尼

(6)        TransformGeometry包含Ogre::body

(7)        TransformGeometry包含CapsuleGeometry

(8)        Ogre::Body粘到ninjiaNode

OgreOde::Body* dollTorsoBody = new OgreOde::Body("torso");

dollTorsoBody->setMass(OgreOde::CapsuleMass(70*2.5,radius,Vector3::UNIT_Y,radius));

dollTorsoBody->setAffectedByGravity(false);

dollTorsoBody->setDamping(0,50000);

OgreOde::TransformGeometry* torsoTrans = new OgreOde::TransformGeometry(dollSpace);

OgreOde::CapsuleGeometry* torsoGeom = new OgreOde::CapsuleGeometry(radius,size.y-4*radius,dollSpace);

torsoGeom->setPosition(Ogre::Vector3(0,size.y-((size.y-4*radius)/2+2*radius),0)); //can't find a good way to explain this

torsoGeom->setOrientation(Quaternion(Degree(90),Vector3::UNIT_X));

torsoTrans->setBody(dollTorsoBody);

torsoTrans->setEncapsulatedGeometry(torsoGeom);

ninjaNode->attachObject(dollTorsoBody);

这个几何体和脚的几何体在同一个空间,所以我们要取消内部碰撞检测。讲阻尼设置高些,并且取消重力,不然它会从那个球体上掉下来。

创建关节

剩下的事情就是将两个碰撞体连在一起了。一个绞连连接的代表是自行车前轮。

OgreOde::HingeJoint* joint = new OgreOde::HingeJoint();

joint->attach(dollTorsoBody,dollFeetBody);

joint->setAxis(Ogre::Vector3::UNIT_X);      //set the rotation axis

注意:不要忘记记录所有碰撞体和连接的位置以便你之后能得到它们。你可以用ogre堆栈或者创建你自己的。

移动角色

你可以通过不同方法移动或者旋转你的角色,我决定通过改变碰撞体方向而不是施加力或者力矩。

前后移动

下面代码可以放在按键响应里执行,象KC_UP。现在你需要获取碰撞体,从堆栈或者hashTable中获得,然后获取它的方向。我用:

译注:猜想堆栈就是你为屏幕上所有物体所创建的Ogre::Body的一个列表。

OgreOde::Body* torso = torsoBodies->getObject("ninja");

Quaternion q = torso->getOrientation();

然后赋予脚一个角速度。

OgreOde::Body* feet = feetBodies->getObject("ninja");

feet->wake();

feet->setAngularVelocity(q*Ogre::Vector3(10*cos(1),0,10*sin(1)));

10是我们用的角速度,必须乘以三角函数以便让角色向正确的方向前进。

左右转动

下面代码同样放在按键响应中执行,比如KC_RIGHT

OgreOde::Body* torso = torsoBodies->getObject("ninja");

Quaternion q1 = torso->getOrientation();

Quaternion q2(Degree(-4),Ogre::Vector3::UNIT_Y);

torso->setOrientation(q1*q2);

Degree(-4)让角色向右转动,用正数向左转动。也许你已经注意到,我总是从椭球体获取或者设置方向。我没有太多想,我想如果你从脚的球体来获取和设置也应该没有什么问题。

注意:如果你松开按键,你必须把速度设置为0来停止运动。

feetbody->setAngularVelocity(Vector3(0,0,0));

feetBody->setLinearVelocity(Vector3(0,feetBody->getLinearVelocity().y,0));

让角色爬起来

最后,我们要确定你的角色不摔倒,所以我们需要不时重新设定他的垂直方向。

OgreOde::Body* torso = torsoBodies->getObject("ninja");

Quaternion q = torso->getOrientation();                     

 

Vector3 x = q.xAxis();

Vector3 y = q.yAxis();

Vector3 z = q.zAxis();

 

torso->wake();

torso->setOrientation(Quaternion(x,Vector3::UNIT_Y,z));

问题

部分代码没有我想象的那么好。我重新设定垂直方向会让角色有奇怪的行为。虽然我设置了速度为0,但是角色在一些不规则的表面上仍然无法停下来。

posted on 2009-01-02 04:56 小王 阅读(1222) 评论(0)  编辑 收藏 引用 所属分类: 游戏引擎

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理