Unity让一辆越野车沿着指定路径自动行驶(非手动操作)

这篇具有很好参考价值的文章主要介绍了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<Transform> nodes = new List<Transform>();
    
    private void OnDrawGizmosSelected()
    {
        Gizmos.color = lineColor;
        
        Transform[] pathTransforms = GetComponentsInChildren<Transform>();
        
        for(int i = 0; i < pathTransforms.Length; i++)
        {
            if(pathTransforms[i] != transform)
            {
                nodes.Add(pathTransforms[i]);
            }
        }
        for(int i = 0; i < nodes.Count; i++)
        {
            Vector3 currentNode = nodes[i].position;
            Vector3 previousNode = Vector3.zero;
            if(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);
    }
}

将该脚本挂载在一个空的游戏物体(路径)上并且为该游戏物体创建几个子物体作为路径点,得到的效果如下图所示:

 unity实现小车按固定轨迹自动移动,unity,游戏引擎,汽车

如果按照步骤并没有显示出路线,那么请你打开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也是可以的,总之注意碰撞器不要把车全部包裹起来了。可以参照下图

unity实现小车按固定轨迹自动移动,unity,游戏引擎,汽车

为了让车动起来,需要给每个车轮添加上车轮碰撞器wheel Collider,如下图所示,将汽车模型的车轮放在wheel组中,在创建一个wheelColliders存放四个轮胎的wheel collider,具体就是在wheelCollider这个组中创建四个空物体,在这四个空物体上添加wheel Collider组件,调整这四个空物体的位置让他们与轮胎重合,如下图所示(我这里隐藏了车身方便观察)

unity实现小车按固定轨迹自动移动,unity,游戏引擎,汽车

上图中绿色线条就是wheel Collider的位置。Wheel Collider的参数请根据自己的车辆适当的调整,各个参数的意思请自行查阅Unity文档。

PS:调整的时候可以将场景调成2D。

汽车自动转弯

这里主要实现让汽车可以在不在同一条直线上的两个路径点直接转弯。unity实现小车按固定轨迹自动移动,unity,游戏引擎,汽车

注意横轴是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<Transform> nodes;
    //路径点索引值
    private int currentIndex = 0;
    

    //车轮碰撞器
    public WheelCollider LF;  //左前轮
    public WheelCollider RF;  //右前轮
    
    private float targetSteerAngle; //汽车轮胎实际的转角

    private void Start()
    {
        Transform[] pathTransfroms = path.GetComponentsInChildren<Transform>();
        nodes = new List<Transform>();
        
        for(int i = 0; i < pathTranforms.Length; i++ )
        {
            if(pathTransfroms[i] != path.transform)
            {
                nodes.Add(pathTransforms[i]);
            }
        }
    }

    
    
    private void FixUpdate()
    {
        ApplySteer();
    }

    private void ApplySteer()
    {
        //根据车的位置和路径点的位置坐标计算出相对向量
        Vector3 relativeVector=transform.InverseTransformPoint(nodes[currentIndex].position);
        //计算轮胎的实际转角
        float newSteer = (relativeVector.x / relativeVector.magnitude) * maxSteerAngle;
        targetSteerAngle = newSteer;
        //将实际转角运用到左前轮和右前轮的转角
        LF.steerAngle = targetSteerAngle;
        RF.steerAngle = targetSteerAngle;
    }
}

在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就是(1,0,2),newSteer就是1除以根号下1加4等于根号5分之1,在将这个值乘以最大转向角得出车轮的转向角,注意这只是其中一帧的情况,随着车的位置的不断变化,车轮的实际转角也在不断变化。

看一下实际的图片:

unity实现小车按固定轨迹自动移动,unity,游戏引擎,汽车

 可以看到车轮是指向路径点的(到这一步你的车并没有转动,但是当你点击查看车轮碰撞器时,你可以发现车轮碰撞器是转向的,车轮没转向是因为还没有同步车轮碰撞器和车轮的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 < maxSpeed)
        {
            LF.motorTorque = maxMotorTorque;
            RF.motorTorque = maxMotorTorque;
        }
        else
        {
            LF.motorTorque = 0;
            Rf.motorTorque = 0;
        }
    } 
    private void CheckNextWaypointDistance()
    {
        //判断汽车当前的距离和路径点之间的距离
        ifVector3.Distance(new Vector3(transform.position.x,0,transform.position.z),
                           new Vector3(nodes[currentIndex].position.x,0,                      
                           nodes[currentIndex].position.z))<0.5f)
        {
            //如果已经到达了最后一个路径点,那么将索引值置0,绕圈
            if(currentIndex == nodes.Count-1)
            {
                currentIndex = 0;
            }
            else
            {
                currentIndex++;
            }
        }
    }
}

Drive函数中先根据汽车的轮胎的rpm来计算汽车的速度,这里有个公式:

车速 = 2 * Π * 轮胎半径 * 车轮的rpm * 60 / 1000 

