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

Easter79
• 阅读 465

原文链接

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的时候是我想起一个行驶的小船在水上拐弯。这些值无论如何将会被适应当轮子连接到车子底盘。所以现在不要大惊小怪。

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Karen110 Karen110
2年前
一篇文章带你了解JavaScript日期
日期对象允许您使用日期(年、月、日、小时、分钟、秒和毫秒)。一、JavaScript的日期格式一个JavaScript日期可以写为一个字符串:ThuFeb02201909:59:51GMT0800(中国标准时间)或者是一个数字:1486000791164写数字的日期,指定的毫秒数自1970年1月1日00:00:00到现在。1\.显示日期使用
Wesley13 Wesley13
2年前
java将前端的json数组字符串转换为列表
记录下在前端通过ajax提交了一个json数组的字符串,在后端如何转换为列表。前端数据转化与请求varcontracts{id:'1',name:'yanggb合同1'},{id:'2',name:'yanggb合同2'},{id:'3',name:'yang
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Peter20 Peter20
3年前
mysql中like用法
like的通配符有两种%(百分号):代表零个、一个或者多个字符。\(下划线):代表一个数字或者字符。1\.name以"李"开头wherenamelike'李%'2\.name中包含"云",“云”可以在任何位置wherenamelike'%云%'3\.第二个和第三个字符是0的值wheresalarylike'\00%'4\
Easter79 Easter79
2年前
SpringBoot整合Redis乱码原因及解决方案
问题描述:springboot使用springdataredis存储数据时乱码rediskey/value出现\\xAC\\xED\\x00\\x05t\\x00\\x05问题分析:查看RedisTemplate类!(https://oscimg.oschina.net/oscnet/0a85565fa
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
Easter79
Easter79
Lv1
今生可爱与温柔,每一样都不能少。
文章
2.8k
粉丝
5
获赞
1.2k