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)]

 

【参考】

GitHub - Unity-Technologies/NavMeshComponents: High Level API Components for Runtime NavMesh Building

Unity3D新版NavMesh系统功能初步探索 - 知乎 (zhihu.com)

 

posted @ 2022-06-28 23:36  yanghui01  阅读(702)  评论(0编辑  收藏  举报