Unity专题_导航寻路

前言:导航寻路(NavMesh)技术是一种系统内置的强大寻路算法系统,可以方便、快捷的开发出各种复杂应用,被大量应用于各种RPG、设计、动作、冒险等游戏中。

一.基本的导航寻路

我们会在本章模拟游戏开发过程中敌人的自动的寻路,绕过障碍,爬上与调下障碍物,按类别寻找属于自己的道路、动态设置道路的障碍等。

1.新建项目:在场景中添加如下图所示的地形系统:

unity A星寻路 unity寻路点_unity A星寻路

2.标记场景中的所有不动的游戏对象为“寻路静态(Navigation Static)”,我们通常把不动的游戏对象放在环境(新建的空物体方便统一的管理)下,如图所示:

unity A星寻路 unity寻路点_自定义_02

3.执行命名菜单中的Windows——>Navigation,显示导航寻路(Navigation)窗口,如图所示:

unity A星寻路 unity寻路点_寻路_03

ok,当我们看到这个面板之后,我们先来分析一下这里面的属性都有哪些作用吧,我们首先来分析Bake面板:

Agent Radius:烘培半径,值越小越好。

Agent Height:角色所要通过的高度。

Max Slope:最大的坡度。当大于这个坡度是,会被丢弃。

Step Height : 台阶的高度。低于这个高度时,导航网格地区会连接。

Drop Height:如果这个属性是值是正数的话,相邻的导航网格表面高度差低于此值时,将进行网格连接。

Jump Distance:跳跃距离。如果这个属性的值是正数的话,相邻导航网格表面的水平的距离低于此值的时候,将进行网格连接。

 

4.接下来是Object面板,如图所示:

unity A星寻路 unity寻路点_自定义_04

Navigation Static : 我们只有选中了Navigation Static 之后才能进行网格导航。

Generate OffMeshLinks:处于勾选状态的话,网格导航可以Jump和Drop。

Navigation Area:导航网格层。

5.:Areas面板:

unity A星寻路 unity寻路点_unity A星寻路_05

在寻路计算中,每个连通点都有代价属性(Cost),在实施A*时根据代价估算决定这个点是否丢进个行走路点队列中。在Navigation寻路导航界面下,Areas分页下是在给导航区域分类(相当于分层的设置),以及为每个分类设置不同的消费Cost,意义在于,导航算法中会计算出的是累加起来消耗最低的路径(不一定是视觉上最短可行的路径)。例如:设置地面上有一滩沼泽,该地形新建一个分类,并设置为一个很高的消费。那么在正常的情况下,寻路将会绕过该区域,走其他消费更低的路径。但若此时游戏中动态生成的物体阻挡了其他的路径。只有该路径可以走,那么该角色会穿过该沼泽地进行导航。

所以该界面的作用是:可以为每种地形自定义的分类,并可自定义的其他可行走的难以程度,来影响导航网格的选择。

 

6.进行烘培,如下图所示:

unity A星寻路 unity寻路点_unity A星寻路_06

7.进行脚本的编写:

 

public class Demo15_NavMesh : MonoBehaviour
{

    #region 字段和属性定义

    public Transform FindDestination;                   //寻找的目标
    private NavMeshAgent _agent;                        //寻路的组件

    #endregion

    void Awake()
    {
        _agent = this.GetComponent<NavMeshAgent>();
    }

    void Update()
    {
        //设置寻路
        if (_agent && FindDestination)
        {
            //设置目标
            _agent.SetDestination(FindDestination.transform.position);
        }
    }

}//class_end

这样的话就可以进行移动了。

unity A星寻路 unity寻路点_字段_07

二:使用OffMeshLik组件:

如果在我们项目中不能有很长的斜坡,如何让主角“走上”陡峭的高处呢?这种情况下,我们可以使用Unity中提供的“OffMeshLink”组件来解决问题。

(1):我们还是对静态的游戏对象标记为“导航静态”,然后做烘培的处理,但是我们会发现烘培出了两块独立的区域。如图所示:

unity A星寻路 unity寻路点_寻路_08

(2):这个时候我们给“梯子”增加“OffMeshLink”组件,并且设置其“Start”与“End”。因为“Start”与“End”参数的目的是标示用“OffMeshLink”连接两个原本不相同的区域。所以不进行渲染且触发自身的触发检测组件,如图所示:

unity A星寻路 unity寻路点_字段_09

 

unity A星寻路 unity寻路点_unity A星寻路_10

 这时候我们运行程序的时候,我们的主角就可以从我们的“梯子”中爬到我们的目标的位置了。

三:网格分层:                                                                                  

在本小节中我们讨论游戏对象走特定路径的问题。

(1):我们先搭建最基本的场景:

unity A星寻路 unity寻路点_unity A星寻路_11

(2):我们在导航窗口(Navigation)中的"Areas"面板中,添加对应的“层”:

unity A星寻路 unity寻路点_寻路_12

在我们的两个桥体中进行设置:

unity A星寻路 unity寻路点_自定义_13

当我们再次的运行游戏的时候就可以看到我们两个玩家会按照我们设置的进行移动了。

四:Nav Mesh Obstacle组件:

NavMeshObstacle是我们导航寻路中的“障碍物”组件。可以在导航寻路路径中设置特定的“关卡”使得项目中的这些关卡可以按照剧情的需要,按照一定的触发条件控制主角是否通过。

(1):新建我们场景的环境:

unity A星寻路 unity寻路点_自定义_14

(2):编写桥的动态开启脚本:

 

public class Demo17_NavMesh_NavMeshObstacle : MonoBehaviour
{

    #region 字段和属性定义

    private NavMeshObstacle _navMeshObstacle;                   //"障碍物"组件

    #endregion

    void Awake()
    {
        _navMeshObstacle = this.GetComponent<NavMeshObstacle>();
    }

    void Update()
    {
        //检测鼠标左键的按下
        if (Input.GetButtonDown("Fire1"))
        {
            if (_navMeshObstacle)
            {
                //允许通过
                _navMeshObstacle.enabled = false;
                this.GetComponent<Renderer>().material.color = Color.green;
            }
        }

        if (Input.GetButtonUp("Fire1"))
        {
            if (_navMeshObstacle)
            {
                //允许通过
                _navMeshObstacle.enabled = true;
                this.GetComponent<Renderer>().material.color = Color.red;
            }
        }
    }

}//class_end

 

这时候当我们运行程序的时候,我们发现当主角靠近桥体的时候,由于受到“障碍物”组件的阻挡作用而停止前进,当我们的单击鼠标左键的时候,“障碍物”会消失,主角可以继续前进通过桥体到达我们指定的位置。

unity A星寻路 unity寻路点_寻路_15

总结:导航寻路技术是目前游戏开发过程中不可或缺的核心技术之一,大量的应用在主角或者敌人的自动寻找目标等场景中。本篇博客仅介绍以上的四节,并没有完整的介绍其他的。大家有兴趣的话可以自己进行深入的研究。我自己也会在以后的开发过程中,继续进行编写有关于寻路的专题的博客,大家有兴趣的话可以进行关注我,谢谢!     

 

{/*background:url("") no-repeat 10px 50%;*/background:url("")no-repeat 20px 40%;margin-top: 20px;padding: 10px 10px 10px 120px;box-shadow: 0px 0px 15px #aaa;border-radius: