用OgreOde創(chuàng)建一個(gè)會(huì)走動(dòng)的角色
原文http://www.game798.com/html/2007-12/5156.htm
第一版 by SuperMegaMau
這個(gè)教程包括的代碼和算法是作者自己的經(jīng)驗(yàn),也許不正確或不怎么準(zhǔn)確,如果發(fā)現(xiàn)問題請(qǐng)糾正。
|
內(nèi)容
- 1 介紹
- 2 創(chuàng)建物理模型
- 2.1 創(chuàng)建角色
- 2.2 獲取 AABB
- 2.3 創(chuàng)建新空間
- 2.4 創(chuàng)建球
- 2.5 創(chuàng)建橢球
- 2.6 創(chuàng)建關(guān)節(jié)
- 3 移動(dòng)角色
- 3.1 前后移動(dòng)
- 3.2 左右旋轉(zhuǎn)
- 4 讓角色爬起來
- 5 問題
|
介紹
我相信我不是第一人自問如何用OgreOde創(chuàng)建一個(gè)運(yùn)動(dòng)角色。搜索論壇和wiki后,我意識(shí)到這是一個(gè)很有用的信息。這個(gè)教程解釋了如何創(chuàng)建一個(gè)可以在地形上行走的運(yùn)動(dòng)角色(包括其它meshes,如樹和房屋)。
創(chuàng)建物理模型
我按照在Monster的方法用下圖代表一個(gè)角色:
下面,我假設(shè)你對(duì)Ogre的SceneNodes, meshes 和AlignedBoxes都有所了解,并且會(huì)用SceneManager創(chuàng)建地形。
創(chuàng)建角色
首先創(chuàng)建一個(gè)SceneNode來放角色的mesh,在這個(gè)例子中我用了Ogre例子中的忍者模型。創(chuàng)建兩個(gè)SceneNode并把它們連在一起。后面我會(huì)解釋為什么是兩個(gè)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);
也許你已經(jīng)注意到,ninjaNode被縮放得很小,這是因?yàn)槿绻?span>mesh很大的話渲染的速度就變很慢(不知道為什么)。
獲取 AABB(AxisAlignedBox, 軸對(duì)齊包圍盒)
現(xiàn)在用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;
創(chuàng)建一個(gè)新空間
我們需要?jiǎng)?chuàng)建一個(gè)新空間把角色放在其中,并且取消內(nèi)部碰撞檢測(cè)。
OgreOde::SimpleSpace* dollSpace = new OgreOde::SimpleSpace(_world->getDefaultSpace());
dollSpace->setInternalCollisions(false);
創(chuàng)建球體(腿部)
現(xiàn)在有了兩個(gè)SceneNodes, "ninjaNode" 和 "modelNode"。ninjaNode是代表你角色的節(jié)點(diǎn),modelNode是你真正貼mesh的地方。這么做是因?yàn)?span>mesh的中心總是在OgreOde::Body的中心,所以我們用ninjaNode來創(chuàng)建碰撞體的位置,然后根據(jù)ninjaNode和OgreOde::Body來獲得mesh的正確位置。
左邊圖是用一個(gè)SceneNode所得到的效果,右邊是用兩個(gè)SceneNode。你可以注意到,左邊的角色懸浮在半空中。下面代碼創(chuàng)建了一個(gè)球體代表角色的腳部。我們需要一個(gè)SphereGeometry和一個(gè)TransformGeometry 將球體放到正確位置。
譯注:注釋為我的猜想,具體不知道步驟這么復(fù)雜,歡迎糾正。
(1) 創(chuàng)建一個(gè)碰撞體,命名為feet
(2) 設(shè)置碰撞體為球形物體,半徑為AABB獲得的半徑
(3) SphereGeometry,半徑為AABB獲得的半徑
(4) TransformGeometry,空間為剛才創(chuàng)建的空間, TransformGeometry似乎是為了包含某特定形狀的幾何體
(5) 改變modelNode相對(duì)于ninjiaNode的位置,以便讓腳占到地上
(6) 讓TransformGeometry包含一個(gè)OgreOde::Body和一個(gè)幾何體
(7) 將Ogre::Body粘到ninjaNode上
懷疑創(chuàng)建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);
創(chuàng)建橢球體
對(duì)于角色的上半身用一個(gè)橢球體來表示。
譯注:和上面一樣。
(1) 創(chuàng)建Ogre::Body
(2) 設(shè)置Ogre::Body形狀,另外設(shè)置不被重力影響,
(3) 創(chuàng)建TransformGeometry,空間為剛才創(chuàng)建的空間
(4) 創(chuàng)建CapsuleGeometry,半徑為AABB獲得半徑
(5) 設(shè)置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);
這個(gè)幾何體和腳的幾何體在同一個(gè)空間,所以我們要取消內(nèi)部碰撞檢測(cè)。講阻尼設(shè)置高些,并且取消重力,不然它會(huì)從那個(gè)球體上掉下來。
創(chuàng)建關(guān)節(jié)
剩下的事情就是將兩個(gè)碰撞體連在一起了。一個(gè)絞連連接的代表是自行車前輪。
OgreOde::HingeJoint* joint = new OgreOde::HingeJoint();
joint->attach(dollTorsoBody,dollFeetBody);
joint->setAxis(Ogre::Vector3::UNIT_X); //set the rotation axis
注意:不要忘記記錄所有碰撞體和連接的位置以便你之后能得到它們。你可以用ogre堆棧或者創(chuàng)建你自己的。
移動(dòng)角色
你可以通過不同方法移動(dòng)或者旋轉(zhuǎn)你的角色,我決定通過改變碰撞體方向而不是施加力或者力矩。
前后移動(dòng)
下面代碼可以放在按鍵響應(yīng)里執(zhí)行,象KC_UP。現(xiàn)在你需要獲取碰撞體,從堆棧或者hashTable中獲得,然后獲取它的方向。我用:
譯注:猜想堆棧就是你為屏幕上所有物體所創(chuàng)建的Ogre::Body的一個(gè)列表。
OgreOde::Body* torso = torsoBodies->getObject("ninja");
Quaternion q = torso->getOrientation();
然后賦予腳一個(gè)角速度。
OgreOde::Body* feet = feetBodies->getObject("ninja");
feet->wake();
feet->setAngularVelocity(q*Ogre::Vector3(10*cos(1),0,10*sin(1)));
10是我們用的角速度,必須乘以三角函數(shù)以便讓角色向正確的方向前進(jìn)。
左右轉(zhuǎn)動(dòng)
下面代碼同樣放在按鍵響應(yīng)中執(zhí)行,比如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)讓角色向右轉(zhuǎn)動(dòng),用正數(shù)向左轉(zhuǎn)動(dòng)。也許你已經(jīng)注意到,我總是從橢球體獲取或者設(shè)置方向。我沒有太多想,我想如果你從腳的球體來獲取和設(shè)置也應(yīng)該沒有什么問題。
注意:如果你松開按鍵,你必須把速度設(shè)置為0來停止運(yùn)動(dòng)。
feetbody->setAngularVelocity(Vector3(0,0,0));
feetBody->setLinearVelocity(Vector3(0,feetBody->getLinearVelocity().y,0));
讓角色爬起來
最后,我們要確定你的角色不摔倒,所以我們需要不時(shí)重新設(shè)定他的垂直方向。
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));
問題
部分代碼沒有我想象的那么好。我重新設(shè)定垂直方向會(huì)讓角色有奇怪的行為。雖然我設(shè)置了速度為0,但是角色在一些不規(guī)則的表面上仍然無法停下來。