当汽车的速度小于最大速度时给予汽车车轮最大动力,这里是给了前面两个轮胎,也就是所谓的前驱。这样汽车就可以不断加速,当汽车到达最大速度,就将动力置0,这样汽车会减速,然后又被判定小于最大速度,无限套娃,从而最终让汽车以最大速度行驶。

 CheckNextWaypointDistance函数中,刚开始路径点索引值currentIndex为0,那么会计算汽车距离第一个路径点的距离,注意这里是将汽车位置和路径点的位置点的y值都置0再计算距离,因为不同的汽车他的中心点位置可能偏高也有可能偏低,路径点可能会在不同的高度,如果直接判断,那么无法确定距离小于某个值才是真正合理的值,这里直接将y值置0,就省去了判断正确的值。如果距离小于0.5f,那么就将索引值加一以指导汽车前往下一个路径点,如果已经到达最后的路径点,那么将索引值置0,让汽车返回第一个路径点以完成跑圈。

汽车稳定性的提升

如果你将车速设置的较大那么车辆在急转弯时会发生侧翻的情况(很符合显示(😓)),为了解决这一问题,可以改变车辆的重心。

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CarEngine : MonoBehaviour
{
    //.....    

    private Rigidbody rb;
    public Vector3 centorOfMass;

    void Start()
    {
        rb = GetComponent<Rigbody>();
        rb.centorOfMass = centerOfMass;
    }
    //.....
}

如代码所示,在Unity界面调整centorOfMass的值,将这个位置放在车的底部位置,我设置的是(0,-1,0),不要太离谱就行。设置得太低重心直接在地上就很幽默了。 

汽车的刹车 

汽车应该具有刹车功能。代码如下:

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CarEngine : MonoBehaviour
{
    //....

    //汽车动力
    public float maxBrakeTorque = 600f;  //刹车的制动力

    //刹车部分的设置
    public bool isBraking;
    public Texture2D textureNormal;     //不刹车时车灯的贴图
    public Texture2D textureBreaking;   //刹车时车灯的贴图
    public Renderer carRender;


    //....
    
    void FixedUpdate()
    {
        //...
        Brakking();
    }

    //...
    
    private void Drive()
    {
        //....
        //修改一下汽车行驶的条件
        if(currentSpeed < maxSpeed && !isBraking)
        //...
    }


    private void Braking()
    {
        if(isBraking)
        {
            carRender.material.mainTexture = textureBraking;
            //后轮刹车
            RB.brakeTorque = maxBrakeTorque;
            LB.brakeTorque = maxBrakeTorque;
        }
        else
        {
            carRender.material.mainTexture = textureNormal;
            RB.brakeTorque = 0;
            LB.brakeTorque = 0;
        }
    }
}

声明一个标志位isBraking,当该标志位为真时,让后轮采用最大制动力,同时修改了Drive函数中的if判断语句,当标志位为真时,汽车的前轮制动力也会被置为0,这样汽车就会停下来。

同时为了做出汽车刹车时后面的车灯亮起来的效果, 在刹车和不刹车的情况下更新车灯处的贴图。找出自己车模型中的车灯的贴图位置,有可能在整个贴图中,一般在模型的Texture文件夹下,找到之后用PS等作图软件打开它,然后更改车灯处贴图的颜色,让它更亮,比如我这里原来是暗红,更改之后让其变得更红,更改之后将其保存在相同的文件夹下,注意不要覆盖原来的图片,将其保存为一张新的图片。(PS:本人PS能力较差,看个效果吧,理解一下)

unity实现小车按固定轨迹自动移动,unity,游戏引擎,汽车

unity实现小车按固定轨迹自动移动,unity,游戏引擎,汽车

然后在Unity界面更改变量,将textureNormal设置为没改的图片,将textureBraking设置为修改后的图片。将carRender设置为车灯部分。

刹车部分的更改图片似乎没什么必要,但是这么一个小的例子将PS等绘图软件和Unity结合起来了,让我们当了一次美术。起始也可以在刹车时添加例子特效,让车灯更亮一些。

 汽车车轮的转动

做到这车轮并不会转动,下面来同步车轮和车轮碰撞器的状态

添加脚本CarWheel

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CarWheel : MonoBehaviour
{
    public WheelCollider targetWheel;
    private Vector3 wheelPosition = new Vector3();
    private Quaternion wheelRotation;

    void Update()
    {
        targetWheel.GetWorldPose(out wheelPosition,out wheelRotation);
        transform.position = wheelPosition;
        transform.rotation = wheelRotation;
    }
}

将该脚本挂载到每个轮胎上,注意不是车轮碰撞器上,然后在Unity界面将每个轮胎对应的车轮碰撞器拖到每个车轮负载脚本的targetWheel上。如下图所示

unity实现小车按固定轨迹自动移动,unity,游戏引擎,汽车

该脚本就是通过GetWorldPose方法去获取车轮碰撞器的位置和转动情况,然后把对应的值直接赋给相应的轮胎。

总结

完成到这汽车已经可以沿着指定路径行驶了,原视频还做了一个传感器来避障,这一部分放在下一篇文章中讲解。文章来源地址https://www.toymoban.com/news/detail-762109.html

