导航系统
网络游戏中的自动寻路功能,点击地图上的某个位置,角色自动跑到这个位置 RTS 和 Moba 类游戏中鼠标右键点击一个位置,角色自动走向这个位置,并且还具备规避障碍物的能力 以上使用的功能就是导航,或者叫寻路
主要具备两个特点:
- 以最短路径前往目标
- 规避障碍物
打开导航
导航面板分为4个部分:
解释 | |
---|---|
代理 | 用于对需要进行导航的角色进行分类,默认有一个 Humanoid,也就是人类。但是常规情况下,导航网格并不基于这个生成。 |
区域 | 在游戏场景中,不同区域的行走代价是不同的,比如在陆地上肯定比在沼泽地走起来更快,所谓代价就是移动成本,代价越高的地方,导航系统会尽可能地绕过去。区域面板主要用来设置不同区域的不同成本,但是这里只有预先设计好不同区域类型的成本,并不能直接设置场景中具体“这个游戏物体属于什么区域”。 |
烘培 | 这个面板中有很多代理相关的设置,实际上是在设置一个最极限的情况,可以理解成需要导航的最小单位,设置等同于 Agents 面板。点击 Bake 生成导航网格。 |
对象 | Scene Filter:场景过滤,主要影响层级面板中的显示;Navigation Areas:设置所选游戏物体的区域设置,可以多选游戏物体; 选中一个游戏物体后,Object面板中会出现一定的变化,我们会发现只有静态物体才可以进行 Navigation Areas(导航区域)的设置,这也意味着只有静态物体可以烘培到导航网格中去。 |
导航烘培
选择要烘培的物体,设置为静态导航Navigation Static
点击烘培,设置的就是人物的属性,点击Bake按钮进行烘培
场景中部分区域呈现成蓝色,意味着烘培成功,蓝色就是角色可以前往的地方
使用静态导航(Nav Mesh Agent组件)
场景烘培好后,已经确定了哪些地方角色可以前往,但是具体如何进行导航?
在玩家物体里加导航代理组件(Nav Mesh Agent),导航代理组件承担具体如何导航的计算。
Nav Mesh Agent 组件:
添加这个导航代理组件的游戏物体,身上会增加一个圆柱体形状的碰撞体
翻译 | 介绍 | |
---|---|---|
Agent Type | 代理类型 | 我们在 Navigation 中添加类型,主要是设置这个角色的尺寸 |
Base Offset | 基准偏移 | 碰撞体和角色的高度偏移 |
Speed | 速度 | 导航时的最大移动速度 |
Angular Speed | 角速度 | 导航时的最大旋转速度 |
Acceleration | 加速 | 导航刚开始时加速到Speed 需要的程度,越大越快 |
Stopping Distance | 停止距离 | 距离终点多远则停止 |
Auto Braking | 自动刹车 | 勾选后表示到达目标点或离目标点的距离满足 Stopping Distance 时自动停止,如果不勾选即使到达终点,角色也围着终点转来转去 |
障碍躲避
翻译 | 介绍 | |
---|---|---|
Radius | 半径 | 碰撞体半径 |
Height | 高度 | 碰撞体高度 |
Quality | 质量 | 躲避障碍物的行为质量,质量越高躲避行为越好,越智能 |
Priority | 优先级 | 范围为 0~99,(0 优先级最高,99 优先级最低),高优先级的 Agent 不会与低优先级的 Agent 进行碰撞。 |
NavMeshAgent 常用属性与方法
常用方法 | 解释 |
---|---|
navMeshAgent.SetDestination(Vector3 position): | 设置导航,填写目标位置 |
常用属性 | 解释 |
---|---|
navMeshAgent.isStopped | 获取和修改,是否停止导航 |
导航系统的代码相关操作都有先 using UnityEngine.AI 命名空间;实现鼠标控制物体移动
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class NavigationTest : MonoBehaviour
{
private NavMeshAgent agent;
void Start()
{
//获取代理组件
agent = GetComponent<NavMeshAgent>();
}
void Update()
{
//如果按下鼠标
if (Input.GetMouseButtonDown(0)) {
//获取点击位置
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
//判断射线是否碰到物体
if (Physics.Raycast(ray,out hit)) {
//点击的位置
Vector3 point = hit.point;
//设置该位置为导航目标点
agent.SetDestination(point);
}
}
}
}
结果
敌人追踪玩家效果
创建的角色最好是一个玩家和两个敌人。通过改变这些值你可以改变你角色导航的行为。
我用一个比较简单的移动脚本使敌人去追踪玩家 。
using UnityEngine.AI;
public class EnemyMovement : MonoBehaviour
{
// 获取玩家位置,绑定玩家
public Transform player;
// 获取代理组件
NavMeshAgent nav;
void Start ()
{
nav = GetComponent <NavMeshAgent> ();
}
void Update ()
{
// 设置目标为玩家的位置
nav.SetDestination(player.position);
}
}
实现掉落
选中平台,勾选他的Generate OffMeshlinks参数
设置对应的掉落高度,点击bake重新烘培
平台生成对应的掉落路线,人物在平台上时导航就会一起分析掉落路线选择最短距离行走
实现跳跃
跳跃和掉落类似,如选择两个平台进行跳跃,选中平台,勾选他们的Generate OffMeshlinks参数,填写跳跃距离重新烘培
使用动态导航(NavMeshObstacle组件)
目前我们的导航网格,是通过将地形游戏物体设置为静态模式,然后导航烘培后实现,但是游戏中可能存在这种情况:可能因为玩家的建造、角色的技能,出现一个障碍物,我们右键移动角色的时候,需要避开这些障碍物。
比如我们现在有个自动门,当门打开时需要让玩家可以导航通过,按前面的方法肯定不行,故引入了 NavMeshObstacle 组件
先把门的静态导航Navigation Static设置取消
给门绑定NavMeshObstacle组件
NavMeshObstacle 属性
参数 | 翻译 | 解释 |
---|---|---|
Shape | 形状 | 类似碰撞体的蓝色外框,但是实际上并不具备碰撞体的特性,可以穿模,仅仅用于导航计算上 |
Center | 中心 | 中心点 |
Size | 尺寸 | 大小 |
Carve | 切割 | 不勾选的情况 :该障碍物只是阻挡了物体前进路线,并没有改变物体导航网格的(导航只会不断地计算最短路径,依旧认为该条路径是最短的,不会重新计算路径)。物体不会绕过该障碍物进行导航。会越走越慢,然后停在门前过不去 |
如果勾选切割
参数,导航会按门的位置动态的渲染网格
实现传送(Off Mesh Link组件)
新建两个物体,实现从物体cube位置传送到cube(1)位置
给两个物体绑定Off Mesh Link组件
绑定起始和结束物体位置物体,如果勾选自动更新位置
,传送目标位置会跟着物体位置而变动
点击重新烘培,实现人物从cube位置传送到cube(1)位置
如果有多个不同宽高角色怎么办?
这就体现了导航代理的作用
导航代理,可以设置不同宽高角色的导航代理
每个人物再去选择对应的代理类型
区域
在游戏场景中,不同区域的行走代价是不同的,比如在陆地上肯定比在沼泽地走起来更快,所谓代价就是移动成本,代价越高的地方,导航系统会尽可能地绕过去。区域面板主要用来设置不同区域的不同成本,但是这里只有预先设计好不同区域类型的成本,并不能直接设置场景中具体“这个游戏物体属于什么区域”。
定义一个水区域成本为3
绑定成本,重新烘培即可