/// Stores a set of navmesh tiles which can be placed on a recast graph.
///
/// This component is used to store chunks of a <see cref="RecastGraph"/> to a file and then be able to efficiently load them and place them on an existing recast graph.
/// A typical use case is if you have a procedurally generated level consisting of multiple rooms, and scanning the graph after the level has been generated
/// is too expensive. In this scenario, each room can have its own NavmeshPrefab component which stores the navmesh for just that room, and then when the
/// level is generated all the NavmeshPrefab components will load their tiles and place them on the recast graph, joining them together at the seams.
///
/// Since this component works on tiles, the size of a NavmeshPrefab must be a multiple of the graph's tile size.
/// The tile size of a recast graph is determined by multiplying the <see cref="RecastGraph.cellSize"/> with the tile size in voxels (<see cref="RecastGraph.editorTileSize"/>).
/// When a NavmeshPrefab is placed on a recast graph, it will load the tiles into the closest spot (snapping the position and rotation).
/// The NavmeshPrefab will even resize the graph to make it larger in case you want to place a NavmeshPrefab outside the existing bounds of the graph.
///
/// <b>Usage</b>
///
/// - Attach a NavmeshPrefab component to a GameObject (typically a prefab) that you want to store the navmesh for.
/// - Make sure you have a RecastGraph elsewhere in the scene with the same settings that you use for the game.
/// - Adjust the bounding box to fit your game object. The bounding box should be a multiple of the tile size of the recast graph.
/// - In the inspector, click the "Scan" button to scan the graph and store the navmesh as a file, referenced by the NavmeshPrefab component.
/// - Make sure the rendered navmesh looks ok in the scene view.
/// - In your game, instantiate a prefab with the NavmeshComponent. It will automatically load its stored tiles and place them on the first recast graph in the scene.
///
/// If you have multiple recast graphs you may not want it to always use the first recast graph.
/// In that case you can set the <see cref="applyOnStart"/> field to false and call the <see cref="Apply(RecastGraph)"/> method manually.
///
/// <b>Accounting for borders</b>
///
/// When scanning a recast graph (and by extension a NavmeshPrefab), a margin is always added around parts of the graph the agent cannot traverse.
/// This can become problematic when scanning individual chunks separate from the rest of the world, because each one will have a small border of unwalkable space.
/// The result is that when you place them on a recast graph, they will not be able to connect to each other.
/// [Open online documentation to see images]
/// One way to solve this is to scan the prefab together with a mesh that is slightly larger than the prefab, extending the walkable surface enough
/// so that no border is added. In the image below, this mesh is displayed in white. It can be convenient to make this an invisible collider on the prefab
/// that is excluded from physics, but is included in the graph's rasterization layer mask.
/// [Open online documentation to see images]
/// Now that the border has been removed, the chunks can be placed next to each other and be able to connect.
/// [Open online documentation to see images]
///
/// <b>Loading tiles into a graph</b>
///
/// If <see cref="applyOnStart"/> is true, the tiles will be loaded into the first recast graph in the scene when the game starts.
/// If the recast graph is not scanned, it will be initialized with empty tiles and then the tiles will be loaded into it.
/// So if your world is made up entirely of NavmeshPrefabs, you can skip scanning for performance by setting A* Inspector -> Settings -> Scan On Awake to false.
///
/// You can also apply a NavmeshPrefab to a graph manually by calling the <see cref="Apply(RecastGraph)"/> method.
///
/// Note: A navmesh prefab will fully replace the tiles within its bounding box. You cannot stack multiple navmesh prefabs on top of each other
/// Rounds the size of the <see cref="bounds"/> to the closest multiple of the tile size in the graph, ensuring that the bounds cover at least 1x1 tiles.
/// The new bounds has the same center and size along the y-axis.
thrownewSystem.Exception("NavmeshPrefab has been scanned with a different size than it is right now (or with a different graph). Expected to find "+tileRect.Width+"x"+tileRect.Height+" tiles, but found "+tileMeshes.tileRect.Width+"x"+tileMeshes.tileRect.Height);
}
tileMeshes.tileRect=tileRect;
graph.ReplaceTiles(tileMeshes,yOffset);
UnityEngine.Profiling.Profiler.EndSample();
});
}
/// <summary>Scans the navmesh using the first recast graph in the scene, and returns a serialized byte representation</summary>
publicbyte[]Scan(){
// Make sure this method works even when called in the editor outside of play mode.
AstarPath.FindAstarPath();
if(AstarPath.active==null||AstarPath.active.data.recastGraph==null)thrownewSystem.InvalidOperationException("There's no recast graph in the scene. Add one if you want to scan this navmesh prefab.");
returnScan(AstarPath.active.data.recastGraph);
}
/// <summary>Scans the navmesh and returns a serialized byte representation</summary>
publicbyte[]Scan(RecastGraphgraph){
// Schedule the jobs asynchronously, but immediately wait for them to finish
varresult=ScanAsync(graph).Complete();
vardata=result.data;
// Dispose of all the unmanaged memory
result.Dispose();
returndata;
}
/// <summary>
/// Scans the navmesh asynchronously and returns a promise of a byte representation.
///
/// TODO: Maybe change this method to return a <see cref="TileMeshes"/> object instead?