Unity让一辆越野车沿着指定路径自动行驶(非手动操作)
本文根据油管作者EYEmaginary原视频创作,视频地址是Car AI Tutorial #1 (Unity 5 ) - Make the Path - YouTube
本文主要做的是对视频中的内容进行分析和讲解,如果各位有时间请去看原视频。以下内容如有错误请留言评论,欢迎理性讨论。
本文详细介绍视频中的内容,具体实现的效果可以看我录的这个视频
汽车沿指定路径行驶-CSDN直播
制作路径
为了让汽车沿着指定路径行驶,首先要创造出一条路径,该路径由各个路径点组成,汽车会在相邻的路径点之间完成转弯。首先创造出一个脚本Path,内容如下
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Path : MonoBehaviour { public Color lineColor; //创建一个数组来保存路径点 private List nodes = new List(); private void OnDrawGizmosSelected() { Gizmos.color = lineColor; Transform[] pathTransforms = GetComponentsInChildren(); for(int i = 0; i 0) { previousNode = nodes[i-1].position; } else if(i == 0 && nodes.Count > 0 ) { previousNode = nodes[nodes.Count - 1].position; } } Gizmos.DrawLine(currentNode,previousNode); Gizmos.DrawWireSphere(currentNode,1f); } }
将该脚本挂载在一个空的游戏物体(路径)上并且为该游戏物体创建几个子物体作为路径点,得到的效果如下图所示:
如果按照步骤并没有显示出路线,那么请你打开Line Color将颜色值的A值(这个值应该是不透明度)调到最大
下面来介绍这个脚本。脚本的内容比较简单,主要是创建一个Transform类型的数组nodes,然后利用OnDrawGizmosSelected函数(使用这个函数是为了当我们点选Path时,才会去绘制线条,这是为了方便观察)绘制出相邻两个路径点之间的连线并且在每个路径点上画了一个线条球。
首先通过GetComponentsInChildren方法将Path下的子物体(每个路径点)的Transform保存在一个新创建的数组pathsTransform中,然后将利用第一个for循环将pathsTransform中的每个元素一次添加到nodes这个数组中去。该for语句中有一个if判断,原因是GetComponentsInChildren会获取当前物体以及其所有子物体的Transform组件,如果不加if判断直接将该函数返回的结果放在pathTransform数组中,那么Path父物体的Transform会被放在该数组中的第一个位置。
第二个for循环主要是在获取路径点中相邻的两个,定义了两个Vector3变量,currentNode表示当前节点,previousNode表示当前节点的上一个节点。当索引值i大于0时,previousNode就等于nodes[i-1],如果i等于0并且路径点大于1个,previousNode就是路径点中的最后一个节点。这样做是为了将所有的路径点围成一个圈。
最后利用DrawLine在currentNode和previousNode中画线,用DrawWireSphere在每个节点上画一个线条球。
添加车轮碰撞器
在添加之前请先为汽车添加RigidBody组件,否则是不会有wheel Collider出现的,其次请将RigidBody组件的Mass调大,否则车会飞起来,这个值为1000最好,最后请为车身添加一个碰撞器,注意碰撞器不要把车轮完全包含在内了,可以点击汽车模型中的车身部分,添加一个Mesh Collider,将Convex勾选上,这样系统会自动为车身添加合适大小的碰撞器,用Box Collider也是可以的,总之注意碰撞器不要把车全部包裹起来了。可以参照下图
为了让车动起来,需要给每个车轮添加上车轮碰撞器wheel Collider,如下图所示,将汽车模型的车轮放在wheel组中,在创建一个wheelColliders存放四个轮胎的wheel collider,具体就是在wheelCollider这个组中创建四个空物体,在这四个空物体上添加wheel Collider组件,调整这四个空物体的位置让他们与轮胎重合,如下图所示(我这里隐藏了车身方便观察)
上图中绿色线条就是wheel Collider的位置。Wheel Collider的参数请根据自己的车辆适当的调整,各个参数的意思请自行查阅Unity文档。
PS:调整的时候可以将场景调成2D。
汽车自动转弯
这里主要实现让汽车可以在不在同一条直线上的两个路径点直接转弯。
注意横轴是X轴,纵轴是Z轴(因为车不可能在天上飞,所以忽略y轴)。假设(3,0,2)是汽车的位置点A,(4,0,4)下一个路径点B,车要想行驶到B点,他的前进方向应该是向量(3,0,2),接下来要让汽车的前进方向转变成(4,0,4)减去(3,0,2)也就是向量AB=(1,0,2)。通过这个向量可以发现如果这个向量的X值大于0,那么汽车应该向右转,小于应该向左转,等于0就不转。据此创建一个CarEngine脚本挂载到汽车上,内容如下
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; public class CarEngine : MonoBehaviour { //汽车的最大转向角度 public float maxSteerAngle; //获取路径 public Transform path; private List nodes; //路径点索引值 private int currentIndex = 0; //车轮碰撞器 public WheelCollider LF; //左前轮 public WheelCollider RF; //右前轮 private float targetSteerAngle; //汽车轮胎实际的转角 private void Start() { Transform[] pathTransfroms = path.GetComponentsInChildren(); nodes = new List(); for(int i = 0; i在Start函数中重新让汽车获取了路径点,这部分代码和Path部分是一样的,在Path中是为了绘制路线,在此脚本中是为了让汽车获取路径点。注意需要在脚本写完之后在Unity界面将Path这个游戏物体拖到该脚本的path上,同时请将上一部分调整好的wheel Collider放在LF和RF上。然后声明了一个函数ApplySteer(),该函数主要负责车轮的转向。在Unity中Vector3有一个方法InverseTransform,该方法可以获取两个点之间的相对向量,transform.InverseTransform(nodes[currentIndex].position);相当于求车目前的位置和下一个要到达的路径点之间的方向向量。得到该向量之后计算实际的转角,具体的逻辑是利用该相对向量的x值除以相对向量的模依次来获得一个值,该值在-1到1之间,然后用这个值乘以最大转向角得出车轮实际转向角,将其赋给targetSteer,再赋给左右轮的steerAngle(这里多用了一个targetSteer是为了后面的平滑)。
为了方便理解这里举个例子:假设车在某一帧的位置是(1,0,2),下一个路径点的位置是(4,0,4),那么此时relativeVector就是(3,0,2),newSteer就是1除以根号下9加4等于根号13分之1,在将这个值乘以最大转向角得出车轮的转向角,注意这只是其中一帧的情况,随着车的位置的不断变化,车轮的实际转角也在不断变化。
看一下实际的图片:
可以看到车轮是指向路径点的(到这一步你的车并没有转动,但是当你点击查看车轮碰撞器时,你可以发现车轮碰撞器是转向的,车轮没转向是因为还没有同步车轮碰撞器和车轮的Transform)。
汽车的移动
这一步将使汽车移动起来。
继续编写CarEngine脚本,如下所示
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; public class CarEngine : MonoBehaviour { //.... //轮胎碰撞器 public WheelCollider LB; //左后 public WheelCollider RB; //右后 //车轮动力部分 public float maxMotorTorque = 600f; //车轮最大动力 public float maxSpeed = 60f; //最大车速 public float currentSpeed; //汽车当前车速 private void FixedUpdate() { //... Drive(); CheckNextWayPointDistance(); } private void Drive() { //计算车速 currentSpeed = 2 * Mathf.PI * LF.radius * LF.rpm * 60 / 1000; //如果车速小于最大速度,那么给予车轮动力 if(currentSpeed
还没有评论,来说两句吧...