基于Unity3D的自动寻路车辆模拟

案例展示

 

1、开发背景

仿真测试可以在开发的早期,在即便实际处理器控制器和实车都没有ready的情况下——依然能对算法进行测试,尽早发现bug,尽早解决bug,而不把过多的bug带到实车测试中去。

2、开发工具的基本使用

本项目开发工具选择Unity3D(2020.3.30f),开发语言选择C#

3、项目准备工作:导入素材搭建场景

选取我们需要的场景和车的模型,这里我们可以使用unity自带的Asset Store

 在商店页面搜索Car,筛选中选择免费

 这里会给我们展示出其他用户提前创建好的模型,同理搜索Road筛选出我们需要的道路。

首先导入我们的道路素材

在Hierarchy中创建一个空物体,命名为Car,Transform中,Reset位置,从我们的素材中导入汽车的预制体作为子物体。

这里需要注意的是,我们的赛车模型,主要是靠车轮碰撞器来进行移动,所以我们需要将赛车的预制体模型拆分为汽车本体以及WheelMesh

首先结储预制体子物体的关联,将四个车轮放置在WheelMesh下。

继续创建一个空物体命名为WheelColider车轮碰撞体。

 4、摄像机跟随

摄像机跟随在该项目中有两种方式,最简单的方式就是直接将摄像机拖动到Car物体上,但是这样造成的问题是摄像机的移动不会十分平滑。

为了实现更加真实的场景,我们需要方式二对相机挂载脚本。

创建Folleow脚本,思路如下:

先确认好,相机初始合适的第三人称视角位置。

首先创建一个目标位置的变量,该变量通过查找标签为Player的物体获取位置。

定义一个临时变量,作为相机和车的固定距离。再定义一个临时变量用于存放摄像机位置。

固定位置变量(按照初始合适视角定义),摄像机位置等于车的位置加上固定距离。

最后让摄像机看着汽车尾部即可。

注意:这里需要将本地坐标转为世界坐标。

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

public class Follow : MonoBehaviour
{
    private Transform targetPos;//跟随的目标
    private Vector3 offsetPos;//固定位置
    private Vector3 tempPos;//临时变量
    // Start is called before the first frame update
    void Start()
    {
        offsetPos = new Vector3(0,3,-6);
        targetPos = GameObject.FindGameObjectWithTag("Player").transform;
        
    }

    // Update is called once per frame
    void FixedUpdate()
    {
        tempPos = targetPos.position + targetPos.TransformDirection(offsetPos);
        transform.position = Vector3.Lerp(transform.position, tempPos, Time.fixedDeltaTime * 3);
        transform.LookAt(targetPos);
    }
}

 5、车轮碰撞体的添加

首先为Car添加Rigidbody(刚体组件)在这里要注意车的质量要符合实际情况,否则会抖动或者被弹飞,Boxcolider(碰撞体组件),这里初始的Boxcolider比较小,范围不够大,我们需要手动编辑一下包含车头车尾即可,不要碰撞到轮胎

 在WheelColider的空物体中创建LF,添加wheel Colider组件,并调整到左前轮的位置和轮胎大小一致即可

 同理为四个轮子都添加WheelColider命名为LF、RF、LB、RB。

 注意:一定要保持WheelColider和轮胎高度的一致否则会造成抖动。

6、控制赛车的移动

创建一个脚本挂载到我们的车上,思路如下:

创建我们的车轮模型变量WheelMesh,创建WheelColider变量,定义最大角度,最大扭矩。

使用键盘操作类,

 1.Vertical                        对应键盘上面的上下箭头,当按下上或下箭头时触发

 2.Horizontal                    对应键盘上面的左右箭头,当按下左或右箭头时触发

float v = Input.GetAxisRaw("Vertical");

float v=h = Input.GetAxisRaw("Horizontal ");

按下向上方向键,返回值为1.0

按下向下方向键,返回值为0,继续按下返回值为-1.0
当在游戏运行的时候,按下你设置好的键盘就会返回 1和-1这两个值

遍历车轮碰撞体中每一个车轮最大扭矩×h即可实现车的前进和后退。

转弯的前轮是遍历车轮碰撞体中前两个车轮,让前两个车轮转动v×最大角度

6、赛车的车轮转动效果

赛车的车轮在运动的过程中需要实现滚动的效果。遍历模型中的每一个车轮,所有轮子都绕着X轴旋转,rpm为转速。
 

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

public class Control : MonoBehaviour
{
    // Start is called before the first frame update
    private MeshRenderer[] wheelMesh;
    private WheelCollider[] wheel;
    private float maxAngle;//转弯最大角度
    private float maxToque;//扭矩
    private float h, v;
    void Start()
    {
        maxAngle = 30;
        maxToque = 1000;
        wheelMesh = transform.GetChild(0).GetComponentsInChildren<MeshRenderer>();
        wheel = transform.GetChild(1).GetComponentsInChildren<WheelCollider>();
        
    }

    // Update is called once per frame
    void Update()
    {
        h = Input.GetAxis("Horizontal");
        v = Input.GetAxis("Vertical");
        if (Mathf.Abs(h) == 0 && Mathf.Abs(v) == 0) return;
        Move();      
    }
    private void Move()//车辆移动
    {
        
        for (int i = 0; i < 2; i++)
        { 
            wheel[i].steerAngle = h * maxAngle;
        }
        foreach (var o in wheel)
        {
            o.motorTorque = maxToque * v;

        }
        for (int i = 0; i < 4;i++)
        {
            wheelMesh[i].transform.localRotation = Quaternion.Euler(wheel[i].rpm*360/60,wheel[i].steerAngle,0);

        }
    }
}

7、实现赛车的Astar寻路导航

 AStar算法是一种静态路网中求解最短路径最有效的直接搜索方法。在包含各种障碍物的地图中,为游戏角色的移动,寻找一条到目标地点最短路径。

AStar的核心在于将游戏背景分为一个又一个格子,每个格子有自己的靠谱值,然后通过遍历起点的格子去找到周围靠谱的格子,接着继续遍历周围…… 最终找到终点。

使用Unity自带的Navigation寻路组件,新建一个空物体作为我们的目的地,将Road设置为静态。

在导航栏中选择Windows->Navigation打开 Bake 烘培,蓝色部分即为我们烘培的可行使区域。

 为Car添加NavMeshAgent组件。

 为了实现寻路轨迹展示,添加LineRenderer组件,用于在Game视图中画线段。

首先需要设置Materials否则材质会丢失。

 新建一个Navigation的C#脚本挂载到Car上,并选择好我们的目的地。

using UnityEngine;
using UnityEngine.AI;
using System.Collections;

public class Navigation : MonoBehaviour
{

	public Transform TargetObject = null;

	void Start()
	{
		if (TargetObject != null)
		{
			GetComponent<NavMeshAgent>().destination = TargetObject.position;
		}
	}
	void Update()
	{

	}
}

猜你喜欢

转载自blog.csdn.net/weixin_49305883/article/details/123817390