相关文章推荐
粗眉毛的镜子  ·  python - ...·  1 年前    · 
NavMesh导航寻路使用

NavMesh导航寻路使用

一:NavMesh基本导航寻路

二:OffMesh Link使用

三:网格分层的使用

四:动态更改路线

五:NavMeshObstacle组件使用


一:NavMesh基本导航寻路

1.1 基础使用

导航寻路技术是一种系统内置的强大寻路算法系统,可以方便快捷的开发出各种复杂的应用,被大量使用在各种RPG、射击、动作、冒险游戏中。如果没有这种内置插件,我们需要使用3D数学进行复杂的数学计算,或者使用第三方插件(如A星算法)。

在Unity编辑器中,我们选择菜单栏Window->Navigation,即可打开Navigation面板,如图2-1所示。这个Objcet的面板是对应当前选择的物体的(前提是该物体带网格),旁边的Bake面板是对应全局选项的。上面的All、MeshRenderers、Terrains是对Hirarchy面板里面显示的物品选择的一个筛选过滤。

第一个Navigation Static选项是选择该物体是否用做寻路功能的一部分。只有勾选了这个选项,下面的其他选项才可操作。

Generate OffMeshLinks选项是选择该物体是否根据高度、可跳跃宽度等全局的选项自动生成OffMeshLink,这个会在以后的讲解中详细说明,这次就暂时不讨论。

Navigation Area(旧版叫做Navigation Layer)是对参与寻路功能的地图物体的一个分类,用层来分类,默认有三个层可以选择,当然也可以自己添加层。寻路层就是在2-1的图中的Areas中来设置。

自带寻路Navmesh的三大组件:

1.Nav Mesh Agent:主要挂在寻路物体上

2.Off Mesh Link:实现区域转移功能(例如,有时不一定只是在地面上进行寻路,可能有些高高的平台,平台与地面是不相连的,使用该组件可以跳到平台上)

3.Nav Mesh Obstacle:主要挂在障碍物上。

下面我们通过一个基本的导航寻路案例为大家讲解其具体使用。

(1)新建项目,搭建如下地形。标记场景中所有的不动对象为寻路静态(Navgation Static)

(2)打开Window->Navigation,显示导航寻路窗口。

(3)调整合适的参数,半径,高度等参数都可以修改,点击下方的Bake进行烘焙。

(4)使用场景视图的线框模式查看烘焙的路径。

(5)添加胶囊体,并且添加NavMeshAgent组件。

(6)编写寻路脚本,并且将其挂载到胶囊体之上。

(7)运行程序,点击位置,让胶囊沿着指定的路径行走。

