Unity 简单载具路线 Waypoint 导航

前言

在游戏开发和导航系统中,"waypoint" 是指路径中的一个特定位置或点。它通常用于定义一个物体或角色在场景中移动的目标位置或路径的一部分。通过一系列的 waypoints,可以指定复杂的移动路径和行为。以下是一些 waypoint 的具体用途:

  1. 导航和路径规划

    • Waypoints 用于定义角色或物体从一个位置移动到另一个位置的路径。
    • 例如,在游戏中,敌人可能会沿着一系列 waypoints 巡逻。
  2. 动画和过场

    • Waypoints 可用于定义相机或对象在场景中移动的路径。
    • 例如,在过场动画中,摄像机可能会沿着预定义的路径移动,以展示场景的不同部分。
  3. 人工智能(AI)行为

    • AI 角色可以使用 waypoints 来确定移动路径和行为模式。
    • 例如,AI 角色可以沿着一系列 waypoints 移动,以模拟巡逻或探索行为。
  4. 动态事件触发

    • Waypoints 可以用作触发点,当角色或物体到达某个 waypoint 时触发特定事件或行为。
    • 例如,玩家到达某个 waypoint 时,可能会触发一段对话或开始一个任务。

Unity 简单载具路线 Waypoint 导航

实现

假设我们有一辆载具,需要通过给定的数个路径点(waypoint)来进行移动和转向。

简单粗暴的一种方法是使用Animation动画系统来为载具制作做动画,但是这个方法的致命缺点是非常不灵活,一旦需要改动路径点和模型,几乎就得重新做动画。

好在,DOTween (HOTween v2) | 动画 工具 | Unity Asset Store插件的出现让脚本解决变得异常的简单快捷,这个插件能够完美解决各种各样的过场动画和事件调用:

DOTween (HOTween v2) (demigiant.com)https://dotween.demigiant.com/index.php

  1. 这里需要先事先安装一下DOTween,没什么难度,免费!

  2. 在场景中增加你的路径点,做一个父物体,然后为子物体增加多个路径点,这里每个路径点建议使用带有方向的模型或者图片,这样便于查看

  3. 为你的载具添加文末的脚本,并挂在载具上(如果你不想挂在载具上,那简单改一下代码的变量为你的期望物体上就行)。

  4. 在inspector中绑定好你需要的路径点集合的父物体(wayPointsParent变量),这里我在父物体上额外加了一个LineRender组件,用于后续连线效果:

  5. 运行程序!车子就会动起来了!调节每一个变量,让车子移动的更自然!
    每个变量都有它的意义,比如你可以规定整个路程的总时间、转一次弯需要多少时间,转弯的起始距离阈值以及每到一个waypoint的事件调用等等,并可以更具每个人的需要自行修改和拓展!

cs 复制代码
using System;
using UnityEngine;
using DG.Tweening;
using UnityEngine.Events;

/// <summary>
/// Author: Lizhenghe.Chen https://bunnychen.top/about
/// </summary>
public class CarMover : MonoBehaviour
{
    public LineRenderer wayPointsParent;
    [Header("Total move time in seconds")] public int totalMoveTime = 300;

    [Header("Rotation duration in seconds")]
    public float rotationDuration = 2;

    [Header("Distance threshold to start rotating")]
    public float rotationStartDistance = 1;

    [Header("Show line renderer")] public bool showLineRenderer = true;

    // Duration for each segment
    [SerializeField] private int moveDuration = 5;

    // Array of transforms for the car to move towards
    [SerializeField] private Transform[] waypoints;
    [SerializeField] private Transform currentWaypoint;
    [SerializeField] private int currentWaypointIndex;
    public UnityEvent onWaypointReached;
    private MaterialPropertyBlock _propBlock;
    private static readonly int BaseColor = Shader.PropertyToID("_BaseColor");
    private static readonly int EmissionColor = Shader.PropertyToID("_EmissionColor");

    private void OnValidate()
    {
        // Get the waypoints from the parent object, do not include the parent object itself
        if (wayPointsParent == null) return;
        waypoints = new Transform[wayPointsParent.transform.childCount];
        for (var i = 0; i < waypoints.Length; i++)
        {
            waypoints[i] = wayPointsParent.transform.GetChild(i);
        }

        //foreach waypoint, set the current waypoint to look at the next waypoint
        for (var i = 0; i < waypoints.Length - 1; i++)
        {
            waypoints[i].LookAt(waypoints[i + 1]);
        }

        moveDuration = totalMoveTime / waypoints.Length;
    }

