什么是导航寻路系统

Unity 中的导航寻路系统是能够让我们在游戏世界当中

让角色能够从一个起点准确的到达另一个终点

并且能够自动避开两个点之间的障碍物选择最近最合理的路径进行前往

Unity 中的导航寻路系统的本质

就是在 A 星寻路算法的基础上进行了拓展和优化

  1. 导航网格 (NavMesh) 的生成 — 要想角色能够在场景中自动寻路产生行进路径,那么必须得先有场景地形数据,导航网格生成就是生成用于寻路的地形数据

  2. 导航网格寻路组件 (NavMesh Agent)— 寻路组件就是帮助我们根据地形数据计算路径让角色动起来的关键

  3. 导航网格连接组件 (Off-Mesh Link)— 当地形中间有断层,想让角色能从一个平面跳向另一个平面,网格连接组件时关键

  4. 导航网格动态障碍物组件 (NavMesh Obstacle)— 地形中可能存在的可以移动或动态销毁的障碍物需要挂载的组件

导航网格生成

1.准备地形

2打开导航网格窗口

Window-->AI-->Navigation 打开 Unity 内置的导航网格生成窗口

Object 页签 — 设置参与寻路烘焙的对象

Bake 页签 — 导航数据烘焙页签,设置寻路网格具体信息

Areas 页签 — 导航地区页签,设置对象的寻路消耗

Agents 页签 — 代理页签,设置寻路代理信息

  • 参数相关

    • Ojbect 场景对象设置页签

      • Sceme Filter: 场景过滤器,配合Hierarchy窗口使用 All:显示场景上所有对象 MeshRenderers:显示挂载的网格渲染器的对象 Terrains:显示挂载了地形脚本的对象

      • Navigation Static 导航静态物体开关

      • Generate OffMeshLinks 生成网格连接点开关

      • Navigation Area 导航区域选择,配合Areas页签使用

    • Bake 导航数据烘焙页签

      • Agent Radius 代理半径 决定了烘焙边缘精确度 控制平台可行走区域和边缘可行走区域

      • Agent Height 代理高度 决定了烘焙高度精确度 控制拱桥是否可以穿越

      • Max Slope 最大坡度 斜坡度数 决定了斜坡是否可以行走

      • Step Height 最小楼梯高度 决定了台阶是否可以行走

      • Generated off mesh Links 生成非网格连接 两个分开的网格之间连接相关设置

        • Drop Height 掉落高度 可以从这个高度掉下来

        • Jump Distance 跳跃距离 决定不同平面上的跳跃距离

      • Advanced

        • Manual Voxel Size 手动设置立体像素大小

          • Voxel Size 开启前者后可以在这里设置立体像素大小 可以控制烘焙的准确度 立体像素大小描述了生成的导航网格表示场景几何体的准确程度

          • 注意:立体像素大小减半会使内存使用量增加4倍,构建时间也增加4倍 一般情况下不需要修改这个值 除非想要极其准确的导航网格

        • Min Region Area 最小区域面积 当面积小于此处值的导航网格区域会被移除

        • Height Mesh 高度网格构建开关 主要用来解决楼梯烘焙为斜坡时 希望在楼梯表面的位置准确的放置在楼梯平面上 而不是斜坡上 启用它会增加烘焙时间

    • Areas 导航地区页签 配合Object页签使用

      • Name 区域名字

      • Cost 寻路消耗

    • Agents 代理页签 用于配置寻路代理信息

导航网格寻路组件

导航网格寻路组件是用来干什么的

寻路组件的本质就是根据烘焙出的寻路网格信息

通过基于 A 星寻路的算法计算出行进路径让我们在该路径上移动起来

寻路组件参数相关

导航网格寻路组件

Nav Mesh Agent(导航网格代理人)

  • 参数相关

    • Agent Type 代理类型 配合Agents页签使用

    • Base Offset 基础偏移值,相对对象轴心点的高度偏移

    • Steering 移动设置

      • Speed 寻路时的最大移动速度(世界单位/秒)

      • Angular Speed 寻路时转身的最大旋转速度(度/秒)

      • Acceleration 最大加速度(世界单位/平方秒)

      • Stopping Distance 当靠近目标点多少距离时,停止运动

      • Auto Braking 自动制动(减速) 启用后,当到达目标时将减速,如果存在连续移动(比如巡逻移动)建议不要开启该选项

    • Obstacle Avoidance 避障设置

      • Radius 半径,用于计算障碍物和其它寻路对象之间的碰撞

      • Height 高度,通过头顶障碍物时用于计算高度间隙使用

      • Quality 障碍躲避品质,越高躲避障碍越准确,但是性能消耗较大,如果不想主动避开其它动态障碍,可以设置为无,则只会解析碰撞

      • Priority 优先级,0~99, 避障时,数字较小的障碍物表示较高的优先级 优先级低的会忽略避障

    • Path Finding 路径寻找规则

      • Auto Traverse OffMesh Link 是否开启自动遍历网格外的其它网格连接 如果要自定义判断,则关闭此功能

      • Auto Repath 是否开启自动重设路线 如果开启,当到达路径后段时会再次尝试寻路,当没有到达目标的路径时,会生成一条到达与目标位置最近的可达点

      • Area Mask 寻路时,考虑的区域 如果寻路时不想考虑某些区域,则取消选中 塔防游戏中常见的功能

