常见寻路方式

Unity常用的寻路方式主要有以下几种:

  • 路点寻路(WayPoint)
  • 单元格寻路(Grid)
  • 导航系统(Navigation)
  • A*寻路
  • VO
  • RVO
  • ORCA

本章主要针对Unity自带的导航系统(Navigation)进行概述,后续会对其他各类进行详细的讲解。

路点寻路(WayPoint)

路点寻路就是在地图上指定一些路点,让角色在路点之间移动。常用于一些固定路线的敌人或物体。

优点:路点寻路的优点是实现起来比较简单,且占用资源少、计算开销低。路线容易查看和编辑。
缺点:路线完全固定(有时会显得很僵硬),每个路点都要人为设定。容易出现绕路、走回头路和被障碍物卡住的情况。

单元格寻路(Grid)

单元格寻路就是把地图分隔为多个格子,形成一个矩阵,每个格子标注是否可移动,角色在可移动的格子内寻路。有点类似于战棋类游戏的走格子。

优点:实现简单,路线可控,障碍物可控。
缺点:单元格密度大则开销大,单元格密度小则行动很僵硬。经常出现90°转角的情况,需要做平滑处理。不适合做3D游戏。

导航系统(Navigation)

Unity自带了一套导航系统。导航系统允许使用从场景几何体自动创建的导航网格来创建可在游戏世界中智能移动的角色。动态障碍物可让您在运行时更改角色的导航,而网格外链接 (Off-Mesh Link) 可让您构建特定动作,如打开门或从窗台跳下。本部分将详细介绍 Unity 的导航系统和寻路系统。
在这里插入图片描述

导航系统核心模块

  • 导航网格( Navigation Mesh,缩写为 NavMesh),是一种数据结构,用于描述游戏世界的可行走表面,并允许在游戏世界中寻找从一个可行走位置到另一个可行走位置的路径。该数据结构是从关卡几何体自动构建或烘焙的。
  • 导航网格代理 (NavMesh Agent) 组件可帮助您创建在朝目标移动时能够彼此避开的角色。代理使用导航网格来推断游戏世界,并知道如何避开彼此以及移动的障碍物。
  • 网格外链接 (Off-Mesh Link) 组件允许您合并无法使用可行走表面来表示的导航捷径。例如,跳过沟渠或围栏,或在通过门之前打开门,全都可以描述为网格外链接。
  • 导航网格障碍物 (NavMesh Obstacle) 组件可用于描述代理在世界中导航时应避开的移动障碍物。由物理系统控制的木桶或板条箱便是障碍物的典型例子。障碍物正在移动时,代理将尽力避开它,但是障碍物一旦变为静止状态,便会在导航网格中雕刻一个孔,从而使代理能够改变自己的路径来绕过它,或者如果静止的障碍物阻挡了路径,则代理可寻找其他不同的路线。

工作原理

导航系统需要自己的数据来表示游戏场景中的可行走区域。可行走区域定义了代理可在场景中站立和移动的位置。在 Unity 中,代理被描述为圆柱体。可行走区域是通过测试代理可站立的位置从场景中的几何体自动构建的。然后,这些位置连接到场景几何体之上覆盖的表面。该表面称为导航网格(简称 NavMesh)。
在这里插入图片描述
导航网格将该表面存储为凸多边形。凸多边形是一种有用的表示,因为我们知道多边形内的任意两点之间没有障碍物。除了多边形边界之外,我们还存储有关哪些多边形彼此相邻的信息。这使我们能够推断整个可行走区域。

寻路

要寻找场景中两个位置之间的路径,我们首先需要将起始位置和目标位置映射到各自最近的多边形。然后,我们从起始位置开始搜索,访问所有邻居,直到我们到达目标多边形。通过跟踪被访问的多边形,我们可以找出从起点到目标的多边形序列。一种寻路的常用算法是 A*(发音为“A star”),这也是 Unity 使用的算法。
在这里插入图片描述

跟随路径

在这里插入图片描述
描述从起点到目标多边形的路径的多边形序列称为“走廊”(corridor)。代理将始终朝着走廊的下一个可见拐角移动,直至到达目标。如果一个简单游戏只有一个代理在场景中移动,可一次性找出走廊的所有拐角,并推动角色沿着连接拐角的线段移动。

在多个代理同时移动的情况下,它们需要在避开彼此时偏离原始路径。试图使用由线段组成的路径来纠正这种偏差很快变得非常困难并且容易出错。

在这里插入图片描述
由于每一帧中的代理移动距离非常小,我们可以使用多边形的连接来修复走廊,以防我们需要稍微绕道而行。然后,我们快速找到下一个需要抵达的可见拐角。

躲避障碍物

在这里插入图片描述
转向逻辑将采用下一个拐角的位置并基于该位置计算出到达目标所需的方向和速度。使用所需的速度移动代理可能会导致与其他代理发生碰撞。

障碍躲避系统将选择新的速度,该速度可平衡“代理在所需方向上移动”和“防止未来与其他代理及导航网格边缘发生碰撞”这两个问题。Unity 采用倒数速度障碍物 (RVO) 来预测和防止碰撞。

移动代理

最后在转向和障碍躲避之后计算最终速度。在 Unity 中使用简单的动态模型来模拟代理,该模型还考虑了加速度以实现更自然和平滑的移动。

