Unityで動的にNavMeshを生成する

(2017-07-01)

Unity5.6から動的にNavMeshを生成できるようになった。

Unity-Technologies/NavMeshComponentsの Exampleの2_drop_blankのsceneを開く。

Exampleの2_drop_blank

分断されたCubeの床と、その上に黄色いCylindarと赤いCubeがあって、 クリックしたところに黄色いCylindarが動くんだけど、床がつながっていないのでそのままでは赤いCubeまではたどり着けない。 スペースを押すと目の前に板が出てくるのでこの上を渡って移動することができる。

スペースを押すと板が出てくる

板の上がNavMeshとして認識されている。

スペースを押すと板が出てくる

床のCubeと追加される板にはNavMeshSourceTag.csが付いていて、staticなm_Meshesm_Terrainsにそれぞれ追加している。

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メソッドでNavMeshBuildSourceのリストを生成するのに使われる。

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)
    {
       ...
    }
}

これをLocalNavMeshBuilder.csから呼び、NavMeshBuilder.UpdateNavMeshDataに渡してNavMeshDataを更新している。

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 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);
}