【Unity】NavMeshのデータを実行時にロードしたい
NavMeshの地形データをランタイムにロードする
Sceneに紐付いているNavMeshではなく、動的にNavMeshの経路探索に使用するデータをロード・アンロードする方法について考えてみます。
主な用途としては以下の2つです。自分の場合は前者に当たりました。
- SceneとnavMeshを紐付けたくない場合
- 地形が変化するが動的なNavMesh計算はしたくない場合
NavMeshをベイクする
まずSceneに配置したメッシュの情報を利用してNavMeshデータを構築します。
もしNavMeshComponentsを導入している場合、NavMeshSurfaceをシーン内に配置しBakeボタンを押します。これでSceneファイル以下にNavMeshDataが構築されます。これにNavMeshのデータが格納されています。
一方、NavMeshComponentを使用しない場合、エディターAPIを使ってNavMeshを構築します。使用するAPIはNavMeshBuilder.CollectSourcesInStage
とNavMeshBuilder.BuildNavMeshData
です。今回の場合、動的にロードすることが目的なのでResourcesに突っ込んでいます。実際にはAddressableなりAssetBundleなりに格納する形になると思います。
- 下のコードを導入
- NavMeshを作りたいステージの親オブジェクトに
BuildNavMesh
を追加 - コンテキストメニューから
CreateNavmesh
を選択
using System.Collections.Generic; using UnityEngine; using UnityEngine.AI; public class BuildNavMesh : MonoBehaviour { #if UNITY_EDITOR [ContextMenu("CreateNavmesh")] void Create() { var sources = new List<NavMeshBuildSource>(); var markups = new List<NavMeshBuildMarkup>(); var settings = NavMesh.GetSettingsByID(0); var bounds = new Bounds(Vector3.zero, 1000.0f * Vector3.one); // Navmeshをビルドする対象を収集する UnityEditor.AI.NavMeshBuilder.CollectSourcesInStage( root: transform, // nullの場合Scene全体が含まれます。transformを指定した場合はルートとその子のみを考慮します。 includedLayerMask: ~0, // クェリに含めるレイヤー geometry: NavMeshCollectGeometry.RenderMeshes, // 収集するジオメトリを選択します。レンダラーかコライダー defaultArea: 0, // 割り当てるエリアタイプ markups: markups, // 収集方法についてのマークアップリスト(含めないエリアとか色々) stageProxy: gameObject.scene, // 所属するシーン results: sources); // ベイクに使用するジオメトリのリスト(out) // 実際にビルドする var navmesh = NavMeshBuilder.BuildNavMeshData( buildSettings: settings, // ベイク処理の設定 sources: sources, // ベイクに使用するジオメトリのリスト localBounds: bounds, // NavMeshを構築する範囲 position: transform.position, // NavMeshの原点 rotation: transform.rotation); // NavMeshの向き var exportPath = $"Assets/Resources/{gameObject.name}.asset"; UnityEditor.AssetDatabase.CreateAsset(navmesh, exportPath); UnityEditor.AssetDatabase.Refresh(); } #endif }
NavMeshをロードする
最後にNavMeshDataを現在のシーンに追加します。NavMeshの追加自体はNavMesh.AddNavMeshData(NavMeshData)
でOKです。この時に取得するハンドルは後でNavMeshDataを破棄する際に使用されるので大切に保存しましょう。
using UnityEngine; using UnityEngine.AI; public class LoadNavmesh : MonoBehaviour { [SerializeField] string assetname = "GameObject"; private NavMeshDataInstance instance; void OnEnable() { // NavMeshの登録 var data = Resources.Load<NavMeshData>(assetname); instance = NavMesh.AddNavMeshData(data); } void OnDisable() { // NavMeshの破棄 NavMesh.RemoveNavMeshData(instance); } }
補足
NavMeshの動的なロードを行う場合、NavMeshのロードが完了したかを判断するフラグを必ず設定しておくと良いです。NavMeshDataのロード前にNavMesh関連の処理が走るとエラーになります。