public class PlayerCtr : MonoBehaviour {
	[SerializeField]
	private NavMeshAgent agent;
	// Use this for initialization
	void Start () {
	void Update () {
		if (Input.GetMouseButtonDown (0)) {
			Ray ray =  Camera.main.ScreenPointToRay (Input.mousePosition);
			RaycastHit hit;
			if(Physics.Raycast(ray,out hit)){
				//if (hit.collider.name.Equals ("Plane")) {
					Vector3 p = hit.point;
					Debug.Log (p);
					agent.SetDestination (p);
    speed                      移动速度
    Angular Speed              转角速度 ,转身速度    角速度: 最高转速(度/秒)。
    Acceleration               加速度,启动时的 最大加速度。
    Stopping Distance          停止距离 ,制动距离:制动距离。到目的地的距离小于这个值,代理减速。
    Auto Traverse OffMesh Link 自动遍历OffMesh链接:自动移动并关闭OffMeshLinks
    Auto Repath                 自动重新寻路:如果现有的部分已失效,获得新的路径。
    Height                      高度:代理的高度(用于调试图形)。
    Base offset                 基本偏移:碰撞几何体相对于实际几何体垂直的偏移。
    Obstacle Avoidance Type     障碍躲避类型 :躲避的质量水平。
    NavMesh Walkable            导航网格行走:指定代理可以遍历的导航网格层类型。这个参数很有用,在接下来的实例中可以用到。

1.2 斜坡与跳跃

(1)添加斜坡,设置为NavigationStatic,重新烘焙。如下图所示:

(2)调整参数,重新烘焙查看效果。可以调节半径,高度,最大坡度,阶梯高度。如下图所示:

1.3 掉落功能

(1)选中所有的静态对象,勾选Generate OffMeshLinks,重新烘焙,会自动产生路径。

(2)修改高度和距离,如果两个都是0,哪怕设置了上面的Generate也没有效果。

(3)运行查看效果,将人物放置在高处,点击地板,会按照最优的路径移动。

1.4 跳跃功能

(1)搭建如下界面。选中所有的静态对象,勾选Generate OffMeshLinks

(2)调整JumpDistance,保证大于需要跳跃的距离。进行烘焙即可

(3)挂载脚本,运行游戏,可以看到agent可以在其中进行跳跃。

二:OffMesh Link使用

在前面的讲解之中,如果项目不能有很长的坡,如何让主角走上陡峭的高处呢。这种情况下,我们可以使用Unity的OffMeshLink来解决问题。

(1)搭建一个场景,模拟实现一个梯子的效果。如下图所示:

(2)给梯子增加OffMeshLink组件,并且设置其Start与End参数。不需要重新烘焙。

(3)运行程序,发现原本不相互连通的两个区域现在可以相互连通了。

三:网格分层的使用

Navigation视图下的Areas标签页可以设置层,Object标签页可以为物体指定层,Nav Mesh Agent组件的Area Mask可以指定可行走的层。具有寻路过程中层的过滤功能。

关于areaMask

Built-in 0 对应的areaMask为1

Built-in 1 对应的areaMask为2

Built-in 2 对应的areaMask为4

如此类推,即是areaMask为2的(n-1)次方

当设置areaMask为-1时,表示所有层都能通过

当设置areaMask为0时,表示所有层都不能通过

当设置areaMask为1时,表示只有Built-in 0层能通过

当设置areaMask为2时,表示只有Built-in 1层能通过

当设置areaMask为3时,表示只有Built-in 0和Built-in 1层能通过(3 = 1 + 2)

当设置areaMask为8时,表示只有User 3层能通过

可以通过查看Nav Mesh Agent组件的NavMesh Walkable看到设置areaMask后的结果。通过_agent.areaMask可以打印出寻路代理可行走的层。

(1)搭建如下场景,目的为了让红色的沿着红色的桥通过,蓝色的通过蓝色的桥通过。

(2)在Area窗口添加两个层,分别为 Red和Blue。

(3)分别给P1,P2,P3,P4设置其对应的NavigationArea,P1和P2为Walkable,P3为Red,P4为Blue。

(4)添加两个胶囊体,添加Agent组件。分别设置其材质球为蓝色和红色,挂载脚本。修改AreaMask,蓝色胶囊去除Red Area,红色胶囊去除Blue Area。

(5)烘焙运行,可以发现,代理寻路的时候沿着指定的路线运动。

四:动态更改路线

(1)搭建如下场景,通过点击按钮,让物体沿着指定的Area走路。

(2)在Area窗口添加两个层,分别为 Red和Blue。

(3)添加脚本,将其挂载到相机之上,通过点击按钮修改Area路径。

public class DynamicRoute : MonoBehaviour {
	public NavMeshAgent agent;
	// Use this for initialization
	void Start () {
	void OnGUI()  
		if (GUI.Button(new Rect(0, 0, 100, 50), "走下层"))  
			agent.walkableMask = 9;  
		if (GUI.Button(new Rect(0, 100, 100, 50), "走上层"))  
			agent.walkableMask = 17;  

(4)给胶囊添加脚本PlayCtrl,同前面讲述的一样。运行即可。点击按钮不同,物体就会沿着不同的路线行进。

五:NavMeshObstacle组件使用

NavMesh Obstacle组件是导航寻路中的障碍物组件。可以在导航路径中设置特定的关卡,使得项目中的关卡可以按照剧情的需要,按照一定的触发条件进行触发。

(1)新建一个如下图所示的场景。

(2)给主角添加导航代理组件与脚本,使得主角可以顺利通过桥体。

(3)给独木桥添加障碍物组件,通过脚本的方式动态的改变桥体。

public class ObstacleControl : MonoBehaviour {
	private NavMeshObstacle _navMeshObs;
	// Use this for initialization
	void Start () {
		_navMeshObs=GetComponent<NavMeshObstacle> ();
	// Update is called once per frame
	void Update () {
	  if (Input.GetButtonDown("Fire1")) {
			if(_navMeshObs){
				_navMeshObs.enabled=false;
				GetComponent<MeshRenderer>().material.color=Color.green;
		if (Input.GetButtonUp("Fire1")) {
			if(_navMeshObs){
				_navMeshObs.enabled=true;
				GetComponent<MeshRenderer>().material.color=Color.red;

(4)再次运行程序,发现当主角靠近桥的时候,由于受到障碍物的阻挡作用而停止前进。当我们单击鼠标左键时候,障碍物消失,主角可以继续前进。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class PlayerCtr : MonoBehaviour {
	private NavMeshAgent agent;
	public Animator animator;
	// Use this for initialization
	void Start () {
		agent = GetComponent<NavMeshAgent> ();
		animator = GetComponent<Animator> ();
	// Update is called once per frame
	void Update () {
		if (Input.GetMouseButtonDown(0)) {
			//发出射线点
			Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition);
			RaycastHit hit;//碰撞信息
			if (Physics.Raycast(ray,out hit)) {
				Vector3 p = hit.point;
				Debug.Log (p);
				agent.SetDestination (p);
				animator.SetBool ("run", true);
		if (agent.remainingDistance < 0.1f && agent.hasPath) {
			print ("1");