    private void Start()
    {
        OnValidate();
        if (showLineRenderer) SetLineRenderer();
        SetWaypointsSequence();
        onWaypointReached.AddListener(SetPreviousWaypointColor);
    }

    private void SetWaypointsSequence()
    {
        // Create a new Sequence for movement
        var moveSequence = DOTween.Sequence();

        // Loop through the waypoints and append DOMove tweens to the moveSequence
        foreach (var waypoint in waypoints)
        {
            moveSequence.AppendCallback(() =>
            {
                currentWaypoint = waypoint;
                currentWaypointIndex = Array.IndexOf(waypoints, waypoint);
                onWaypointReached?.Invoke();
            });
            // Move to the waypoint
            moveSequence.Append(transform.DOMove(waypoint.position, moveDuration).SetEase(Ease.Linear));

            // Create a rotation tween that starts when the car is within the specified distance to the waypoint
            moveSequence.AppendCallback(() =>
            {
                // Start rotation when close to the waypoint
                if (Vector3.Distance(transform.position, waypoint.position) < rotationStartDistance)
                {
                    // make the rotation same to the waypoint's rotation
                    transform.DORotateQuaternion(waypoint.rotation, rotationDuration).SetEase(Ease.Linear);
                }
            });
        }

        // Optionally, set some other properties on the sequence
        moveSequence.SetLoops(0); // Infinite loop
        // moveSequence.SetAutoKill(false); // Prevent the sequence from being killed after completion
    }

    private void SetLineRenderer()
    {
        //set the line renderer's position count to the number of waypoints and set the positions to the waypoints' positions
        wayPointsParent.positionCount = waypoints.Length;
        for (var i = 0; i < waypoints.Length; i++)
        {
            wayPointsParent.SetPosition(i, waypoints[i].position);
        }
    }

    private void SetPreviousWaypointColor()
    {
        _propBlock ??= new MaterialPropertyBlock();

        if (currentWaypointIndex == 0) return;
        // Set the color of the current waypoint to green, and the next waypoint to red
        waypoints[currentWaypointIndex - 1].GetComponent<MeshRenderer>().GetPropertyBlock(_propBlock);
        _propBlock.SetColor(BaseColor, Color.green);
        _propBlock.SetColor(EmissionColor, Color.green);
        waypoints[currentWaypointIndex - 1].GetComponent<MeshRenderer>().SetPropertyBlock(_propBlock);
        waypoints[currentWaypointIndex - 1].gameObject.SetActive(false);
    }
}
相关推荐
!chen19 小时前
Unity颜色曲线ColorCurves
unity·游戏引擎
B0URNE19 小时前
【Unity基础详解】(4)Unity核心类:MonoBehaviour
unity·游戏引擎
AA陈超1 天前
虚幻引擎5 GAS开发俯视角RPG游戏 P06-29 属性信息委托
c++·游戏·ue5·游戏引擎·虚幻
AA陈超1 天前
虚幻引擎5 GAS开发俯视角RPG游戏 P06-31 映射标签到属性
c++·游戏·ue5·游戏引擎·虚幻
gshh__1 天前
SuperMap Hi-Fi 3D SDK for Unreal 使用蓝图接口加载多源数据
ue5·游戏引擎·supermap
小时候的阳光1 天前
Cocos Creator 和 Unity 3D 编辑界面字体样式大小调整
unity·cocos2d·字体大小
ellis19701 天前
Lua代码混淆-Prometheus方案教程
unity·lua
EQ-雪梨蛋花汤1 天前
【MRTK3踩坑记录】Unity 2022 中 MRTK3 Input Simulator 无法使用 WASD 控制相机的完整排查记录
数码相机·unity·游戏引擎
星夜泊客2 天前
Unity 游戏开发中的防御性编程与空值处理实践
unity·设计模式·游戏引擎
mit6.8242 天前
[无人机sdk] Open Protocol | 协议包构造&验证
游戏引擎·无人机·cocos2d