在此阶段,您可以将速度从模拟的代理提供给动画系统,从而使用根运动移动角色,或让导航系统处理该问题。

使用任一方法移动代理后,模拟代理位置将移动并约束到导航网格。最后这一小步对于实现强大的导航功能非常重要。

全局和局部

在这里插入图片描述
关于导航需要了解的最重要事项之一是全局和局部导航之间的区别。

全局导航用于在整个世界中寻找走廊。在整个世界中寻路是一项代价高昂的操作,需要相当多的处理能力和内存。

描述路径的多边形的线性列表是用于转向的灵活数据结构,并可在代理的位置移动时进行局部调整。局部导航试图确定如何有效移动到下一个拐角而不与其他代理或移动对象发生碰撞。

障碍物的两种情况

许多导航应用需要其他类型的障碍物而不仅仅是其他代理。这些障碍物可能是射击游戏中的常规板条箱和木桶,或者是车辆。可使用局部障碍躲避或全局寻路功能来应对障碍物。

当障碍物为移动状态时,最好使用局部障碍躲避功能进行处理。这样,代理可预测性地避开障碍物。当障碍物变为静止状态并可认为其阻挡了所有代理的路径时,障碍物应该影响全局导航,即导航网格。

更改导航网格称为“雕刻”(carving)。该过程将检测障碍物的哪些部分会接触导航网格并在导航网格中雕刻孔洞。此操作的计算成本十分高昂,因此这也是应该使用碰撞躲避功能来处理移动障碍物的另一个充分理由。
在这里插入图片描述
局部碰撞躲避功能也常用于绕过稀疏分散的障碍物。由于算法是局部的,因此它只考虑即将发生的碰撞,并且不能绕过陷阱或处理障碍物挡路的情况。这些情况可使用雕刻技术来解决。

描述网格外链接

在这里插入图片描述
导航网格多边形之间的连接是使用寻路系统内的链接描述的。有时需要让代理在不可步行位置进行导航,例如,跳过围栏或穿过关闭的门。这些情况需要知道动作的位置。

可使用网格外链接来注释这些动作;此类链接会告诉寻路器 (pathfinder) 存在一条通过指定链接的路线。稍后在跟随路径时可访问此链接,并可执行特殊动作。

前面是照搬手册中的介绍,下面上点干货。

Navigation实际应用

准备场景

我们模拟一个塔防游戏,先画一张简图。
在这里插入图片描述
然后使用MTE插件创建一张简易的地图(暂未完成),截图如下:
在这里插入图片描述
注意:关于MTE的使用请参考另外一篇文章:【Unity】Unity插件:地形编辑器MTE(Mesh Terrain Editor)

下载 Navigation

因为Unity中的 Navigation 不是自带的,需要我们到包管理器中去下载。步骤如下:

  1. 打开包管理器: Window --> Package Manager;
  2. 点击添加包按钮;
    在这里插入图片描述
  3. 输入包名 com.unity.ai.navigation 后点击 Add 按钮。
    在这里插入图片描述

等待一会,Navigation 的包就下载好了。

添加代理(Nav Mesh Agent)

我们在场景中建立一个空物体,下面放一个 Cube ,然后给它添加导航代理(Nav Mesh Agent),这个代理让导航系统能够帮助我们进行角色移动。
在这里插入图片描述

设置代理

代理设置内容都很简单,先根据角色的大小,通过 Obstacle Avoidance 设置角色的半径、高度、质量等。然后通过 Steering 设置角色的移动速度、角速度、加速度等。如下图:
在这里插入图片描述

烘焙路网

设置好代理后我们进行路面的烘焙。先打开烘焙窗口: Window --> AI --> Navigation 。
在这里插入图片描述

在 Bake 选项卡中点击 Bake 按钮进行烘焙。
在这里插入图片描述

烘焙结果如下:
在这里插入图片描述

创建终点

在场景中创建一个胶囊模型,更名为 Cristal 。
在这里插入图片描述

添加代码

创建一个名为 NavTest 的脚本,具体代码如下:

using UnityEngine;
using UnityEngine.AI;

/// <summary>
/// 寻路系统
/// </summary>
public class NavTest : MonoBehaviour
{
    private NavMeshAgent agent;
    public Transform target;

    private void Start()
    {
        agent = this.GetComponent<NavMeshAgent>();

    }

    private void Update()
    {
        if (agent != null)
        {
            bool flag = agent.SetDestination(target.position);
        }
    }
}

使用脚本

将脚本给到刚才创建的Cube的父对象上,然后将终点的 Cristal 拖拽到脚本的 Target 属性中。
在这里插入图片描述

运行效果

先看看固定视角的寻路。
在这里插入图片描述
再看看角色视角的寻路。
在这里插入图片描述
到这里一个简单的寻路功能就做好了。

其实除了 Unity 自带的 Navigation 功能外,还有一些插件可以使用。

寻路插件

在Unity Asset Store中也有很多寻路插件可以使用,其原理也大部分与其他三种寻路方式相仿,我们可以自己下载下来试试。
在这里插入图片描述

关于 Navigation 中代理代理及障碍物的详细讲解,请参考下一篇文章:【Unity】导航基本组件:Nav Mesh Agent、Off Mesh Link、Nav Mesh Obstacle


更多内容请查看总目录【Unity】Unity学习笔记目录整理

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