Unity3d 赛车车辆各类性能算法

欢迎来到unity学习unity培训unity企业培训教育专区,这里有很多Unity3D资源Unity3D培训视频Unity3D教程Unity3D常见问题Unity3D项目源码,【狗刨学习网】unity极致学院,致力于打造业内unity3d培训、学习第一品牌。

   在制作前,必须先了解真实车辆的原理:

  车辆分前轮驱动,后轮驱动和四驱动。动力由引擎提供,反应的力到轮胎上,因此产生转数,即RPM。

  引擎的功率可以由RPM得到公式为 : RPM = 引擎功率60/2pi , 这些都是模拟,只为了更好的为下面的动作服务。还有大众关心的"漂移" ,所谓 漂移就是,在后驱车辆中,前轮方向旋转大角度,地面给于一定向心力,同时后轮又给予更多动力,导致"漂移"动作。

  基于上面原理,就更容易理解下面的文章。

  我先说明,有非常多种方法来开发一款赛车。本人赛车版本经过无数多种方法,无数次改版后最终选了个大众接受的。这些方法,容我一个个道来。

  首先,你要明白,车辆不可能整个套一个外壳,原因是在接触地面时,对车辆所使的力不可能达到你预期的目标,引起,必须在车辆轮胎以上做外壳碰撞,轮胎以下就需要有力来支持它始终保持不掉下来。

  Unity3d里有个很神奇的东西 叫WheelCollider , 它是专门模拟轮胎支持力和轮胎转数,以及轮胎横向力,前进力,以及 悬架避震系统。

  这个东西非常方便,只要你把这个东西套在4个轮胎上,调试下他的forwardFriction 和 sidewaysFriction

  达到你想要的效果,然后对驱动轮的motorTorque进行赋值,你的车辆就能动了。

  记得你需要无数次调试 前进摩擦力和横向摩擦力 。

  至于悬架系统在你需要时也可以改变其值。还有,这两个摩擦力,是一个由低到高,再由高到稳定的一条曲线。

  这个WheelCollider非常好用,曾一度沉迷于此。但后来发现,他有很多不合理的地方。想要得到最好的效果,还是抛弃了他。

  为了支持车辆的碰撞外壳不接触地面,必须写一个悬架动态支持力,在4个轮胎位置,支持整辆车悬浮于地面之上。

  关于这个悬架动态支持力在这奉献下:

  void SuspensionHoldForce()

  {

  float fullCompressionSpringForce = this.rigidbody.mass * 0.25f * 2.0f * -Physics.gravity.y;

  this.OnGround = true;

  foreach( GameObject item in FwheelModels )

  {

  RaycastHit hit;

  bool onGround = Physics.Raycast( item.transform.parent.position , -item.transform.parent.InverseTransformDirection(Vector3.up), out hit, this.suspensionTravel + this.radius);

  if (onGround  hit.collider.isTrigger)

  {

  onGround = false;

  float dist = this.suspensionTravel + this.radius;

  RaycastHit[]

  hits = Physics.RaycastAll( item.transform.parent.position , -item.transform.parent.InverseTransformDirection(Vector3.up) , this.suspensionTravel + this.radius );

  foreach(RaycastHit test in hits)

  {

  if (!test.collider.isTrigger  test.distance = dist)

  {

  hit = test;

  onGround = true;

  dist = test.distance;

  }

  }

  }

  if( onGround )

  {

  Vector3 wheelVelo = this.rigidbody.GetPointVelocity (item.transform.parent.position);

  Vector3 localVelo = transform.InverseTransformDirection (wheelVelo);

  Vector3 groundNormal = transform.InverseTransformDirection (hit.normal);

  float damperForce = Vector3.Dot(localVelo, groundNormal) * 5000f;

  float compression = 1.0f - ((hit.distance - radius) / suspensionTravel);

  Vector3 springForce = ( fullCompressionSpringForce*compression - damperForce ) * item.transform.parent.InverseTransformDirection(Vector3.up);

  springForce.z = springForce.x = 0f;

  this.rigidbody.AddForceAtPosition( springForce , item.transform.parent.position );

  }

  else

  {

  this.OnGround = false;

  }

  }

  foreach( GameObject item in BwheelModels )

  {

  RaycastHit hit;

  bool onGround = Physics.Raycast(  item.transform.parent.position, -item.transform.parent.InverseTransformDirection(Vector3.up), out hit, this.suspensionTravel + this.radius);

  if (onGround  hit.collider.isTrigger)

  {

  onGround = false;

  float dist = this.suspensionTravel + this.radius;

  RaycastHit[]

  hits = Physics.RaycastAll( item.transform.parent.position, -item.transform.parent.InverseTransformDirection(Vector3.up) , this.suspensionTravel + this.radius );

  foreach(RaycastHit test in hits)

  {

  if (!test.collider.isTrigger  test.distance = dist)

  {

  hit = test;

  onGround = true;

  dist = test.distance;

  }

  }

  }

  if( onGround )

  {

  Vector3 wheelVelo = this.rigidbody.GetPointVelocity (item.transform.parent.position);

  Vector3 localVelo = transform.InverseTransformDirection (wheelVelo);

  Vector3 groundNormal = transform.InverseTransformDirection (hit.normal);

  float damperForce = Vector3.Dot(localVelo, groundNormal) * 5000f;

  float compression = 1.0f - ( ( hit.distance - radius ) / suspensionTravel );

  Vector3 springForce = ( fullCompressionSpringForce*compression - damperForce ) * item.transform.parent.InverseTransformDirection(Vector3.up);

  springForce.z = springForce.x = 0f;

  this.rigidbody.AddForceAtPosition( springForce , item.transform.parent.position );

  }

  else

  {

  this.OnGround = false;

  }

  }

  }

  那么在完成悬架支撑后,就该设计车辆动力了。

  这里也有2种方法:一个方向是真实车辆行驶轨迹,另一个是模拟型车辆轨迹。

  前者的方法是 , 将动力点放在车辆驱动轮上,例如后轮。用rigidbody的AddForceAtPosition可以做到,前轮只需要提供横向力就可以实现转弯的轨迹。但别看说说这么容易,这里面还涉及非常多的数值和曲线问题。在提供车辆动力时,你需要一条曲线,以致车辆不会匀加速,因为这样很不真实,还有在前轮横向力中,你必需是条由0到最高点,然后下降到平衡点的曲线。这样你的赛车才显得更真实。这些都需要用到几个数学知识。

  后者,是用算法来模拟的一种车辆轨迹。这个算法所有作用力作用在车辆的中心点。

  转弯轨迹,我是用转弯半径来表示,使得车辆在转弯时有相当的真实性,必须改变车辆转弯速度。当然,用到了些数学知识。代码奉献下:

  #region 计算转弯角度

  void Steering( bool canSteer , Vector3 relativeVelocity )

  {

  if( canSteer  this.OnGround )

  {

  if( this.shiftthrottle == 1 )

  {

  this.transform.RotateAround( this.transform.TransformPoint( ( this.FwheelModels[0].transform.localPosition + this.FwheelModels[1].transform.localPosition) * 0.5f ) , this.transform.up , this.rigidbody.velocity.magnitude *2f* this.steeringInput * Time.deltaTime
* 2f );

  //~ this.rigidbody.AddForceAtPosition( this.FwheelModels[0].transform.TransformDirection(Vector3.right*this.steeringInput) * 3f * this.rigidbody.mass, this.FwheelModels[0].transform.position);

  //~ this.rigidbody.AddForceAtPosition( this.FwheelModels[1].transform.TransformDirection(Vector3.right*this.steeringInput) * 3f * this.rigidbody.mass, this.FwheelModels[1].transform.position);

  return ;

  }

  if( this.throttle * this.transform.InverseTransformDirection(this.rigidbody.velocity).z  0 )

  return ;

  float turnRadius = 3.0f / Mathf.Sin( (90f - this.steering) * Mathf.Deg2Rad );

  float minMaxTurn = EvaluateSpeedToTurn(this.rigidbody.velocity.magnitude);

  float turnSpeed = Mathf.Clamp(relativeVelocity.z / turnRadius, -minMaxTurn / 10, minMaxTurn / 10);

  this.transform.RotateAround( this.transform.position + this.transform.right * turnRadius * this.steeringInput , transform.up , turnSpeed * Mathf.Rad2Deg * Time.deltaTime * this.steeringInput );

  //~ Vector3 debugStartPoint = transform.position + transform.right * turnRadius * this.steeringInput;

  //~ Vector3 debugEndPoint = debugStartPoint + Vector3.up * 5f;

  //~ Debug.DrawLine(debugStartPoint, debugEndPoint, Color.red);

  }

  }

  float EvaluateSpeedToTurn( float speed )

  {

  if(speed  this.topSpeed / 2)

  return minimumTurn;

  float speedIndex = 1 - ( speed / ( this.topSpeed / 2 ) );

  return minimumTurn + speedIndex * (maximumTurn - minimumTurn);

  }

  #endregion

  这个模拟车辆轨迹,不能达到漂移的性能,但我加了一个滑动比例计算的算法,用车辆横向移动速度,和前进速度,的比例来确定,该车辆是否处于漂移状态,如处于,则启动漂移滑动程序。当然,我的赛车是很自然的,不做做。至于轮胎痕迹,就是判断是否触底后,在该点生成轮胎痕迹gameobject,如此而已。

  最后,再介绍下,所有车辆都需要模拟的,行驶时,轮胎随速度旋转这个关系到车辆看起来真实性的东西。其实非常简单。不多说,发代码:

  #region 轮胎滚动与旋转模拟

  void WheelRoll()

  {

  float averageAngularVelo = ( this.rigidbody.GetPointVelocity(this.BwheelModels[0].transform.parent.position).magnitude + this.rigidbody.GetPointVelocity(this.BwheelModels[0].transform.parent.position).magnitude )/2f;

  float engineAngularVelo = averageAngularVelo * 3f;

  float rpm = engineAngularVelo * (60.0f/(2*Mathf.PI)) * (this.transform.InverseTransformDirection(this.rigidbody.velocity).z  0f ? 1f : -1f );

  //~ Debug.Log(this.transform.InverseTransformDirection(this.rigidbody.velocity).z);

  FwheelModels[0].transform.rotation = FwheelModels[0].transform.parent.rotation *     Quaternion.Euler (RotationValue, this.steering , 0);//旋转

  FwheelModels[1].transform.rotation = FwheelModels[1].transform.parent.rotation * Quaternion.Euler (RotationValue, this.steering , 0);//旋转

  BwheelModels[0].transform.rotation = BwheelModels[0].transform.parent.rotation * Quaternion.Euler (RotationValue, 0, 0);//旋转

  BwheelModels[1].transform.rotation = BwheelModels[1].transform.parent.rotation * Quaternion.Euler (RotationValue, 0, 0);//旋转

  RotationValue += rpm * ( 360f/60f ) * Time.deltaTime;

  }

  #endregion

