用OgreOde创建一个会走动的角色

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

创建物理模型

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


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

创建角色

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

  1. Entity* ninja = mSceneMgr->createEntity("ninja","ninja.mesh");   
  2.   
  3. SceneNode* ninjaNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("ninja");   
  4.   
  5. SceneNode* modelNode = ninjaNode->createChildSceneNode("ninja_model");   
  6.   
  7. modelNode->attachObject(ninja);   
  8.   
  9. ninjaNode->setScale(0.05,0.05,0.05);   
  10.   
  11. RootSceneNode   
  12.   
  13. ninjaNode   
  14.   
  15. modelNode  

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

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

现在用AxisAlignedBox获取mesh的大小。

  1. AxisAlignedBox aab = modelNode->getAttachedObject("ninja")->getBoundingBox();   
  2.   
  3. Ogre::Vector3 min = aab.getMinimum()*ninjaNode->getScale();   
  4.   
  5. Ogre::Vector3 max = aab.getMaximum()*ninjaNode->getScale();   
  6.   
  7. Ogre::Vector3 center = aab.getCenter()*ninjaNode->getScale();   
  8.   
  9. Ogre::Vector3 size(fabs(max.x-min.x),fabs(max.y-min.y),fabs(max.z-min.z));   
  10.   
  11. float radius = (size.x>size.z)?size.z/2.0f:size.x/2.0f;  

创建一个新空间

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

  1. OgreOde::SimpleSpace* dollSpace = new OgreOde::SimpleSpace(_world->getDefaultSpace());   
  2.   
  3. 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具象化

  1. OgreOde::Body* dollFeetBody = new OgreOde::Body("feet");     
  2.   
  3. dollFeetBody->setMass(OgreOde::SphereMass(70*2.5,radius));   
  4.   
  5. OgreOde::SphereGeometry* feetGeom = new OgreOde::SphereGeometry(radius);   
  6.   
  7. OgreOde::TransformGeometry* feetTrans = new OgreOde::TransformGeometry(dollSpace);   
  8.   
  9. modelNode->translate(Vector3(0,-radius/ninjaNode->getScale().y,0));   
  10.   
  11. feetTrans->setBody(dollFeetBody);   
  12.   
  13. feetTrans->setEncapsulatedGeometry(feetGeom);   
  14.   
  15. 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上

  1. OgreOde::Body* dollTorsoBody = new OgreOde::Body("torso");   
  2.   
  3. dollTorsoBody->setMass(OgreOde::CapsuleMass(70*2.5,radius,Vector3::UNIT_Y,radius));   
  4.   
  5. dollTorsoBody->setAffectedByGravity(false);   
  6.   
  7. dollTorsoBody->setDamping(0,50000);   
  8.   
  9. OgreOde::TransformGeometry* torsoTrans = new OgreOde::TransformGeometry(dollSpace);   
  10.   
  11. OgreOde::CapsuleGeometry* torsoGeom = new OgreOde::CapsuleGeometry(radius,size.y-4*radius,dollSpace);   
  12.   
  13. torsoGeom->setPosition(Ogre::Vector3(0,size.y-((size.y-4*radius)/2+2*radius),0)); //can't find a good way to explain this   
  14.   
  15. torsoGeom->setOrientation(Quaternion(Degree(90),Vector3::UNIT_X));   
  16.   
  17. torsoTrans->setBody(dollTorsoBody);   
  18.   
  19. torsoTrans->setEncapsulatedGeometry(torsoGeom);   
  20.   
  21. ninjaNode->attachObject(dollTorsoBody);  

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

创建关节

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

  1. OgreOde::HingeJoint* joint = new OgreOde::HingeJoint();   
  2.   
  3. joint->attach(dollTorsoBody,dollFeetBody);   
  4.   
  5. joint->setAxis(Ogre::Vector3::UNIT_X);      //set the rotation axis  

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

移动角色

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

前后移动

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

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

  1. OgreOde::Body* torso = torsoBodies->getObject("ninja");   
  2.   
  3. Quaternion q = torso->getOrientation();  

然后赋予脚一个角速度。

  1. OgreOde::Body* feet = feetBodies->getObject("ninja");   
  2.   
  3. feet->wake();   
  4.   
  5. feet->setAngularVelocity(q*Ogre::Vector3(10*cos(1),0,10*sin(1)));  

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