到了这里,关于Unity让一辆越野车沿着指定路径自动行驶(非手动操作)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • Unity获取物体自身坐标轴的方向以及沿着该方向运动的方法

    有时候对于一个游戏对象,需要其沿着自身的坐标轴方向进行运动,那么首先如何获取自身的坐标轴方向? 获取自身的坐标轴方向可以通过transform组件进行获取(负方向加负号即可)  Vector3 moveDirection = transform.right;  获取自身的x轴的方向  Vector3 moveDirection = transform.forward;  获

    2024年02月12日
    浏览(16)
  • Unity Android 之 读取下载获取移动端 sdcard 路径下的指定文件夹的所有图片的几种方式的简单整理

    Unity Android 之 读取下载获取移动端 sdcard 路径下的指定文件夹的所有图片的几种方式的简单整理

    目录 Unity Android 之 读取下载获取移动端 sdcard 路径下的指定文件夹的所有图片的几种方式的简单整理 一、简单介绍 二、实现原理 三、注意事项 四、简单实现步骤 五、关键代码 附录: 一、不同平台使用宏区分路径加载 二、Unity3D中的资源路径 三、Unity3D各平台路径(包括手

    2024年01月19日
    浏览(77)
  • python -- 实现路径的匹配,剔除掉指定路径,并保存路径

    python -- 实现路径的匹配,剔除掉指定路径,并保存路径

    在处理nc数据时,由于部分数据在插值的过程中,存在过多的0值,使得在制作标签时该时刻的数据出现报错,但是对于一年的数据量来说,无关紧要,所以只是记录了出现报错的时刻的路径,方面在后续变量读取过程中进行剔除,报错后续文件的处理。 下面记录一下主要的代

    2024年02月10日
    浏览(6)
  • opencv imwrite()保存指定路径

    cpp为例 第32行为相对路径,当前工作目录为根目录(指VS的工程根目录、vscode打开的文件夹目录)。 也可以使用绝对路径,如 注: imwrite()不会创建文件夹 如上相对路径的例子中,理想情况下图片会保存到snapPhotos文件夹内。但若不存在snapPhotos文件夹,则 不会做任何事(也不

    2024年02月11日
    浏览(17)
  • anaconda新建虚拟环境于指定路径

    有两种方法: 普通的 conda create -n XXX 会直接把XXX这个包安装在home路径下,会占用默认系统分区很多存储空间。以笔者为例,笔者想把conda环境安装至 /scratch/reza/anaconda3/envs 下,而非 ~/.conda/envs/ . 如果需要指定虚拟环境的安装路径,需要加上 -p 参数,例如: 这样就会把anaconda装

    2024年02月16日
    浏览(11)
  • 【Matlab】加载路径下所有指定文件

    想用matlab加载路径下所有指定文件,比如加载一个路径下的所有png图像、txt文件等,网上查了一圈也不是很好用,解决了问题就分享一下。 dir函数 用到了dir函数,Matlab中的dir函数是可以列出指定文件夹中的所有文件和子文件夹`,白话说就是检索指定文件,把文件名、文件路

    2024年02月02日
    浏览(6)
  • 一辆新能源汽车的诞生之旅:比亚迪常州工厂探营

    一辆新能源汽车的诞生之旅:比亚迪常州工厂探营

    作为在新能源汽车领域首屈一指的国产品牌,比亚迪近年来可以说是捷报频传,高奏凯歌。 以比亚迪常州工厂为例,据介绍该工厂当初规划设计时定下的生产目标,是年产量能够达到20万辆。然而在2023年上半年,该工厂光是主要销往海外市场的海报和元PLUS这两款车型,就已

    2024年02月09日
    浏览(13)
  • 切换Anaconda的指定文件夹路径

    切换Anaconda的指定文件夹路径

    anaconda刚开始下载的时候,默认是在c盘,然后我们使用anaconda打开文件夹也是打开c盘的文件夹。如果想在指定文件夹打开anaconda,可以按照如下步骤来。  如果是这种情况可以进行如下操作    然后切换到指定目录(我一般放在d盘)  如果是这种情况,可以进行如下操作   最后

    2024年02月11日
    浏览(50)
  • 坦克 400 Hi4-T:用产品诠释越野新能源

    坦克 400 Hi4-T:用产品诠释越野新能源

    9 月 25 日,坦克 400 Hi4-T 正式上市,新车共推出两款车型配置,售价区间 27.98-28.98 万元。同时,坦克 400 Hi4-T 将上市及即交付。 权益方面,坦克 400 Hi4-T 共有七重好礼: 质保无忧:整车 5 年或 15 万公里整车质保; 保养无忧:首任车主 5 年至高 5 次免费保养; 网联无忧:终身

    2024年02月07日
    浏览(7)
  • IOS开发:指定路径创建新文件夹

    很遗憾,网上所有相关的博文都是这样写的。但使用最新的swift5.8如此操作,会出现以下报错: Cannot use instance member ‘documentDirectoryURL’ within property initializer; property initializers run before ‘self’ is available 以及: Expression expected 出现第一个错误的原因是,在swift中的一个结构体在

    2024年02月14日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包