用Unity Navigation系统打造《Among Us》式多角色动态巡逻与封锁机制
在派对游戏和非对称对抗游戏中,角色巡逻与区域封锁机制是营造紧张氛围的核心设计元素。《Among Us》中红色角色沿固定路线巡逻、蓝色角色动态设置路障改变通行区域的玩法,正是这种机制的经典呈现。本文将深入探讨如何利用Unity的Navigation系统,特别是NavMesh Areas和NavMeshObstacle组件,实现类似的多角色分路巡逻与动态封锁功能。
1. 基础场景搭建与导航烘焙
首先需要构建一个适合多角色导航的基础场景。与简单寻路不同,多角色动态巡逻需要更精细的NavMesh区域划分:
// 场景初始化脚本示例 using UnityEngine; using UnityEngine.AI; public class SceneSetup : MonoBehaviour { void Start() { // 确保所有静态障碍物标记为Navigation Static GameObject[] obstacles = GameObject.FindGameObjectsWithTag("Obstacle"); foreach (GameObject obj in obstacles) { StaticEditorFlags flags = GameObjectUtility.GetStaticEditorFlags(obj); GameObjectUtility.SetStaticEditorFlags(obj, flags | StaticEditorFlags.NavigationStatic); } // 自动烘焙NavMesh NavMeshSurface surface = FindObjectOfType<NavMeshSurface>(); surface.BuildNavMesh(); } }关键配置参数:
| 参数 | 推荐值 | 作用说明 |
|---|---|---|
| Agent Radius | 0.5-1.0 | 避免角色碰撞卡顿 |
| Agent Height | 2.0 | 标准角色高度 |
| Max Slope | 45° | 可攀爬坡度 |
| Step Height | 0.3 | 可跨越台阶高度 |
提示:烘焙时勾选"Advanced->Partition Type"为"Voxel",可获得更精确的导航网格
2. 多角色分路巡逻系统实现
《Among Us》式巡逻需要每个角色遵循独立的路径规则。通过NavMesh Areas可以实现这一效果:
- 在Navigation窗口的Areas标签页创建多个区域类型(如PatrolRoute1、PatrolRoute2)
- 为不同巡逻路线分配不同Area类型
- 为每个巡逻角色配置专属的Area Mask
// 巡逻角色控制器 public class PatrolAgent : MonoBehaviour { public Transform[] waypoints; public int areaMask; private NavMeshAgent agent; private int currentWaypoint = 0; void Start() { agent = GetComponent<NavMeshAgent>(); agent.areaMask = areaMask; // 设置可通行区域 agent.SetDestination(waypoints[0].position); } void Update() { if (agent.remainingDistance < 0.5f) { currentWaypoint = (currentWaypoint + 1) % waypoints.Length; agent.SetDestination(waypoints[currentWaypoint].position); } } }典型巡逻路线配置方案:
- 红色巡逻者:固定路线,高优先级Area
- 蓝色工程师:全区域通行权限
- 黄色观察者:限制区域+紧急通道
3. 动态路障与区域封锁机制
实现《Among Us》中动态放置/移除路障的效果,需要结合NavMeshObstacle和实时NavMesh更新:
public class DynamicBarrier : MonoBehaviour { private NavMeshObstacle obstacle; private bool isActive = true; void Start() { obstacle = GetComponent<NavMeshObstacle>(); obstacle.carveOnlyStationary = false; // 允许动态雕刻 obstacle.carve = true; } public void ToggleBarrier() { isActive = !isActive; obstacle.enabled = isActive; GetComponent<Collider>().enabled = isActive; // 立即更新受影响Agent的路径 if (!isActive) { NavMeshAgent[] agents = FindObjectsOfType<NavMeshAgent>(); foreach (var agent in agents) { if (agent.isOnNavMesh) { agent.SetDestination(agent.destination); } } } } }路障类型性能对比:
| 类型 | 更新开销 | 适用场景 | 注意事项 |
|---|---|---|---|
| Box | 低 | 方形障碍物 | 避免与角色碰撞体重叠 |
| Capsule | 中 | 角色大小障碍 | 更适合动态物体 |
| Mesh | 高 | 复杂形状 | 影响烘焙性能 |
4. 高级技巧:混合导航策略优化
在复杂场景中,纯Navigation系统可能遇到性能瓶颈。以下是几种优化方案:
分层导航策略:
- 静态区域使用预烘焙NavMesh
- 动态障碍使用NavMeshObstacle实时雕刻
- 频繁移动角色采用RVO避障
// RVO避障集成示例 using UnityEngine; using UnityEngine.AI; using RVO; public class HybridNavigation : MonoBehaviour { private NavMeshAgent navAgent; private RVOSimulator rvoSim; private int rvoAgentId; void Start() { navAgent = GetComponent<NavMeshAgent>(); rvoSim = RVOSimulator.Instance; rvoAgentId = rvoSim.addAgent(transform.position); } void Update() { // 从RVO系统获取修正后的速度 Vector2 rvoVelocity = rvoSim.getAgentVelocity(rvoAgentId); navAgent.velocity = new Vector3(rvoVelocity.x, 0, rvoVelocity.y); } }路径搜索优化技巧:
- 对巡逻角色使用
NavMeshAgent.SetPath()预计算完整路径 - 动态障碍区域设置
NavMeshObstacle.carveOnlyStationary=true - 使用
NavMeshQuery进行异步路径计算
5. 实战案例:《Among Us》式游戏原型实现
综合运用上述技术,我们可以构建一个完整的游戏原型:
场景设置:
- 创建包含多个房间的太空站地图
- 划分3-4条独立巡逻路线
- 设置可交互的通风管道(捷径)
角色配置:
// 红色巡逻者配置 public class RedPatrol : PatrolAgent { void Start() { base.Start(); agent.speed = 3.5f; // 较快的巡逻速度 agent.angularSpeed = 360; // 快速转向 } } // 蓝色工程师配置 public class BlueEngineer : MonoBehaviour { public GameObject barrierPrefab; void Update() { if (Input.GetMouseButtonDown(0)) { Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); if (Physics.Raycast(ray, out RaycastHit hit)) { Instantiate(barrierPrefab, hit.point, Quaternion.identity); } } } }路障交互系统:
- 工程师角色有限的路障携带量
- 路障存在时间限制(2-3分钟后自动消失)
- 特殊区域禁止放置路障
导航可视化调试:
void OnDrawGizmos() { if (agent != null && agent.hasPath) { Gizmos.color = Color.red; for (int i = 0; i < agent.path.corners.Length - 1; i++) { Gizmos.DrawLine(agent.path.corners[i], agent.path.corners[i+1]); } } }
在实际项目中测试发现,当同时存在10个以上动态障碍时,建议采用对象池管理NavMeshObstacle组件,而不是频繁实例化/销毁。另外,将角色的Stopping Distance设置为0.1-0.3可以避免巡逻时在路点处出现不自然的停顿。