71.导航寻路系统-导航网格动态障碍组件
71.1 知识点
导航网格动态障碍组件用来干啥
在游戏中常常会有这样的一个功能,场景中有一道门,如果这道门没有被破坏是不能自动导航到门后场景的。
只有当这道门被破坏了,才可以通过此处前往下一场景。
而类似这样的物体本身是不需要进行寻路的,所以没有必要为它添加NavMeshAgent脚本。
这时就会使用动态障碍组件实现该功能。
导航动态障碍物组件的使用
为需要进行动态阻挡的对象添加NavMeshObstacle组件
创建一个立方体作为障碍物,目标是障碍物被销毁才能通过桥。立方体添加NavMeshObstacle组件
设置NavMeshObstacle相关参数
代码逻辑控制障碍物的移动或者显隐,让代理对象看看要不要通过某些区域或者不然通过某些区域
NavMeshObstacle导航网格障碍物组件参数
Shape 动态障碍的形状
- Capsule:胶囊
- Box:盒装
Carve 切割 是否开启雕刻功能
- 开启后,障碍物会在导航网格中挖一个孔,所谓挖孔就是会生成对应的网格信息,认为这篇区域无法前往,孔就是障碍物的意思,挖了孔就无法经过了。
- 如果动态障碍物是固定不动的,建议开启,比如阻碍物门木桶栅栏等等。
- 如果动态障碍物是频繁移动的,建议不开启该功能,比如汽车和玩家。
Move Threshold 移动阈值
- 当障碍物移动超过该距离时,会认为其为移动状态,会更新移动的孔。
Time To Stationary 静止时间
- 障碍物作为静止状态需要等待的时间,单位为秒。
- 当静止时间超过该值会认为真正静止了。
Carve Only Stationary 仅在静止时切割
- 只有在静止状态时才会计算孔。
71.2 知识点代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class Lesson71_导航寻路系统_导航网格动态障碍组件 : MonoBehaviour
{
private NavMeshAgent navMeshAgent;
void Start()
{
#region 知识点一 导航网格动态障碍组件用来干啥?
//在游戏中常常会有这样的一个功能
//场景中有一道门,如果这道门没有被破坏是不能自动导航到门后场景的
//只有当这道门被破坏了,才可以通过此处前往下一场景
//而类似这样的物体本身是不需要进行寻路的所以没有必要为它添加NavMeshAgent脚本
//这时就会使用动态障碍组件实现该功能
#endregion
navMeshAgent = this.GetComponent<NavMeshAgent>();
#region 知识点二 导航动态障碍物组件的使用
//1.为需要进行动态阻挡的对象添加NavMeshObstacle组件
//2.设置相关参数
//3.代码逻辑控制其的移动或者显隐
#endregion
}
void Update()
{
if(Input.GetMouseButtonDown(0))
{
RaycastHit hit;
if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit))
{
navMeshAgent.SetDestination(hit.point);
}
}
}
}
71.3 练习题
在上一个练习题的基础上,在场景上加入一个阻碍玩家前进的动态障碍物,玩家摧毁它才可以前往下一个区域
创建两个地形,中间的桥。桥上添加一个立方体作为障碍物,障碍物添加NavMeshObstacle组件。注意要打开雕刻功能。打开后因为中间寻路网格断开了,玩家会自动停止不播跑步动画了。假如是移动的怪物就不应该打开雕刻功能,注意玩家可以绕开怪物走。
给障碍物立方体修改成Wall层级,在脚本中添加鼠标右键销毁障碍物的代码
if (Input.GetMouseButtonDown(1)) // 如果按下鼠标右键
{
RaycastHit hit; // 定义一个RaycastHit变量,用于存储射线检测的结果
if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit, 1000, 1 << LayerMask.NameToLayer("Wall"))) // 从主摄像机的鼠标位置发出一条射线,并在1000米内检测到属于Wall层的碰撞体
{
hit.collider.gameObject.SetActive(false); // 将碰撞体所在的游戏对象设置为不激活状态,相当于消除障碍物
}
}
71.4 练习题代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class Lesson71_练习题 : MonoBehaviour
{
#region 练习题一
//在上一个练习题的基础上,在场景上加入一个阻碍玩家前进的动态障碍物,玩家摧毁它才可以前往下一个区域
private NavMeshAgent navMeshAgent; // 定义一个NavMeshAgent变量,用于存储导航代理组件
private Animator animator; // 定义一个Animator变量,用于存储动画控制器组件
void Start()
{
navMeshAgent = this.GetComponent<NavMeshAgent>(); // 获取当前游戏对象上的NavMeshAgent组件并赋值给navMeshAgent变量
animator = this.GetComponent<Animator>(); // 获取当前游戏对象上的Animator组件并赋值给animator变量
}
void Update()
{
if (Input.GetMouseButtonDown(0)) // 如果按下鼠标左键
{
RaycastHit hit; // 定义一个RaycastHit变量,用于存储射线检测的结果
if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit)) // 如果从主摄像机的鼠标位置发出一条射线,并检测到碰撞体
{
print(hit.collider.name); // 在控制台输出碰撞体的名称
navMeshAgent.SetDestination(hit.point); // 设置导航代理的目标位置为碰撞点的位置,导航代理会自动计算并沿着路径移动
}
}
if (Input.GetMouseButtonDown(1)) // 如果按下鼠标右键
{
RaycastHit hit; // 定义一个RaycastHit变量,用于存储射线检测的结果
if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit, 1000, 1 << LayerMask.NameToLayer("Wall"))) // 从主摄像机的鼠标位置发出一条射线,并在1000米内检测到属于Wall层的碰撞体
{
hit.collider.gameObject.SetActive(false); // 将碰撞体所在的游戏对象设置为不激活状态,相当于消除障碍物
}
}
if (navMeshAgent.velocity == Vector3.zero) // 如果导航代理的速度为零,表示没有移动
animator.SetInteger("Speed", 0); // 设置动画控制器中的Speed参数为0,表示播放静止动画
else // 否则,表示正在移动
animator.SetInteger("Speed", 1); // 设置动画控制器中的Speed参数为1,表示播放行走动画
}
#endregion
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com