NavMesh-动态生成寻路数据
# 实现效果
# 地面ground, 连接地面的bridge, 寻路数据都是在运行时生成的,而不是一开始bake生成好的
# 动态生成寻路数据的核心类NavMeshBuilder
使用NavMeshBuilder.UpdateNavMeshData来生成寻路数据。对于物体很多的情况建议使用异步版本UpdateNavMeshDataAsync来避免卡顿
# 其他涉及的类
NavMeshData
NavMeshDataInstance
NavMeshBuildSource
NavMeshBuildSettings
# 这边还用到了相机控制和角色行走的代码(参考之前的博文,分别挂在Main Camera和Player/Capsule上了),下面的脚本挂在NavMeshBuild上了
# 鼠标左键点击ground之间的柱子,就可以生成两个地面之间的桥梁(黄色)
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.AI; [DefaultExecutionOrder(-102)] public class NavMeshTest2 : MonoBehaviour { public Transform bridgePrefab; public Vector3 _navMeshSize = new Vector3(10, 10, 10); public Transform _navMeshBuildSource; private Bounds _tempBounds = new Bounds(); private NavMeshData _navMeshData; private NavMeshDataInstance _navMeshDataInst; private List<MeshFilter> _meshFilterList = new List<MeshFilter>(); private List<NavMeshBuildSource> _navMeshBuildSourceList = new List<NavMeshBuildSource>(); private AsyncOperation _operation; void OnEnable() { if (null == _navMeshData) { _navMeshData = new NavMeshData(); _navMeshDataInst = NavMesh.AddNavMeshData(_navMeshData); } UpdateNavMeshBuildSource(); UpdateNavMesh(); } private void UpdateNavMeshBuildSource() { _meshFilterList.Clear(); _navMeshBuildSourceList.Clear(); _navMeshBuildSource.GetComponentsInChildren<MeshFilter>(false, _meshFilterList); for (var i = 0; i < _meshFilterList.Count; ++i) { var meshFilter = _meshFilterList[i]; if (meshFilter == null) continue; var mesh = meshFilter.sharedMesh; if (mesh == null) continue; var s = new NavMeshBuildSource(); s.shape = NavMeshBuildSourceShape.Mesh; s.sourceObject = mesh; s.transform = meshFilter.transform.localToWorldMatrix; s.area = 0; _navMeshBuildSourceList.Add(s); } } private void UpdateNavMesh() { var defaultSettings = NavMesh.GetSettingsByID(0); _tempBounds.center = transform.position; _tempBounds.size = _navMeshSize; NavMeshBuilder.UpdateNavMeshData(_navMeshData, defaultSettings, _navMeshBuildSourceList, _tempBounds); //var operation = NavMeshBuilder.UpdateNavMeshDataAsync(_navMeshData, defaultSettings, _navMeshBuildSourceList, _navMeshBuildBounds); } void Update() { if (Input.GetMouseButtonUp(0)) { const int maxDistance = 300; var ray = Camera.main.ScreenPointToRay(Input.mousePosition); //摄像机方向发射1条射线 if (Physics.Raycast(ray, out var hit, maxDistance, 1 << LayerMask.NameToLayer("bridge raycast"))) { if (null == _navMeshBuildSource.Find(hit.transform.name)) { var bridgeTrans = GameObject.Instantiate<Transform>(bridgePrefab, _navMeshBuildSource, false); bridgeTrans.gameObject.SetActive(true); var pos = hit.point; pos.y = bridgeTrans.position.y; bridgeTrans.position = pos; bridgeTrans.name = hit.transform.name; UpdateNavMeshBuildSource(); UpdateNavMesh(); } return; } } } void OnDrawGizmosSelected() { Gizmos.color = Color.yellow; Gizmos.DrawWireCube(transform.position, _navMeshSize); } }
# 一定要把脚本的执行顺序设置的比NavMeshAgent先执行,否则会有以下警告信息。这边设置了[DefaultExecutionOrder(-102)]
【参考】
Unity3D新版NavMesh系统功能初步探索 - 知乎 (zhihu.com)