HOME
BLOG
Unity 导航系统
9月 14 2022

| 介绍

首先来看两个情况:

  • 很多网络游戏中都有一个功能,点击地图上的某个位置,角色自动跑到这个位置

  • RTS 和 Moba 类游戏中鼠标右键点击一个位置,角色自动走向这个位置,并且还具备规避障碍物的能力

以上使用的功能就是导航,或者叫寻路,主要具备两个特点

  • 以最短路径前往目标

  • 规避障碍物

| 烘培

既然要导航,那必须先确定一张“地图”,确定玩家什么地方可以去、什么地方不可以去

  • Navigation(导航)面板

  • 功能:导航面板的主要功能就是规划场景中哪里可以去哪里不可以去

  • 面板:Window-》Al-》Navigation

这个面板之后课程我们会单独来说,但是需要导航就必须先简单烘焙一下地图才能进行下去

  1. 选择场景中,将地形、场景相关的游戏物体设置为Static模式,这是烘焙的前提
  2. 直接选择子栏目Bake
  3. 点击Bake按钮
  4. 场景中部分区域呈现成蓝色,意味着烘焙成功,蓝色就是角色可以前往的地方

| 核心组件

  • 现在的场景已经确定了那些地方角色可以前往,但是具体如何进行导航?

    • 这就需要游戏物体身上具备NavMeshAgent这个组件,导航代理组件承担具体如何导航的计算

    • 添加这个导航代理组件的游戏物体,身上会增加一个圆柱体形状的碰撞体

  • 面板:

  1. Agent Type: 代理类型,我们在Navigation中添加类型,主要是设置这个角色的尺寸    

  2. Base Offset:碰撞体和角色的高度偏移

  3. Speed:导航时的最大移动速度

  4. Angular Speed:导航时的最大旋转速度

  5. Acceleration:导航刚开始时加速到Speed需要的程度,越大越快

  6. Stopping Distance:距离终点多远则停止

  7. Auto Braking:勾选后表示到达目标点或离目标点的距离满足 StoppingDistance 时自动停止,如果不勾选即使到达终点,角色也围着终点转来转去

| 障碍规避

  • Avoidance

  • 障碍回避设置:

    • Radius:碰撞体半径
    • Height:碰撞体高度
    • Quality:躲避障碍物的行为质量,质量越高躲避行为越好,越智能
    • Prioritv:优先级,范围为0-99,(0优先级最高,99优先级最低),高优先级的Agent不会与低优先级的Agent进行碰撞
  • NavMeshAgent 常用属性与方法:

    • 导航系统的代码相关操作都要先 using UnityEngine.AI; 命名空间
  • 常用方法:

    • navMeshAgent.SetDestination(vector3) :设置导航,填写目标位置
  • 常用属性:

    • navMeshAgent.isStopped :获取和修改是否停止导航
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;


public class L9_1Demo : MonoBehaviour
{
    private NavMeshAgent agent;
    public Transform target;
    void Start()
    {
        agent = GetComponent<NavMeshAgent>();
    }

    void Update()
    {
        agent.SetDestination(target.position);
        if (Input.GetKeyDown(KeyCode.Space))
        {
            agent.isStopped = !agent.isStopped;
        }
    }
}

导航面板分为四部分

  • Agents

    • Agents 代理设置

    • 对需要进行导航的角色进行分类,默认有一个 Humanoid,也就是人类。

    • 但是常规情况下不使用,导航网格并不基于这个生成

    • 主要设置:

      • Name:名称

      • Radius:半径

      • Hieght::高度

      • Step Height:能上多高的台阶

      • Max Slope:能上多陡峭的坡度

  • Areas

    • 在游戏场景中,不同区域的行走代价是不同的,比如陆地肯定比沼泽地走起来更快,所谓代价就是移动成本,代价越高的地方,导航系统会尽可能绕过去

    • Areas面板主要用来设置不同区域的不同成本,但是这里只有预先设计好不同区域类型的成本,并不能直接设置场景中具体“这个游戏物体属于什么区域”

  • Bake

    • 这个面板中有很多 Agents 相关的设置,实际上是在设置一个最极限的情况,可以理解成需要导航的最小单位,设置等同与 Agents 面板

    • 点击 Bake 生成导航网格

  • Object

    • Scene Filter:场景过滤,主要影响层级面板中的显示

    • 选中一个游戏物体后,Object 面板中会出现一定的变化,我们会发现只有静态物体可以进行 Navigation Areas(导航区域)的设置,这也意味着只有静态物体可以烘焙到导航网格中

    • Navigation Areas:设置所选游戏物体的区域设置,可以多选游戏物体


| 组件

目前我们的导航网格,是通过将地形游戏物体设置为静态模式,然后导航烘焙后实现,但是游戏中可能存在这种情况:RTS或Moba类型游戏中,可能因为玩家的建造、角色的技能,出现一个障碍物,我们右键移动角色得到时候,需要避开这些障碍物

按照我们目前所学,其实是达不到的,所以这节课我们需要学习导航 NavMeshObstacle 障碍物组件

| 属性

这个组件用起来十分方便,直接给游戏物体添加这个组件可

参数:

Shape:形状,类似碰撞体的蓝色外框,但是实际上并不具备碰撞体的特性,可以穿模,仅仅用于导航计算上

Center:中心点

Size:尺寸

Carve:不勾选的情况。该障碍物只是阻挡了物体前进路线,并没有改变物体导航网格的路径(导航只会不断的计算最短路径依旧认为此条路径是最短的不会重新计算路径)。物体不会绕过该障碍物进行导航


案例:鼠标点击移动

应用之前学习的动画系统、射线、导航系统等知识来完成一个点击移动的小案例

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

public class L9_4Demo : MonoBehaviour
{
    private Animator animator;
    private NavMeshAgent agent;

    void Start()
    {
        animator = GetComponent<Animator>();
        agent = GetComponent<NavMeshAgent>();
    }

    void Update()
    {
        if (Input.GetMouseButtonDown(1))
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

            if (Physics.Raycast(ray, out RaycastHit hitinfo, 1000))
            {
                // hitinfo: 射线碰撞的相关信息
                Debug.DrawLine(ray.origin, hitinfo.point, Color.red);
                agent.SetDestination(hitinfo.point);
                animator.SetBool("Run", true);
                agent.isStopped = false;
            }
        }

        if(Vector3.Distance(transform.position,agent.destination)<=agent.stoppingDistance)
        {
            animator.SetBool("Run", false);
            agent.isStopped = true;
        }
    }
}


👾森木清树👾

Unity Study