Custom Navmesh Generation
舉例而言,自訂Quantum導航網格的生成或嵌入的理由是:
- 為了生成更高的準確性,以使代理能夠在3D導航網格上正確的行走。
- 為了在運行階段動態地生成一個導航網格(警告:SDK仍然有部分的工具鏈不是確定性,請聯絡我們)。
這個章節將提供一個概述,說明如何延展導航網格工具。
生成嵌入資料
建議的流程是生成中繼導航網格格式MapNavMesh.BakeData
及透過MapNavMeshBaker.BakeNavMesh(MapData data, MapNavMesh.BakeData navmeshBakeData)
運行這個,其使用來自BakeData
的三角形資訊以填入Quantum導航網格的所需的資料架構。
MapNavMeshBaker.BakeNavMesh()
被開發為用於編輯的時候,並且完全地替換它可能產生效能改善,但也可能是一個更加耗費心力的工作。
MapNavMesh.BakeData層級
類型 | 欄位 | 說明 |
---|---|---|
字串 | 名稱 | 導航網格的名稱,透過f.Map.NavMeshes[name] 在模擬中可存取 |
向量3 | 位置 | 導航網格的位置。最終導航網格頂點儲存在全域空間,並且它們的位置在嵌入時由此來翻譯。 |
FP | 代理半徑 | 建立導航網格的最大的代理的半徑。較舊的Quantum版本曾允許不同的代理半徑,但是這已經被廢除。現在代理可以一直向前走,直到它們的樞紐位於導航網格的邊緣上。這樣代理應該遠離牆壁的邊界地方將被嵌入三角形之中。此值只用於轉譯偵錯圖形。 |
清單<字串> | 區域 | 用於這個導航網格的所有區域識別碼。在嵌入區域時,識別碼會被新增到地圖資產的區域清單,並且它們的索引被嵌入到導航網格三角形區域遮罩(NavMeshTriangle.Regions )。區域被彙總在地圖上,因為一個地圖可以有多個導航網格,並且它們可以共享區域識別碼。 |
地圖導航網格頂點[] | 頂點 | 導航網格的頂點。 |
地圖導航網格三角形[] | 三角形 | 導航網格的三角形。這是一個常規的網格資料架構,其中三角形及頂點被保存在兩個分離的陣列中,並且三角形指向頂點陣列以標記它們的3個頂點。 |
地圖導航網格連結[] | Links | 在同樣的導航網格上的位置之間的連結。 |
列舉 | 最近三角形計算 | Quantum導航網格使用一個格線來進行空間分割。各個格線單元格將被指派一個遞補三角形。預設搜尋相當緩慢(BruteForce ),雖然SpiralOut 更有效率,但是它可能導致空的遞補三角形。 |
整數 | 最近三角形計算深度 | 格線單元格數量以延展SpiralOut 搜尋。 |
bool | 啟用Quantum_XY | 當啟用後,導航網格嵌入將翻轉頂點位置的Y及Z元件,以支援在XY平面生成的導航網格。 |
布林值 | 連結錯誤校正 | 在嵌入時自動地校正導航網格位置到最近的三角形。 |
地圖導航網格三角形層級
三角形應具有順時鐘的捲繞順序。
不是所有欄位都應該被填入。有些欄位只用於傳統導航網格繪製工具。
類型 | 欄位 | 說明 |
---|---|---|
字串 | Id | 不需要 |
字串[] | 頂點識別碼 | 至少有3個單位的長度。被參照的頂點作為識別碼。SDK 2.1.或更早版本所需要。 |
整數32[] | 頂點識別碼2 | 至少有3個單位的長度。被參照的頂點作為索引被放入陣列中。SDK 2.2.版本所需要。 |
整數32 | 區域 | 不需要 |
字串 | 區域識別碼 | 這個三角形屬於的區域。預設是空值 。 |
FP | 成本 | 三角形的成本。預設應為FP._1 。 |
地圖導航網格頂點層級
Position
的類型已經由FPVector3
SDK 2.2.所取代。
類型 | 欄位 | 說明 |
---|---|---|
字串 | 識別碼 | SDK 2.1或更早版本所需要 |
向量3 | 位置 | 頂點的位置 |
清單<整數32> | 芳鄰 | 不需要 |
清單<整數32> | 三角形 | 不需要 |
地圖導航網格連結層級
Start
、End
及CostOveride
的類型在SDK 2.2中已經各自由FPVector3
及FP
所取代。
類型 | 欄位 | 說明 |
---|---|---|
向量3 | 開始 | 連結的開始位置。必須在同樣的導航網格上。 |
向量3 | 結束 | 連結的結束位置。必須在同樣的導航網格上。 |
布林值 | 雙向 | 連結可從兩個方向周遊。 |
浮點數 | 成本覆寫 | 連線的成本。 |
字串 | 區域識別碼 | 連結屬於的區域識別碼。預設是空值 。 |
字串 | 名稱 | 連結的名稱。可透過navmesh.Links[NavMeshPathfinder.CurrentLink()].Name 來查詢。 |
程式碼片段
C#
// Generate simple navmesh BakeData
var bakeData = new MapNavMesh.BakeData() {
AgentRadius = FP._0_20,
ClosestTriangleCalculation = MapNavMesh.FindClosestTriangleCalculation.SpiralOut,
ClosestTriangleCalculationDepth = 1,
Name = "DynamicNavmesh",
PositionFP = FPVector3.Zero,
Regions = new System.Collections.Generic.List<string>(),
Vertices = new MapNavMeshVertexFP[] {
new MapNavMeshVertexFP { Position = FPVector3.Forward },
new MapNavMeshVertexFP { Position = FPVector3.Right },
new MapNavMeshVertexFP { Position = -FPVector3.Forward},
new MapNavMeshVertexFP { Position = -FPVector3.Right},
},
Triangles = new MapNavMeshTriangle[] {
new MapNavMeshTriangle { VertexIds2 = new int[] { 0, 1, 2}, Cost = FP._1 },
new MapNavMeshTriangle { VertexIds2 = new int[] { 0, 2, 3}, Cost = FP._1 }
}
};
在啟動模擬之前替換導航網格資產
在啟動模擬之前替換一個現存的Unity Quantum導航網格資產的內容。所有的客戶端及延遲加入者需要執行這件事。
要求:
- 針對SDK 2.1的
navmesh-deterministic-baking
分支(請聯絡我們) BakeData
生成是確定性的
替換資產必須在透過UnityDB
載入導航網格之前及啟動模擬之前完成。在這個程式碼片段中,載入Unity導航網格資產以取代在它之中的Quantum資產(.Settings
),並且複製Guid
及Path
值。使.DataAsset
失效將防止二進位導航網格資產(_data
資產)的還原序列化,這是在當其最終由Quantum載入的時候。
C#
var navmesh = MapNavMeshBaker.BakeNavMesh(mapdata.Asset.Settings, bakeData, null);
var navmeshAsset = UnityEngine.Resources.Load<NavMeshAsset>("PathToNavmeshAsset");
navmesh.Guid = navmeshAsset.Settings.Guid;
navmesh.Path = navmeshAsset.Settings.Path;
navmeshAsset.Settings = navmesh;
navmeshAsset.Settings.DataAsset.Id = AssetGuid.Invalid;
// QuantumRunner.StartGame()
在運行階段插入導航網格
地圖資產的限制
Map
資產帶有兩個查閱,在載入地圖及相關導航網格時,用於方便地進行被填入的導航網格及區域名稱查閱。兩個查閱都不能用於動態導航網格資產。
C#
public Dictionary<String, NavMesh> NavMeshes;
public Dictionary<String, Int32> RegionMap;
public NavMesh GetNavMesh(String name) {}
GetNavMesh()替代方案
在Map
(f.Map.GetNavMesh(name)
)中的導航網格查閱不能用於動態導航網格,因為在Map
上的字典的修改不能用於延遲加入者或重新連線玩家。取而代之地,使用這個程式碼片段,以搜尋所有導航網格:
C#
public static NavMesh FindNavmeshByName(Frame f, string name) {
var result = f.Map.GetNavMesh(name);
if (result != null) {
return result;
}
foreach (var a in f.DynamicAssetDB.Assets) {
if (a is NavMesh navmeshAsset) {
if (navmeshAsset.Name == name) {
return navmeshAsset;
}
}
}
return null;
}
替代性地,建立及管理一個儲存在f.Globals
上的導航網格查閱:
C#
global {
dictionary<QStringUtf8<32>, asset_ref<NavMesh>> Navmeshes;
}
在動態導航網格上使用區域
如果一個動態導航網格有自己的區域,它必須重新使用所有由靜態地圖及其他動態區域載入的區域。如果它有新的區域,它們不被允許使用已經使用的相同的區域識別碼。切換將無法正常使用。
最好離線建立一個靜態RegionMap
,並且在動態生成導航網格中使用它。在Map.Loaded()
時從地圖Region
成員來建立RegionMap
。
C#
public string[] Regions;
在模擬中插入導航網格
確定性地建立NavmeshBakeData
,並且在運行階段在模擬中嵌入一個Quantum導航網格。使用Quantum動態資料庫。
要求:
- 針對SDK 2.1,
navmesh-deterministic-baking
分支(請聯絡我們) NavmeshBakeData
的建立需要是確定性的- 必須在一個
verified
幀時被執行
導航網格嵌入程式碼已經被移動到(在2.1中已經被複製到)quantum.code
專案,以能夠在Unity外運行。
C#
using Quantum.Experimental;
// May be more buffer required for serialization
private static byte[] _byteStreamData = new byte[1024 * 1024];
// Generate bake data
var bakeData = new NavmeshBakeData() {
AgentRadius = FP._0_20,
ClosestTriangleCalculation = NavMeshBakeDataFindClosestTriangle.SpiralOut,
ClosestTriangleCalculationDepth = 1,
Name = "DynamicNavmesh",
PositionFP = FPVector3.Zero,
Regions = new List<string>(),
Vertices = new Experimental.NavmeshBakeDataVertex[] {
new NavmeshBakeDataVertex { Position = FPVector3.Forward },
new NavmeshBakeDataVertex { Position = FPVector3.Right },
new NavmeshBakeDataVertex { Position = -FPVector3.Forward},
new NavmeshBakeDataVertex { Position = -FPVector3.Right},
},
Triangles = new NavmeshBakeDataTriangle[] {
new NavmeshBakeDataTriangle { VertexIds = new int[] { 0, 1, 2}, Cost = FP._1 },
new NavmeshBakeDataTriangle { VertexIds = new int[] { 0, 2, 3}, Cost = FP._1 }
}
};
// Bake navmesh asset
var navmesh = NavmeshBaker.BakeNavMesh(f.Map, bakeData);
// Create and add binary navmesh data asset (to support late joiners)
var byteStream = new ByteStream(_byteStreamData);
navmesh.Serialize(byteStream, true);
var binaryDataAsset = new BinaryData();
binaryDataAsset.Data = byteStream.ToArray();
var binaryDataAssetRef = new AssetRefBinaryData();
binaryDataAssetRef.Id = f.AddAsset(binaryDataAsset);
navmesh.DataAsset = binaryDataAssetRef;
// Add navmesh to Dynamic DB
f.AddAsset(navmesh);
同時使用來自下一個章節的FindNavmeshByName()
程式碼片段,以正確地透過名稱找到動態導航網格資產。
從Unity插入導航網格
用於延遲加入者。必須由 一個 客戶端初始化。
要求:
- 最新的動態資產插入附加元件
在Unity中在 一個 客戶端上建立BakeData
,並且使用資產插入命令:
C#
var map = QuantumRunner.Default.Game.Frames.Verified.Map;
// Bake navmesh
var navmesh = MapNavMeshBaker.BakeNavMesh(map, bakeData, null);
var data = AssetInjectionUtility.SerializeAsset(null, navmesh);
// Adjust PlayerRef 0
AssetInjectionUtility.InjectAsset(QuantumRunner.Default.Game, 0, bakeData.Name, data);
為了轉譯動態導航網格的Gizmos,在QuantumGameGizmos.cs
中更改下列行:
C#
// ################## NavMeshes ##################
if (editorSettings.DrawNavMesh) {
var listOfNavmeshes = new System.Collections.Generic.List<NavMesh>();
if (editorSettings.DrawNavMesh) {
listOfNavmeshes.AddRange(frame.Map.NavMeshes.Values);
}
if (frame.DynamicAssetDB.IsEmpty == false) {
listOfNavmeshes.AddRange(frame.DynamicAssetDB.Assets.Where(a => a is NavMesh).Select(a => (NavMesh)a).ToList());
}
foreach (var navmesh in listOfNavmeshes) {
// ...
}
}
Back to top