/// When generating a recast graph what happens is that the world is voxelized.
/// You can think of this as constructing an approximation of the world out of lots of boxes.
/// If you have played Minecraft it looks very similar (but with smaller boxes).
/// [Open online documentation to see images]
///
/// The Recast process is described as follows:
/// - The voxel mold is build from the input triangle mesh by rasterizing the triangles into a multi-layer heightfield.
/// Some simple filters are then applied to the mold to prune out locations where the character would not be able to move.
/// - The walkable areas described by the mold are divided into simple overlayed 2D regions.
/// The resulting regions have only one non-overlapping contour, which simplifies the final step of the process tremendously.
/// - The navigation polygons are peeled off from the regions by first tracing the boundaries and then simplifying them.
/// The resulting polygons are finally converted to triangles which makes them perfect for pathfinding and spatial reasoning about the level.
///
/// The recast generation process usually works directly on the visiable geometry in the world. This is usually a good thing, because world geometry is usually more detailed than the colliders.
/// You can, however, specify that colliders should be rasterized instead. If you have very detailed world geometry, this can speed up scanning and updating the graph.
///
/// \section export Exporting for manual editing
/// In the editor there is a button for exporting the generated graph to a .obj file.
/// Usually the generation process is good enough for the game directly, but in some cases you might want to edit some minor details.
/// So you can export the graph to a .obj file, open it in your favourite 3D application, edit it, and export it to a mesh which Unity can import.
/// You can then use that mesh in a navmesh graph.
///
/// Since many 3D modelling programs use different axis systems (unity uses X=right, Y=up, Z=forward), it can be a bit tricky to get the rotation and scaling right.
/// For blender for example, what you have to do is to first import the mesh using the .obj importer. Don't change anything related to axes in the settings.
/// Then select the mesh, open the transform tab (usually the thin toolbar to the right of the 3D view) and set Scale -> Z to -1.
/// If you transform it using the S (scale) hotkey, it seems to set both Z and Y to -1 for some reason.
/// Then make the edits you need and export it as an .obj file to somewhere in the Unity project.
/// But this time, edit the setting named "Forward" to "Z forward" (not -Z as it is per default).
/// Radius of the agent which will traverse the navmesh.
/// The navmesh will be eroded with this radius.
///
/// This value will be rounded up to the nearest multiple of <see cref="cellSize"/>.
///
/// [Open online documentation to see images]
/// </summary>
publicfloatcharacterRadius=0.5F;
/// <summary>
/// Max distance from simplified edge to real edge.
/// This value is measured in voxels. So with the default value of 2 it means that the final navmesh contour may be at most
/// 2 voxels (i.e 2 times <see cref="cellSize)"/> away from the border that was calculated when voxelizing the world.
/// A higher value will yield a more simplified and cleaner navmesh while a lower value may capture more details.
/// However a too low value will cause the individual voxels to be visible (see image below).
///
/// [Open online documentation to see images]
///
/// See: <see cref="cellSize"/>
/// </summary>
[JsonMember]
publicfloatcontourMaxError=2F;
/// <summary>
/// Voxel sample size (x,z).
/// When generating a recast graph what happens is that the world is voxelized.
/// You can think of this as constructing an approximation of the world out of lots of boxes.
/// If you have played Minecraft it looks very similar (but with smaller boxes).
/// [Open online documentation to see images]
/// The cell size is the width and depth of those boxes. The height of the boxes is usually much smaller
/// and automatically calculated, however.
///
/// Lower values will yield higher quality navmeshes, however the graph will be slower to scan.
///
/// [Open online documentation to see images]
/// </summary>
[JsonMember]
publicfloatcellSize=0.25F;
/// <summary>
/// Character height.
/// [Open online documentation to see images]
/// </summary>
[JsonMember]
publicfloatwalkableHeight=2F;
/// <summary>
/// Height the character can climb.
/// [Open online documentation to see images]
/// </summary>
[JsonMember]
publicfloatwalkableClimb=0.5F;
/// <summary>
/// Max slope in degrees the character can traverse.
/// [Open online documentation to see images]
/// </summary>
[JsonMember]
publicfloatmaxSlope=30;
/// <summary>
/// Longer edges will be subdivided.
/// Reducing this value can sometimes improve path quality since similarly sized triangles
/// yield better paths than really large and really triangles small next to each other.
/// However it will also add a lot more nodes which will make pathfinding slower.
/// For more information about this take a look at navmeshnotes (view in online documentation for working links).
///
/// [Open online documentation to see images]
/// </summary>
[JsonMember]
publicfloatmaxEdgeLength=20;
/// <summary>
/// Minumum region size.
/// Small regions will be removed from the navmesh.
/// Measured in voxels.
///
/// [Open online documentation to see images]
///
/// If a region is adjacent to a tile border, it will not be removed
/// even though it is small since the adjacent tile might join it
/// to form a larger region.
///
/// [Open online documentation to see images]
/// [Open online documentation to see images]
/// </summary>
[JsonMember]
publicfloatminRegionSize=3;
/// <summary>
/// Size in voxels of a single tile.
/// This is the width of the tile.
///
/// [Open online documentation to see images]
///
/// A large tile size can be faster to initially scan (but beware of out of memory issues if you try with a too large tile size in a large world)
/// smaller tile sizes are (much) faster to update.
///
/// Different tile sizes can affect the quality of paths. It is often good to split up huge open areas into several tiles for
/// better quality paths, but too small tiles can also lead to effects looking like invisible obstacles.
/// For more information about this take a look at navmeshnotes (view in online documentation for working links).
/// Usually it is best to experiment and see what works best for your game.
///
/// When scanning a recast graphs individual tiles can be calculated in parallel which can make it much faster to scan large worlds.
/// When you want to recalculate a part of a recast graph, this can only be done on a tile-by-tile basis which means that if you often try to update a region
/// of the recast graph much smaller than the tile size, then you will be doing a lot of unnecessary calculations. However if you on the other hand
/// update regions of the recast graph that are much larger than the tile size then it may be slower than necessary as there is some overhead in having lots of tiles
/// instead of a few larger ones (not that much though).
///
/// Recommended values are between 64 and 256, but these are very soft limits. It is possible to use both larger and smaller values.
/// </summary>
[JsonMember]
publicinteditorTileSize=128;
/// <summary>
/// Size of a tile along the X axis in voxels.
/// \copydetails editorTileSize
///
/// Warning: Do not modify, it is set from <see cref="editorTileSize"/> at Scan
///
/// See: <see cref="tileSizeZ"/>
/// </summary>
[JsonMember]
publicinttileSizeX=128;
/// <summary>
/// Size of a tile along the Z axis in voxels.
/// \copydetails editorTileSize
///
/// Warning: Do not modify, it is set from <see cref="editorTileSize"/> at Scan
///
/// See: <see cref="tileSizeX"/>
/// </summary>
[JsonMember]
publicinttileSizeZ=128;
/// <summary>
/// If true, divide the graph into tiles, otherwise use a single tile covering the whole graph.
///
/// Using tiles is useful for a number of things. But it also has some drawbacks.
/// - Using tiles allows you to update only a part of the graph at a time. When doing graph updates on a recast graph, it will always recalculate whole tiles (or the whole graph if there are no tiles).
/// <see cref="NavmeshCut"/> components also work on a tile-by-tile basis.
/// - Using tiles allows you to use <see cref="NavmeshPrefab"/>s.
/// - Using tiles can break up very large triangles, which can improve path quality in some cases, and make the navmesh more closely follow the y-coordinates of the ground.
/// - Using tiles can make it much faster to generate the navmesh, because each tile can be calculated in parallel.
/// But if the tiles are made too small, then the overhead of having many tiles can make it slower than having fewer tiles.
/// - Using small tiles can make the path quality worse in some cases, but setting the <see cref="FunnelModifier"/>s quality setting to high (or using <see cref="RichAI.funnelSimplification"/>) will mostly mitigate this.
///
/// See: <see cref="editorTileSize"/>
///
/// Since: Since 4.1 the default value is true.
/// </summary>
[JsonMember]
publicbooluseTiles=true;
/// <summary>
/// If true, scanning the graph will yield a completely empty graph.
/// Useful if you want to replace the graph with a custom navmesh for example
///
/// Note: This is mostly obsolete now that the <see cref="EnsureInitialized"/> and <see cref="ReplaceTiles"/> functions exist.
/// </summary>
publicboolscanEmptyGraph;
publicenumRelevantGraphSurfaceMode{
/// <summary>No RelevantGraphSurface components are required anywhere</summary>
DoNotRequire,
/// <summary>
/// Any surfaces that are completely inside tiles need to have a <see cref="RelevantGraphSurface"/> component
/// positioned on that surface, otherwise it will be stripped away.
/// </summary>
OnlyForCompletelyInsideTile,
/// <summary>
/// All surfaces need to have one <see cref="RelevantGraphSurface"/> component
/// positioned somewhere on the surface and in each tile that it touches, otherwise it will be stripped away.
/// Only tiles that have a RelevantGraphSurface component for that surface will keep it.
/// </summary>
RequireForAll
}
/// <summary>Whether to use 3D or 2D mode</summary>
publicenumDimensionMode{
/// <summary>Allows the recast graph to use 2D colliders</summary>
Dimension2D,
/// <summary>Allows the recast graph to use 3D colliders, 3D meshes and terrains</summary>
Dimension3D,
}
/// <summary>
/// Whether the base of the graph should default to being walkable or unwalkable.
/// <summary>Settings for which meshes/colliders and other objects to include in the graph</summary>
[System.Serializable]
publicclassCollectionSettings{
/// <summary>Determines how the initial filtering of objects is done</summary>
publicenumFilterMode{
/// <summary>Use a layer mask to filter objects</summary>
Layers,
/// <summary>Use tags to filter objects</summary>
Tags,
}
/// <summary>
/// Determines how the initial filtering of objects is done.
///
/// See: <see cref="layerMask"/>
/// See: <see cref="tagMask"/>
/// </summary>
publicFilterModecollectionMode=FilterMode.Layers;
/// <summary>
/// The physics scene for collecting colliders when scanning the graph.
///
/// If null (the default), the physics scene that the <see cref="AstarPath"/> component is part of will be used.
///
/// You typically don't have to set this, but it can be useful in some rare situations.
///
/// Note: This field cannot be serialized, so you must set it via code before the graphs are scanned.
///
/// Only used if <see cref="rasterizeColliders"/> is enabled.
///
/// See: <see cref="physicsScene2D"/>
/// </summary>
[System.NonSerialized]
publicPhysicsScene?physicsScene=null;
/// <summary>
/// The physics scene for collecting 2D colliders when scanning the graph.
///
/// If null (the default), the physics scene that the <see cref="AstarPath"/> component is part of will be used.
///
/// You typically don't have to set this, but it can be useful in some rare situations.
///
/// Note: This field cannot be serialized, so you must set it via code before the graphs are scanned.
///
/// Only used if <see cref="rasterizeColliders"/> is enabled.
///
/// See: <see cref="physicsScene"/>
/// </summary>
[System.NonSerialized]
publicPhysicsScene2D?physicsScene2D=null;
/// <summary>
/// Objects in all of these layers will be rasterized.
///
/// Will only be used if <see cref="collectionMode"/> is set to Layers.
///
/// See: <see cref="tagMask"/>
/// </summary>
publicLayerMasklayerMask=-1;
/// <summary>
/// Objects tagged with any of these tags will be rasterized.
///
/// Will only be used if <see cref="collectionMode"/> is set to Tags.
///
/// See: <see cref="layerMask"/>
/// </summary>
publicList<string>tagMask=newList<string>();
/// <summary>
/// Use colliders to calculate the navmesh.
///
/// Depending on the <see cref="dimensionMode"/>, either 3D or 2D colliders will be rasterized.
///
/// Sphere/Capsule/Circle colliders will be approximated using polygons, with the precision specified in <see cref="colliderRasterizeDetail"/>.
///
/// Note: In 2D mode, this is always treated as enabled, because no other types of inputs (like meshes or terrains) are supported.
/// </summary>
publicboolrasterizeColliders=true;
/// <summary>
/// Use scene meshes to calculate the navmesh.
///
/// This can get you higher precision than colliders, since colliders are typically very simplified versions of the mesh.
/// However, it is often slower to scan, and graph updates can be particularly slow.
///
/// The reason that graph updates are slower is that there's no efficient way to find all meshes that intersect a given tile,
/// so the graph has to iterate over all meshes in the scene just to find the ones relevant for the tiles that you want to update.
/// Colliders, on the other hand, can be efficiently queried using the physics system.
///
/// You can disable this and attach a <see cref="RecastNavmeshModifier"/> component (with dynamic=false) to all meshes that you want to be included in the navmesh instead.
/// That way they will be able to be efficiently queried for, without having to iterate through all meshes in the scene.
///
/// In 2D mode, this setting has no effect.
/// </summary>
publicboolrasterizeMeshes;
/// <summary>
/// Use terrains to calculate the navmesh.
///
/// In 2D mode, this setting has no effect.
/// </summary>
publicboolrasterizeTerrain=true;
/// <summary>
/// Rasterize tree colliders on terrains.
///
/// If the tree prefab has a collider, that collider will be rasterized.
/// Otherwise a simple box collider will be used and the script will
/// try to adjust it to the tree's scale, it might not do a very good job though so
/// an attached collider is preferable.
///
/// Note: It seems that Unity will only generate tree colliders at runtime when the game is started.
/// For this reason, this graph will not pick up tree colliders when scanned outside of play mode
/// but it will pick them up if the graph is scanned when the game has started. If it still does not pick them up
/// make sure that the trees actually have colliders attached to them and that the tree prefabs are
/// in the correct layer (the layer should be included in the layer mask).
///
/// In 2D mode, this setting has no effect.
///
/// See: <see cref="rasterizeTerrain"/>
/// See: <see cref="colliderRasterizeDetail"/>
/// </summary>
publicboolrasterizeTrees=true;
/// <summary>
/// Controls how much to downsample the terrain's heightmap before generating the input mesh used for rasterization.
/// A higher value is faster to scan but less accurate.
/// </summary>
publicintterrainHeightmapDownsamplingFactor=3;
/// <summary>
/// Controls detail on rasterization of sphere and capsule colliders.
///
/// The colliders will be approximated with polygons so that the max distance to the theoretical surface is less than 1/(this number of voxels).
///
/// A higher value does not necessarily increase quality of the mesh, but a lower
/// value will often speed it up.
///
/// You should try to keep this value as low as possible without affecting the mesh quality since
/// that will yield the fastest scan times.
///
/// The default value is 1, which corresponds to a maximum error of 1 voxel.
/// In most cases, increasing this to a value higher than 2 (corresponding to a maximum error of 0.5 voxels) is not useful.
///
/// See: rasterizeColliders
///
/// Version: Before 4.3.80 this variable was not scaled by the <see cref="cellSize"/>, and so it would not transfer as easily between scenes of different scales.
/// </summary>
publicfloatcolliderRasterizeDetail=1;
/// <summary>
/// Callback for collecting custom scene meshes.
///
/// This callback will be called once when scanning the graph, to allow you to add custom meshes to the graph, and once every time a graph update happens.
/// Use the <see cref="RecastMeshGatherer"/> class to add meshes that are to be rasterized.
///
/// Note: This is a callback, and can therefore not be serialized. You must set this field using code, every time the game starts (and optionally in edit mode as well).
/// Whether the base of the graph should default to being walkable or unwalkable.
///
/// This is only used in 2D mode. In 3D mode, this setting has no effect.
///
/// For 2D games, it can be very useful to set the background to be walkable by default, and then
/// constrain walkability using colliders.
///
/// If you don't want to use a walkable background, you can instead create colliders and attach a RecastNavmeshModifier with Surface Type set to Walkable Surface.
/// Require every region to have a RelevantGraphSurface component inside it.
/// A RelevantGraphSurface component placed in the scene specifies that
/// the navmesh region it is inside should be included in the navmesh.
///
/// If this is set to OnlyForCompletelyInsideTile
/// a navmesh region is included in the navmesh if it
/// has a RelevantGraphSurface inside it, or if it
/// is adjacent to a tile border. This can leave some small regions
/// which you didn't want to have included because they are adjacent
/// to tile borders, but it removes the need to place a component
/// in every single tile, which can be tedious (see below).
///
/// If this is set to RequireForAll
/// a navmesh region is included only if it has a RelevantGraphSurface
/// inside it. Note that even though the navmesh
/// looks continous between tiles, the tiles are computed individually
/// and therefore you need a RelevantGraphSurface component for each
/// region and for each tile.
///
/// [Open online documentation to see images]
/// In the above image, the mode OnlyForCompletelyInsideTile was used. Tile borders
/// are highlighted in black. Note that since all regions are adjacent to a tile border,
/// this mode didn't remove anything in this case and would give the same result as DoNotRequire.
/// The RelevantGraphSurface component is shown using the green gizmo in the top-right of the blue plane.
///
/// [Open online documentation to see images]
/// In the above image, the mode RequireForAll was used. No tiles were used.
/// Note that the small region at the top of the orange cube is now gone, since it was not the in the same
/// region as the relevant graph surface component.
/// The result would have been identical with OnlyForCompletelyInsideTile since there are no tiles (or a single tile, depending on how you look at it).
///
/// [Open online documentation to see images]
/// The mode RequireForAll was used here. Since there is only a single RelevantGraphSurface component, only the region
/// it was in, in the tile it is placed in, will be enabled. If there would have been several RelevantGraphSurface in other tiles,
/// those regions could have been enabled as well.
///
/// [Open online documentation to see images]
/// Here another tile size was used along with the OnlyForCompletelyInsideTile.
/// Note that the region on top of the orange cube is gone now since the region borders do not intersect that region (and there is no
/// RelevantGraphSurface component inside it).
///
/// Note: When not using tiles. OnlyForCompletelyInsideTile is equivalent to RequireForAll.
/// This can get you higher precision than colliders, since colliders are typically very simplified versions of the mesh.
/// However, it is often slower to scan, and graph updates can be particularly slow.
///
/// The reason that graph updates are slower is that there's no efficient way to find all meshes that intersect a given tile,
/// so the graph has to iterate over all meshes in the scene just to find the ones relevant for the tiles that you want to update.
/// Colliders, on the other hand, can be efficiently queried using the physics system.
///
/// You can disable this and attach a <see cref="RecastNavmeshModifier"/> component (with dynamic=false) to all meshes that you want to be included in the navmesh instead.
/// That way they will be able to be efficiently queried for, without having to iterate through all meshes in the scene.
///
/// In 2D mode, this setting has no effect.
/// Deprecated: Use <see cref="collectionSettings.rasterizeMeshes"/> instead
/// Controls detail on rasterization of sphere and capsule colliders.
///
/// The colliders will be approximated with polygons so that the max distance to the theoretical surface is less than 1/(this number of voxels).
///
/// A higher value does not necessarily increase quality of the mesh, but a lower
/// value will often speed it up.
///
/// You should try to keep this value as low as possible without affecting the mesh quality since
/// that will yield the fastest scan times.
///
/// The default value is 1, which corresponds to a maximum error of 1 voxel.
/// In most cases, increasing this to a value higher than 2 (corresponding to a maximum error of 0.5 voxels) is not useful.
///
/// See: rasterizeColliders
///
/// Version: Before 4.3.80 this variable was not scaled by the <see cref="cellSize"/>, and so it would not transfer as easily between scenes of different scales.
///
/// Deprecated: Use <see cref="collectionSettings.colliderRasterizeDetail"/> instead
if(HashSettings(graph)!=graphHash)thrownewSystem.InvalidOperationException("Recast graph changed while a graph update was in progress. This is not allowed. Use AstarPath.active.AddWorkItem if you need to update graphs.");
if(delta.x!=0&&delta.y!=0)thrownewSystem.ArgumentException("Only translation in a single direction is supported. delta.x == 0 || delta.y == 0 must hold.");
/// Number of extra voxels on each side of a tile to ensure accurate navmeshes near the tile border.
/// The width of a tile is expanded by 2 times this value (1x to the left and 1x to the right)
/// </summary>
internalintTileBorderSizeInVoxels{
get{
returnCharacterRadiusInVoxels+3;
}
}
internalfloatTileBorderSizeInWorldUnits{
get{
returnTileBorderSizeInVoxels*cellSize;
}
}
/// <summary>
/// Resize the number of tiles that this graph contains.
///
/// This can be used both to make a graph larger, smaller or move the bounds of the graph around.
/// The new bounds are relative to the existing bounds which are IntRect(0, 0, tileCountX-1, tileCountZ-1).
///
/// Any current tiles that fall outside the new bounds will be removed.
/// Any new tiles that did not exist inside the previous bounds will be created as empty tiles.
/// All other tiles will be preserved. They will stay at their current world space positions.
///
/// Note: This is intended to be used at runtime on an already scanned graph.
/// If you want to change the bounding box of a graph like in the editor, use <see cref="forcedBoundsSize"/> and <see cref="forcedBoundsCenter"/> instead.
///
/// <code>
/// AstarPath.active.AddWorkItem(() => {
/// var graph = AstarPath.active.data.recastGraph;
/// var currentBounds = new IntRect(0, 0, graph.tileXCount-1, graph.tileZCount-1);
///
/// // Make the graph twice as large, but discard the first 3 columns.
/// // All other tiles will be kept and stay at the same position in the world.
thrownewSystem.Exception("Loaded tile size does not match this graph's tile size.\n"
+"The source tiles have a world-space tile size of "+tileMeshes.tileWorldSize+" while this graph's tile size is ("+TileWorldSizeX+","+TileWorldSizeZ+").\n"
+"For a recast graph, the world-space tile size is defined as the cell size * the tile size in voxels");
Debug.LogError("In version 5.1.0 or higher of the A* Pathfinding Project you can no longer include objects both using a tag mask and a layer mask. Please choose in the recast graph inspector which one you want to use.");