文档章节

top down car教程1【原创翻译老外的】

阳666
 阳666
发布于 2015/11/26 22:53
字数 2231
阅读 39
收藏 0

原文链接

http://www.iforce2d.net/b2dtut/top-down-car

最近讨论'top down' car physics如何很实现的话题很越来越多,所以我想我可以试一试并且展开一个话题。一个 td(top-down) car 被设计成在一个0重力的世界中,被一个为底盘的身体和四个分离的轮子身体。它取决于仅仅用一个带有底盘的身体

和不必担心的分离的轮子。
问题的关键的另一种情况是阻止身体沿一条轴移动,然而仍然允许他在另外一条轴上自由移动(轮子应该可以来回移动。)这本身并不是一个艰难的壮举,诀窍是使得那些用户来控制车有很好的感受。如果水平速度被简单的完全种植,车子就会感觉像是在铁路上跑一样,并且我们想允许车子可以在一定的情况下大话,并且在不同的表面有不同的表现等等。在我们开始以前你可以看一下这个完美实现的top-down赛车游戏.这种事情是我们的目标。
 基础的步骤是去找到最近的身体的水平速度并且给他一个推理是的我们可以取消那个水平速度。我们开始仅仅用一个身体来代表一个轮子,一会我们用四个它来固定到另外一个身体来实现更复杂的模拟。因为所有的轮子做同样的事情,我们可以为他做一个类。这是起始点,一个带有b2Body指针自以为成员变量并且带着一个简单的盒子形状来初始化。
 
  class TDTire {
  public:
      b2Body* m_body;
  
      TDTire(b2World* world) {
          b2BodyDef bodyDef;
          bodyDef.type = b2_dynamicBody;
          m_body = world->CreateBody(&bodyDef);
  
          b2PolygonShape polygonShape;
          polygonShape.SetAsBox( 0.5f, 1.25f );
          m_body->CreateFixture(&polygonShape, 1);//shape, density
          
          m_body->SetUserData( this );
      }
  
      ~TDTire() {
          m_body->GetWorld()->DestroyBody(m_body);
      }
  };
  
  这里我是用了一个CreateFixture不需要fixture定义的简单版本,注意到我们也设置了数据来创建b2body,所以这个物理身体和游戏逻辑会彼此涉及。
  ----------------------------------------------------------------------------------
  消除水平速度。
为了取消水平速度,我们需要知道它是什么。我们可以找到在最近时间内身体的水平速度在赛道方向的法线上的投影假。设轮子的当前坐标是(0,1)将会向前走1,0将会向右走我们可以使用获取世界向量来获取最近的身体在世界坐标中的朝向。

假设我们的轮胎旋转了一点点,向上移动如下图所示。我们想要使得蓝色向量向红色向量投影来看看他有多长如果他仅仅是以红色方向移动。
我们可以增加下面这样的方法到TDTire里面:
b2Vec2 getLateralVelocity() {
      b2Vec2 currentRightNormal = m_body->GetWorldVector( b2Vec2(1,0) );
      return b2Dot( currentRightNormal, m_body->GetLinearVelocity() ) * currentRightNormal;
}
然后给他一个推力,使得摆脱他的水平速度。这和最后一部分“一恒定速度前进”很相似,那里我们有一个渴望的速度。我们应用一个推力乘以身体的质量,使其达到这一速度在一个时间步。我们增加一个函数使得它在一个时间步让他做这些。
void updateFriction() {
      b2Vec2 impulse = m_body->GetMass() * -getLateralVelocity();
      m_body->ApplyLinearImpulse( impulse, m_body->GetWorldCenter() );
}
在舞台上,如果你创建了一个轮子身体在世界中,如果使用鼠标推动他,将会注意到赛道方向的速度确实被推力取消了,如果朝前方推动轮子你会发现它更倾向于转圆圈。几乎他就像一个真的轮胎一样当你滚动轮子并且他会慢慢停下来。

你也会注意到他可以自由旋转,使得它看起来有点不真实,一个真实的车轮子不是那样的,所以让我们给用
相似的方式来消除侧面速度。。旋转更容易一些,因为我们不需要做这个向量投影的东西——添加这个到updateFriction:


m_body->ApplyAngularImpulse( 0.1f * m_body->GetInertia() * -m_body->GetAngularVelocity() );
0.1是我决定的值,他有点像我上一次旋转的轮胎。如果我们完全停掉轮子的旋转,他将会像在铁路一样,哪里都不能去,除了走直线。没有完全阻止轮胎的旋转的另一个原因是我们想让玩家来使用这个身体来驱动它。
最后你可能注意到轮子会永远朝前走,所以我们给一个强力使得它最终停止。
 
