程序员开发实例大全宝库

网站首页 > 编程文章 正文

12.Unity导航系统(unity导航网格路径)

zazugpt 2024-10-24 19:25:12 编程文章 17 ℃ 0 评论

12.导航系统

12.1 导航网格

导航网格指的是游戏场景中可以通过的区域。在 Unity 中,我们只需要指定场景中哪些游戏对象参与导航网格的计算,就可以自动生成导航网格。

生成导航网格的步骤:

1.选中所有参与计算的游戏对象,在 Inspector 视图中展开 Static 下列列表选中 Navigation Static 选项。

2.执行菜单:Window -> AI -> Navigation ,打开 Navigation 面板。在该面板中的有 4 个选项卡分别是:Agents、Areas、Bake、Object,选中 Bake 选项卡,单击 Bake 按钮。

3.Bake 完成后在场景中会出现 Bake 网格,网格内的区域就是允许导航的区域。如下图所示:

Bake 属性如下:

  • Agent Radius - 代理半径,半径越小, Bake 后网格区域面积越大。
  • Agent Height - 代理高度,会影响 Bake 后的门或者桥洞等的高度是否能够通过。
  • Max Slope - 最大坡度,可以走上去的最大坡度。
  • Step Height - 可以走上去的最大台阶高度。
  • Drop Height - 掉落的高度。
  • Jump Distance - 在网格间跳跃的最大距离。

12.2 导航代理

导航代理是一个独立的组件,给游戏角色添加 Nav Mesh Agent(导航网格代理)组件后,角色就会在导航区域中进行导航移动。

Nav Mesh Agent 组件属性:

  • Agent Type - 代理类型,与 Bake 代理的属性相同,可以在打开的导航面板中删除,也可以为不同的角色添加多个代理类型。
  • Base Offset - 基准偏移。
  • Speed - 速度,导航网格代理的最大移动速度。
  • Angular Speed - 角速度,导航网格代理的旋转角速度。
  • Acceleration - 加速,导航网格代理的加速度。
  • Stopping Distance - 停止距离,距离目标位置多远停止导航。
  • Auto Braking - 自动刹车,到达目标位置后自动减速至停止。
  • Radius - 半径,导航网格代理用来计算障碍躲避的半径。
  • Height - 高度,导航网格代理用户计算障碍躲避的高度。
  • Quality - 质量,躲避障碍质量,质量越高,躲避时的计算越精细,同时性能消耗也越大。
  • Priority - 优先级,多个导航网格代理的躲避优先级,数值越大,优先级越高。
  • Auto Traverse Off Mesh Link - 是否自动通过网格代理链接。
  • Auto Repath - 是否自动重新进行寻路。
  • Area Mask - 该导航网格代理允许通过的区域。

使用脚本控制导航:

当场景中导航网格已经生成,游戏角色也挂载了导航代理组件后,接下来就可以为游戏角色添加脚本,在脚本中控制导航进行移动。

示例:单击场景中的位置作为目标点,使角色朝目标点移动,并最终到达目标点。代码如下:

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

public class PlayerController : 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(0))
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit;
            bool res = Physics.Raycast(ray, out hit);
            if (res)
            {
                Vector3 point = hit.point;
                _agent.SetDestination(point);
            }
        }
    }
}

12.3 障碍物

在导航网格生成时,静态障碍物会自动生成好,但在游戏中经常会遇到动态障碍物,对于动态障碍物,需要使用 Nav Mesh Obstacle(导航网格障碍)组件。

Nav Mesh Obstacle 组件属性:

  • Shape - 障碍物的形状,可以选择 Box(盒状) 或 Capsule(胶囊状)。
  • Center - 障碍物的中心点。
  • Size - 障碍物的尺寸。
  • Carve - 切割,不切割会作为普通障碍物;切割会对网格进行切割,并对移动区域进行修改。
  • Move Threshold - 移动阈值,移动距离超过阈值时才会切割。
  • Time to Stationary - 静止时间,障碍物静止时间到达该值后才会进行切割。
  • Carve Only Stationary - 是否只在静止时进行切割。

在添加障碍物时,除了要挂载 Nav Mesh Obstacle 组件外,还要为障碍物物体取消勾选 Static 选项。

示例:通过障碍物控制胶囊体移动。

1.新建项目,在场景中添加一个 Plane,缩放为 (2,2,2),添加一个胶囊体,命名为 NPC,并给 NPC 添加 Nav Mesh Agent 组件。

2.在 Project 视图中创建脚本 NPCController,并挂载到 NPC 物体上,代码如下:

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