左右转动

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

  1. OgreOde::Body* torso = torsoBodies->getObject("ninja");   
  2.   
  3. Quaternion q1 = torso->getOrientation();   
  4.   
  5. Quaternion q2(Degree(-4),Ogre::Vector3::UNIT_Y);   
  6.   
  7. torso->setOrientation(q1*q2);  

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

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

  1. feetbody->setAngularVelocity(Vector3(0,0,0));   
  2.   
  3. feetBody->setLinearVelocity(Vector3(0,feetBody->getLinearVelocity().y,0));  

让角色爬起来

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

  1. OgreOde::Body* torso = torsoBodies->getObject("ninja");   
  2.   
  3. Quaternion q = torso->getOrientation();                        
  4.   
  5.   
  6.   
  7. Vector3 x = q.xAxis();   
  8.   
  9. Vector3 y = q.yAxis();   
  10.   
  11. Vector3 z = q.zAxis();   
  12.   
  13.   
  14.   
  15. torso->wake();   
  16.   
  17. torso->setOrientation(Quaternion(x,Vector3::UNIT_Y,z));  

问题

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的Java机器人待机动画示例,它来回走动: ```java import java.awt.Color; import java.awt.Graphics; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.Timer; public class RobotAnimation extends JPanel implements ActionListener { private static final long serialVersionUID = 1L; private int x = 0; private int y = 100; private int dx = 5; private int dy = 0; private int width = 50; private int height = 50; private int legAngle = 0; private int armAngle = 0; private Timer timer; public RobotAnimation() { timer = new Timer(50, this); timer.start(); } public void paintComponent(Graphics g) { super.paintComponent(g); g.setColor(Color.BLUE); g.fillRect(x, y, width, height); g.setColor(Color.RED); g.drawLine(x, y, x + width, y); g.drawLine(x, y + height, x + width, y + height); g.drawLine(x, y, x, y + height); g.drawLine(x + width, y, x + width, y + height); g.drawLine(x, y + height / 2, x - 20, y + height / 2 - 20); g.drawLine(x, y + height / 2, x - 20, y + height / 2 + 20); g.drawLine(x + width, y + height / 2, x + width + 20, y + height / 2 - 20); g.drawLine(x + width, y + height / 2, x + width + 20, y + height / 2 + 20); g.drawLine(x + width / 2, y, x + width / 2, y - 20); g.drawLine(x + width / 2, y, x + width / 2 - 20, y - 20); g.drawLine(x + width / 2, y, x + width / 2 + 20, y - 20); g.drawLine(x + width / 2, y + height, x + width / 2, y + height + 20); g.drawLine(x + width / 2, y + height, x + width / 2 - 20, y + height + 20); g.drawLine(x + width / 2, y + height, x + width / 2 + 20, y + height + 20); g.drawLine(x + width / 2, y + height / 2, x + width / 2 - 20, y + height / 2 + legAngle); g.drawLine(x + width / 2, y + height / 2, x + width / 2 + 20, y + height / 2 - legAngle); g.drawLine(x + width / 2, y + height / 2, x + width / 2 - 20, y + height / 2 + armAngle); g.drawLine(x + width / 2, y + height / 2, x + width / 2 + 20, y + height / 2 - armAngle); } public void actionPerformed(ActionEvent e) { if (x + dx < 0 || x + dx > getWidth() - width) { dx = -dx; } x += dx; legAngle = (legAngle + 5) % 30; armAngle = (armAngle + 10) % 60; repaint(); } public static void main(String[] args) { JFrame frame = new JFrame("Robot Animation"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(400, 300); frame.setLocationRelativeTo(null); frame.add(new RobotAnimation()); frame.setVisible(true); } } ``` 该程序创建了一个继承自JPanel的RobotAnimation类,它包含一个机器人的x和y坐标、宽度和高度、腿和臂的角度等属性。在构造函数中,程序创建了一个定时器,每50毫秒调用一次actionPerformed()方法。在paintComponent()方法中,程序使用Graphics对象绘制机器人的矩形身体、四条腿、两只手臂和两只眼睛。在actionPerformed()方法中,程序更新机器人的x坐标、腿和臂的角度,并调用repaint()方法重新绘制机器人。在main()方法中,程序创建了一个JFrame窗口,并将RobotAnimation对象添加到其中。运行程序后,您将看到一个机器人来回走动的待机动画。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值