b2Vec2 currentForwardNormal = getForwardVelocity();
float currentForwardSpeed = currentForwardNormal.Normalize();
float dragForceMagnitude = -2 * currentForwardSpeed;
m_body->ApplyForce( dragForceMagnitude * currentForwardNormal, m_body->GetWorldCenter() );
再次进入updateFriction和值2来自一些诈骗和调整。当然你聪明的人自动知道getForwardVelocity getLateralVelocity()是一样的(),但与当地的一个向量(0,1)代替(1,0)对吗?
    
控制一个轮子。
在我们将要制作四轮车之前,我们需要更多的考虑下一个轮子应该做的事情。我们至少能让他往前往后走。我也要使得它能够打滑,并且处理不同的表面。我们先集中精力做好一个,剩下四个就会很容易。
要测试轮胎是受多种运动的情况,我们可以先假设这个单一的轮胎本身就是一辆汽车,让用户直接旋转它。这是一个基本的方式跟踪的关键(W/A/S/D)的用户是当前按下的按键:
//global scope
  enum {
      TDC_LEFT   = 0x1,
      TDC_RIGHT  = 0x2,
      TDC_UP     = 0x4,
      TDC_DOWN   = 0x8
  };
  
  //testbed Test class variable
  int m_controlState;
  
  //testbed Test class constructor
  m_controlState = 0;
  
  //testbed Test class functions
  void Keyboard(unsigned char key) {
      switch (key) {
        case 'a' : m_controlState |= TDC_LEFT;  break;
        case 'd' : m_controlState |= TDC_RIGHT; break;
        case 'w' : m_controlState |= TDC_UP;    break;
        case 's' : m_controlState |= TDC_DOWN;  break;
        default: Test::Keyboard(key);
      }
  }
  void KeyboardUp(unsigned char key) {
      switch (key) {
        case 'a' : m_controlState &= ~TDC_LEFT;  break;
        case 'd' : m_controlState &= ~TDC_RIGHT; break;
        case 'w' : m_controlState &= ~TDC_UP;    break;
        case 's' : m_controlState &= ~TDC_DOWN;  break;
        default: Test::Keyboard(key);
      }
  }
注意: KeyboardUp 可以在最新的box2d testbed中使用,但是如果你正在使用v2.1.2 从其他的教程中找不到。你可以获取最新的box2d版本,或者在testbed中增加实现,他是很简单的仅仅使用键盘函数。
让我们增加一个函数到轮子的clas中,使得他在输入的状态中做更聪明的事情。
 float m_maxForwardSpeed;  // 100;
  float m_maxBackwardSpeed; // -20;
  float m_maxDriveForce;    // 150;
  
  //tire class function
  void updateDrive(int controlState) {
      //find desired speed
      float desiredSpeed = 0;
      switch ( controlState & (TDC_UP|TDC_DOWN) ) {
          case TDC_UP:   desiredSpeed = m_maxForwardSpeed;  break;
          case TDC_DOWN: desiredSpeed = m_maxBackwardSpeed; break;
          default: return;//do nothing
      }
      
      //find current speed in forward direction
      b2Vec2 currentForwardNormal = m_body->GetWorldVector( b2Vec2(0,1) );
      float currentSpeed = b2Dot( getForwardVelocity(), currentForwardNormal );
      
      //apply necessary force
      float force = 0;
      if ( desiredSpeed > currentSpeed )
          force = m_maxDriveForce;
      else if ( desiredSpeed < currentSpeed )
          force = -m_maxDriveForce;
      else
          return;
      m_body->ApplyForce( force * currentForwardNormal, m_body->GetWorldCenter() );
  }






使用速度和力量来到使他变成你想要的那样,话题的开始我们有太多考虑过尺寸,并且我的轮子不是个不真实的一宽,所以这些速度也不是真实世界的值。
  现在轮子可以向前移动和向后移动,我们也使得它运转通过事假一些扭转力,当a/d 按键被按下。因为我们最后的目标是绑定这些轮子到一个车子的身上,程序的这部分将会被丢掉,所以他只是一个粗略的方式使得拐弯发生。所以我们可以测试下一部分--打滑和表面。另一方面,如果你真的倾向于设计汽车作为单体,你可能需要冲个这些使得它够合乎情理的。e.g.不要让车子拐弯除非他在移动。
   void updateTurn(int controlState) {
      float desiredTorque = 0;
      switch ( controlState & (TDC_LEFT|TDC_RIGHT) ) {
          case TDC_LEFT:  desiredTorque = 15;  break;
          case TDC_RIGHT: desiredTorque = -15; break;
          default: ;//nothing
      }
      m_body->ApplyTorque( desiredTorque );
  }
  
  Allowing skidding