public class NPCController : MonoBehaviour
{
    // 导航代理组件
    private NavMeshAgent _agent;
    // 目标点
    private Vector3 targetPosition;
    // 是否到达目标点
    private bool isArrived = false;
    // Start is called before the first frame update
    void Start()
    {
        _agent = GetComponent<NavMeshAgent>();
        targetPosition = transform.position + new Vector3(0, 0, 10);
        _agent.SetDestination(targetPosition);
    }

    // Update is called once per frame
    void Update()
    {
        // 获取距离目标点的距离
        float dis = Vector3.Distance(transform.position, targetPosition);
        if (_agent.remainingDistance < 0.1f)
        {
            // 在与目标点的距离小于 0.1 的情况下
            if (isArrived)
            {
                // 在开始位置
                isArrived = false;
                // 向终点移动
                targetPosition = transform.position + new Vector3(0, 0, 10);
            }
            else
            {
                // 在终点位置
                isArrived = true;
                // 向开始点移动
                targetPosition = transform.position - new Vector3(0, 0, 10);
            }

            _agent.SetDestination(targetPosition);
        }
    }
}

3.选中 Plane,执行 Window -> AI -> Navigation ,在 Inspector 视图中勾选 Navigation Static,单击 Bake 选项卡,单击 Bake 按钮。

4.在场景中添加立方体,命名为 Player,位置为(-0.15,1,-3),缩放为(8,2,0.4),给 Player 添加 Nav Mesh Obstacle 组件。

5.在 Project 视图中创建脚本 PlayerController,并挂载到 Player 上,代码如下:

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

public class PlayerController : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        float h = Input.GetAxis("Horizontal");
        transform.position += transform.right * h * 0.3f;
    }
}

运行游戏,我们会发现,无论怎么样移动障碍物,NPC 都会自动躲避障碍物。

12.4 导航网格链接

网格之间可以设置网格链接。在导航网格生成时,Bake 选项卡中的 Generated Off Mesh Links 中包括 Drop Height(掉落高度)和 Jump Distance(跳跃距离),这个两个属性运行从高台掉落并在高台间跳跃。同时,选中场景中的高台物体(一个或多个),在 Object 选项卡中勾选 Generate Off Mesh Links 选项,然后再在 Bake 选项卡中单击 Bake 按钮,Bake 完成后,可以看到高台的附加自动生成链接,这样角色就可以在链接之间移动了。

上面我们说明的是通过 Bake 自动生成导航网格链接,在实际开发中,我们会需要自己控制网格间的链接,也就是说,我们希望能从某点到另外一点生成一个链接。这时,就需要用到 Off Mesh Link 组件。

Off Mesh Link 组件属性:

Start - 网格链接起始位置的游戏物体。

End - 网格连接结束位置的游戏物体。

Cost Override - 网格链接花费的成本。

Bi Directional - 是否允许双向移动。

Actived - 是否已激活该网格链接。

Auto Update Positions - 是否自动更新位置,勾选后,当起始或结束位置的对象移动时,会更新网格链接。

Navigation Area - 导航区域,网格链接属于的导航区域。

创建网格链接的步骤:

创建一个平面,缩放为(0.03,0.03,0.03),复制一份,并将两个平面放在希望进行网格链接的两个位置。

选择其中一个添加 Off Mesh Link 组件,然后将两个平面分别放在组件的 Start 和 End 处。即可看到生成的网格链接,如下图所示:

12.5 导航区域

在 Navigation 面板上,Areas 选项卡中,一共可以设置 32 个区域,默认设置了 3 个区域,其余区域可以自定义,每个区域都会有颜色和成本的设置。

成本的含义为导航代理网格移动时消耗的体力,导航系统在计算推荐路线时会将成本计算在内。

示例:让胶囊体在规划的路线上移动。

1.新建项目,并在场景中添加 1 个 平面和 8 个 立方体(路面)、1 个胶囊体(角色),形成如下图所示场景:

2.打开 Navigation 面板(Window -> AI -> Navigation),单击 Areas 选项卡,增加一个 Road 区域,设置成本为 1,Walkable 成本为 100。

3.单击一个立方体,在 Navigation 面板中的 Object 选项卡这设置 Navigation Area 为 Road(其他立方体也是如此)。

4.选择 1 个 平面和 8 个 立方体,在 Navigation 面板中 Bake 选项卡中单击 Bake 按钮,完成后如下图所示:

5.在 Project 视图中创建脚本 PlayerController,挂载到胶囊体,代码如下:

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

public class PlayerController : 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(0))
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit;
            bool res = Physics.Raycast(ray, out hit);
            if (res && hit.collider.gameObject.name != "Plane") // 只能在非 Plane 上移动
            {
                Vector3 point = hit.point;
                _agent.SetDestination(point);
            }
        }
    }
}

运行游戏,看看效果,应该是只有在单击立方体时才会移动,移动时是沿着立方体的表面移动的,而不会走 Plane 绿色的区域。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表