代码控制

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.AI;

public class lesson20 : MonoBehaviour
{
    private NavMeshAgent nma;
    // Start is called before the first frame update
    void Start()
    {
        nma = GetComponent<NavMeshAgent>();

        //自动寻路设置目标点
        //nma.SetDestination();

        //停止寻路
        //nma.isStopped = true;

        //面板参数相关 速度 加速度 旋转速度
        //nma.speed
        //nma.acceleration
        //nma.angularSpeed

        //当前是否有路径
        //bool temp = nma.hasPath;

        //代理目标点的设置和获取
        //nma.destination

        //当前路径
        //nma.path

        //路径是否在计算中
        //if (nma.pathPending) { }

        //路径状态
        //nma.pathStatus

        //是否更新位置
        //nma.updatePosition = true;

        //是否更新角度
        //nma.updateRotation = true;

        //代理速度
        //nma.velocity

        //计算生成路径
        NavMeshPath path = new NavMeshPath();
        if (nma.CalculatePath(Vector3.zero, path))
        {
            Vector3[] temp = path.corners;
            //路径上的点
        }

        //设置新路径
        //nma.SetPath();

        //清除路径
        //nma.ResetPath();

        //调整到指定点位置
        //nma.Warp(Vector3.zero);
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetMouseButtonDown(0)) // 检测鼠标左键点击
        {
            RaycastHit hit;
            // 从摄像机发射射线到鼠标位置
            if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit))
            {
                print(hit.collider.name); // 输出被点击物体的名称
                nma.SetDestination(hit.point); // 设置寻路目标点
            }
        }

        if (Input.GetKeyDown(KeyCode.Space))
        {
            nma.isStopped = true;
        }

        if (Input.GetKeyUp(KeyCode.Space))
        {
            nma.isStopped = false;
        }
    }
}

导航网格外连接组件

网格外连接组件是什么?

我们在烘焙地形数据的时候

可以生成网格外连接

但是它是满足条件的都会生成

而且是要在编辑模式下生成

如果我们只希望两个未连接的平面之间只有有限条连接路径可以跳跃过去

并且运行时可以动态添加

就可以使用网格外连接组件

达到 “指哪打哪” 的效果

网格外连接组件的使用

  1. 使用两个对象作为两个平面之间的连接点(起点和终点)

  2. 添加 Off Mesh Link 脚本进行关联

  3. 设置参数

  • 参数相关

    • 网格外连接组件

      • Start 起始点

      • End 结束点

      • Cost Override 覆盖消耗值 负数或0则使用所属Area区域中的消耗值 如果为正数,则使用Area区域寻路消耗值*正数代表该连接点的寻路消耗 相当于我们可以自定义连接点的寻路消耗 主要用来解决当有"步行"和连接点都可以到达某一个目标时,希望优先选择步行区域,但是步行路径明显更远时修改该值

      • Bi Directional 是否开启双向连接点 开启后可以从Start到End,反之也可以 如果关闭,则只能Start到End

      • Activated 是否启用该连接点 如果关闭,则自动寻路时相当于失效

      • Auto Update Positions 是否自动更新位置 如果启用 当开始和结束位置改变时,导航网格也将更新 如果不启用,即使改变了开始结束位置,也会按照刚开始的位置进行计算

导航网格动态障碍组件

导航网格动态障碍组件用来干啥?

在游戏中常常会有这样的一个功能

场景中有一道门,如果这道门没有被破坏是不能自动导航到门后场景的

只有当这道门被破坏了,才可以通过此处前往下一场景

而类似这样的物体本身是不需要进行寻路的所以没有必要为它添加 NavMeshAgent 脚本

这时就会使用动态障碍组件实现该功能

导航动态障碍物组件的使用

  1. 为需要进行动态阻挡的对象添加 NavMeshObstacle 组件

  2. 设置相关参数

  3. 代码逻辑控制其的移动或者显隐

  • 参数相关

    • 导航网格动态障碍物组件

      • Shape 动态障碍的形状 Capsule:胶囊 Box:盒装

      • Carve: 是否开启雕刻功能 开启后,障碍物会在导航网格中挖一个孔,所谓挖孔就是会生成对应的网格信息,认为这篇区域无法前往 如果动态障碍物是固定不动的,建议开启,比如阻碍物门木桶栅栏等等 如果动态障碍物是频繁移动的,建议不开启该功能,比如汽车和玩家

        • Move Threshold: 移动阈值 当障碍物移动超过该距离时,会认为其为移动状态,会更新移动的孔

        • Time To Stationary: 障碍物作为静止状态需要等待的时间,单位为秒 当静止时间超过该值会认为真正静止了

        • Carve Only Stationary: 只有在静止状态时才会计算孔

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

public class lesson20_2 : MonoBehaviour
{
    private NavMeshAgent agent;
    // Start is called before the first frame update
    void Start()
    {
        agent = GetComponent<NavMeshAgent>();
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetMouseButtonDown(1))
        {
            RaycastHit hit;
            if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition),out hit))
            {
                agent.SetDestination(hit.point);
            }
        }

        if (Input.GetMouseButtonDown(0))
        {
            RaycastHit hit;
            if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit,1000,1 << LayerMask.NameToLayer("wall")))
            {
                hit.collider.gameObject.SetActive(false);
            }
        }
    }
}
Logo

更多推荐