这一点我们有一个可以控制的汽车身体,根据我们最初的计划来消除横向速度,他表现的良好。这是非常好的,如果你想模拟的槽车,坚持自己的轨道像胶水。但是他感觉更自然如果车子可以再打滑一点。记得当我们完全横向速度的时候,我们怎样完全消除他的?我们简单的计算一下推力并给他加上去。所以所有我们需要做的事情就是约束这个推力到最大值,并且轮子将会打滑当环境要求一个比允许值更大的修改。在updateFriction函数中这仅仅是一个另外的状态。
 b2Vec2 impulse = m_body->GetMass() * -getLateralVelocity(); //existing code
  if ( impulse.Length() > maxLateralImpulse )
      impulse *= maxLateralImpulse / impulse.Length();
  m_body->ApplyLinearImpulse( impulse, m_body->GetWorldCenter() ); //existing code
我发现maxLateralImpulse为3的时候允许很少的数量的打滑 当以告诉度拐弯的时候,值为2的时候就像一个湿撸,值为1的时候是我想起一个行驶的小船在水上拐弯。这些值无论如何将会被适应当轮子连接到车子底盘。所以现在不要大惊小怪。




  




© 著作权归作者所有

共有 人打赏支持
阳666
粉丝 6
博文 78
码字总数 63755
作品 0
杨浦
程序员
私信 提问
收集的资料

图形学 视频:《游戏图像简史》 《 剑英陪你玩转图形学 》<打死也不要看这个图形学教程太逗比> 《图形管线之旅》 不仅仅是D3D/OpenGL图形管线,而是涵盖了这些3D API的低级别的GPU和管线描述...

陌路-野山君
2016/07/21
7
0
轻松学习 JavaScript——第 8 部分:JavaScript 中的类

1. 程序员必读24本经典,100%免费领取 2. 最值得学习的24本编程书,免费领取 3. 24本编程纸质书籍,云沃客限时免费送! 4. 助力程序员快速涨薪的24本精选纸质书 本文由码农网 – 小峰原创翻译...

3. 24本编程纸质书籍,云沃客限时免费送!
2018/01/06
0
0
人人都会设计模式---适配器模式--Adapter

适配器模式大纲 版权声明:本文为博主原创文章,未经博主允许不得转载 PS:转载请注明出处 作者: TigerChain 地址: www.jianshu.com/p/1edf5d944… 本文出自 TigerChain 简书 人人都会设计模...

TigerChain
2017/11/13
0
0
作为一个英语菜鸡改如何优雅的在stackoverflow提问?

无奈一些冷门的开源软件在国内各大论坛提问,通常得不到答案,所以时不时回去stackoverflow提问,但是本人英语特别菜,结合百度翻译勉强能提出问题。但是被踩的面目全非,老外经常看不明白题...

学而不思则罔
2017/04/06
246
6
SQL Server Database Mail配置指南SQL Server Database Mail配置指南

参考文献: 1.圖文教你怎樣配置Database Mail+JOB(MSSQL2005以上版本)(按照这个教程完成配置) 2.SQL SERVER 2008配置Database Mail –用SQL 数据库发邮件(官方教程,从老外那边翻译过来的...

嗯哼9925
2017/12/26
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Windows同步对象Event和Linux的条件变量

最近在看一些同步对象模拟的东东,特别对在Windows下如何模拟条件变量折腾了很久。 1 Windows同步对象Event 微软有一个很有意思的同步对象,某种程度上和Linux的条件变量很相似。但秉承微软一...

shzwork
9分钟前
0
0
从上往下打印出二叉树的每个节点,同层节点从左至右打印。

//第一种做法 public class Solution { public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) { ArrayList <Integer> li=new ArrayList<Integer>(); ArrayList <TreeN......

南桥北木
19分钟前
0
0
linux 服务管理 Crontba、Ntpdate、Logrotate、Supervisor

crond linux 系统则是由 cron (crond) 这个系统服务来控制的。Linux 系统上面原本就有非常多的计划性工作,因此这个系统服务是默认启动的。 另外, 由于使用者自己也可以设置计划任务,所以,...

狼王黄师傅
49分钟前
3
0
Sobel算子和Scharr滤波器

Sobel算子在数学上的本质是微分,对离散信号,是求邻域内的增量。 基本原理:在图像上,对图像信号在某点进行微分,表示图像的某个特征(如,强度、色调或者饱和度)在该点的变换程度。以强度...

yepanl
今天
2
0
Jenkins API 使用

Jenkins 是一款流行的开源持续集成工具,可以用来做一些软件开发的自动化工作,如打包,测试,自动部署等。 Jenkins 中有 view 和 job 的概念, view 相当于组, job 则是具体的任务。 view...

YanWen
今天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部