Unity 自动寻路 NavMeshBuilder

在官方的Demo2(drop_plank)中主要演示的是动态加载NavMesh。

首先还是一样,我们需要布置好场景,然后设置好NavMeshAgent。和之前不一样的是,这次不在场景中设置NavMeshSurface进行烘托(Bake),因为NavMeshSurface适用于场景不在改变的情况,提前烘托好寻路的网格。但是在demo2中,场景的都是一块块分开的。只有在游戏运行的时候,通过添加平板来连接起来。这个时候需要使用NavMeshBuilder。

首先,我们在场景中能行走的物体(方块和木板)添加NavMeshSourceTag脚本。其主要内容就是将场景中添加了NavMeshSourceTag的物体的MeshFilter都存放在一个静态数组当中。在Collect方法中,创建一个NavMeshBuildSource数组,对应存放的MeshFilter,供后面使用。

[DefaultExecutionOrder(-200)]
public class NavMeshSourceTag : MonoBehaviour {
    // Global containers for all active mesh/terrain tags
    public static List<MeshFilter> m_Meshes = new List<MeshFilter>();
    public static List<Terrain> m_Terrains = new List<Terrain>();

    void OnEnable() {
        var m = GetComponent<MeshFilter>();
        if(m != null) {
            m_Meshes.Add(m);
        }

        var t = GetComponent<Terrain>();
        if(t != null) {
            m_Terrains.Add(t);
        }
    }

    void OnDisable() {
        var m = GetComponent<MeshFilter>();
        if(m != null) {
            m_Meshes.Remove(m);
        }

        var t = GetComponent<Terrain>();
        if(t != null) {
            m_Terrains.Remove(t);
        }
    }

    // Collect all the navmesh build sources for enabled objects tagged by this component
    public static void Collect(ref List<NavMeshBuildSource> sources) {
        sources.Clear();

        for(var i = 0; i < m_Meshes.Count; ++i) {
            var mf = m_Meshes[i];
            if(mf == null)
                continue;

            var m = mf.sharedMesh;
            if(m == null)
                continue;

            var s = new NavMeshBuildSource();
            s.shape = NavMeshBuildSourceShape.Mesh;
            s.sourceObject = m;
            s.transform = mf.transform.localToWorldMatrix;
            s.area = 0;
            sources.Add(s);
        }

        for(var i = 0; i < m_Terrains.Count; ++i) {
            var t = m_Terrains[i];
            if(t == null)
                continue;

            var s = new NavMeshBuildSource();
            s.shape = NavMeshBuildSourceShape.Terrain;
            s.sourceObject = t.terrainData;
            // Terrain system only supports translation - so we pass translation only to back-end
            s.transform = Matrix4x4.TRS(t.transform.position, Quaternion.identity, Vector3.one);
            s.area = 0;
            sources.Add(s);
        }
    }
}

然后Demo的场景中一个LocalNavMeshBuilder的物体有一个LocalNavMeshBuilder组件。在里面会逐帧执行NavMeshBuilder.UpdateNavMeshDataAsync(NavMeshData data, NavMeshBuildSettings buildSettings, List<NavMeshBuildSource> sources, Bounds localBounds);方法用于动态更新寻路网格。其中List<NavMeshBuildSource>就是上面NavMeshSourceTag组件中Collect获得的。

// Build and update a localized navmesh from the sources marked by NavMeshSourceTag
[DefaultExecutionOrder(-102)]
public class LocalNavMeshBuilder : MonoBehaviour {
    // The center of the build
    public Transform m_Tracked;

    // The size of the build bounds
    public Vector3 m_Size = new Vector3(80.0f, 20.0f, 80.0f);

    NavMeshData m_NavMesh;
    AsyncOperation m_Operation;
    NavMeshDataInstance m_Instance;
    List<NavMeshBuildSource> m_Sources = new List<NavMeshBuildSource>();

    IEnumerator Start() {
        while(true) {
            UpdateNavMesh(true);
            yield return m_Operation;
        }
    }

    void OnEnable() {
        // Construct and add navmesh
        m_NavMesh = new NavMeshData();
        //使NavMeshData可以被NavMeshAgent使用
        m_Instance = NavMesh.AddNavMeshData(m_NavMesh);
        if(m_Tracked == null)
            m_Tracked = transform;
        UpdateNavMesh(false);
    }

    void OnDisable() {
        // Unload navmesh and clear handle
        m_Instance.Remove();
    }

    void UpdateNavMesh(bool asyncUpdate = false) {
        NavMeshSourceTag.Collect(ref m_Sources);
        var defaultBuildSettings = NavMesh.GetSettingsByID(0);
        var bounds = QuantizedBounds();

        if(asyncUpdate)
            m_Operation = NavMeshBuilder.UpdateNavMeshDataAsync(m_NavMesh, defaultBuildSettings, m_Sources, bounds);
        else
            NavMeshBuilder.UpdateNavMeshData(m_NavMesh, defaultBuildSettings, m_Sources, bounds);
    }

    static Vector3 Quantize(Vector3 v, Vector3 quant) {
        float x = quant.x * Mathf.Floor(v.x / quant.x);
        float y = quant.y * Mathf.Floor(v.y / quant.y);
        float z = quant.z * Mathf.Floor(v.z / quant.z);
        return new Vector3(x, y, z);
    }

    Bounds QuantizedBounds() {
        // Quantize the bounds to update only when theres a 10% change in size
        var center = m_Tracked ? m_Tracked.position : transform.position;
        return new Bounds(Quantize(center, 0.1f * m_Size), m_Size);
    }

    void OnDrawGizmosSelected() {
        if(m_NavMesh) {
            Gizmos.color = Color.green;
            Gizmos.DrawWireCube(m_NavMesh.sourceBounds.center, m_NavMesh.sourceBounds.size);
        }

        Gizmos.color = Color.yellow;
        var bounds = QuantizedBounds();
        Gizmos.DrawWireCube(bounds.center, bounds.size);

        Gizmos.color = Color.green;
        var center = m_Tracked ? m_Tracked.position : transform.position;
        Gizmos.DrawWireCube(center, m_Size);
    }
}

这样每帧都会进行更新寻路的网格,当添加一个木板的时候,也会将木板的mesh计算进去形成新的寻路网格。

 

相关API:NavMeshBuilder .UpdateNavMeshDataAsync

NavMesh.AddNavMeshData

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值