更多内容,请访问【狗刨学习网】unity极致学院

声明:此篇文档时来自于【狗刨学习网】社区-unity极致学院,是网友自行发布的Unity3D学习文章,如果有什么内容侵犯了你的相关权益,请与官方沟通,我们会即时处理。

时间: 2024-10-05 09:43:47

Unity3d 赛车车辆各类性能算法的相关文章

数据结构-各类排序算法总结[结局]

各类排序算法总结 五.分配类排序->基数排序: 基数排序是一种借助于多关键码排序的思想,是将单关键码按基数分成"多关键码"进行排序的方法.基数排序属于"低位优先"排序法,通过反复进行分配与收集操作完成排序. 对于数字型或字符型的单关键字,可以看成是由多个数位或多个字符构成的多关键字, 此时可以采用这种"分配-收集"的办法进行排序,称作基数排序法.其好处是不需要进行关键字间的比较. 例如:对下列这组关键字{278, 109, 063, 930

数据结构-各类排序算法总结[续]

各类排序算法总结 三.交换类排序[接上] 2.快速排序 快速排序是通过比较关键码.交换记录,以某个记录为界(该记录称为支点),将待排序列分成两部分.其中,一部分所有记录的关键码大于等于支点记录的关键码,另一部分所有记录的关键码小于支点记录的关键码.我们将待排序列按关键码以支点记录分成两部分的过程,称为一次划分.对各部分不断划分,直到整个序列按关键码有序. 如果每次划分对一个元素定位后,该元素的左侧子序列与右侧子序列的长度相同,则下一步将是对两个长度减半的子序列进行排序,这是最理想的情况! [算法

(转)各类排序算法总结

各类排序算法总结 一. 排序的基本概念 排序(Sorting)是计算机程序设计中的一种重要操作,其功能是对一个数据元素集合或序列重新排列成一个按数据元素某个项值有序的序列. 有 n 个记录的序列{R1,R2,…,Rn},其相应关键字的序列是{K1,K2,…,Kn},相应的下标序列为1,2,…,n.通过排序,要求找出当前下标序列1,2,…, n 的一种排列p1,p2, …,pn,使得相应关键字满足如下的非递减(或非递增)关系,即:Kp1≤Kp2≤…≤Kpn,这样就得到一个按关键字有序的记录序列{R

数据结构-各类排序算法总结

各类排序算法总结 一. 排序的基本概念 排序(Sorting)是计算机程序设计中的一种重要操作,其功能是对一个数据元素集合或序列重新排列成一个按数据元素某个项值有序的序列. 有 n 个记录的序列{R1,R2,-,Rn},其相应关键字的序列是{K1,K2,-,Kn},相应的下标序列为1,2,-,n.通过排序,要求找出当前下标序列1,2,-, n 的一种排列p1,p2, -,pn,使得相应关键字满足如下的非递减(或非递增)关系,即:Kp1≤Kp2≤-≤Kpn,这样就得到一个按关键字有序的记录序列{R

unity3d 赛车游戏——复位点检测

一直没有时间写博客 昨天我的CarWaypoints插件也告一段落了 今年没回家,过年就我一个人 挺无聊的,那就休息一天写几篇博客吧 我的代码可能很少,但是思路很重要 希望不懂的朋友别只copy代码 赛车游戏的话赛车难免会冲出跑道.掉入水坑.卡在障碍物上....等情况 那么问题来了,遇到这些情况怎么办呢? 玩家玩得好好的,难道就因为遇到这些情况要退出游戏重新进入吗? 那当然是不现实的,要是我的话果断卸载游戏 还要骂一句做游戏的人是脑残啊 我想你不希望玩家骂你是脑残吧,哈哈哈 新技能,赶快GET起

数据结构—各类‘排序算法’实现(上)

数据结构中的排序算法分为比较排序,非比较排序.比较排序有插入排序.选择排序.交换排序.归并排序,非比较排序有计数排序.基数排序.下面是排序的具体分类: 1.直接排序 主要思想:使用两个指针,让一个指针从开始,另一个指针指向前一个指针的+1位置,两个数据进行比较 void InsertSort(int* a, size_t size) {      assert(a);      for (size_t i = 0; i < size - 1; i++)      {           int 

php各类hash算法长度及性能

Hash结果如下 <?php $data = "hello world"; foreach (hash_algos() as $v) { $r = hash($v, $data, false); printf("%-12s %3d %s\n", $v, strlen($r), $r); } ?> md2 32 d9cce882ee690a5c1ce70beff3a78c77 md4 32 aa010fbc1d14c795d86ef98c95479d17

各类排序算法总结

各类排序实现代码: 1 #include <stdio.h> 2 #include <math.h> 3 #include <queue> 4 #include <vector> 5 #include <stack> 6 #include <map> 7 #include <string> 8 #include <cstring> 9 #include <algorithm> 10 #include

Unity3D 2D游戏中寻径算法的一些解决思路

需求 unity3d的3d开发环境中,原生自带了Navigation的组件,可以很便捷快速的实现寻路功能.但是在原生的2d中并没有相同的功能. 现在国内很多手机游戏都有自动寻路的功能,或者游戏中存在一些例如机器人.npc等,都需要自动寻路的功能. 我需要实现的功能类似于当年FC游戏中淘金者的运动方式.游戏中有淘金者.敌人,可移动,不可移动区域,只能沿着直线的向前向后或者向上向下. 思路 unity3d中也有一些2d寻路的插件.例如A Pathfinding Project Pro和NavMesh