用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 移動角色
- 4 讓角色爬起來
- 5 問題
|
介紹
我相信我不是第一人自問如何用OgreOde創建一個運動角色。搜索論壇和wiki后,我意識到這是一個很有用的信息。這個教程解釋了如何創建一個可以在地形上行走的運動角色(包括其它meshes,如樹和房屋)。
創建物理模型
我按照在Monster的方法用下圖代表一個角色:
下面,我假設你對Ogre的SceneNodes, 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);
也許你已經注意到,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來創建碰撞體的位置,然后根據ninjaNode和OgreOde::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,但是角色在一些不規則的表面上仍然無法停下來。