Bug: tiled recast scan ignores graph bounds

When scanning tiled recast graphs the result seems to ignore the graph bounds on X+ and Z+ directions.


This is quite troublesome as units are not supposed to go into the dark areas.
I know I could just slap some navmeshcuts around the bounds, but that’s just a sloppy workaround, I’d rather have this fixed.

PS: I see the forum changed again, I did not find a bug section, so I’m posting this here.

Hi

Hm. I think it will round it up to the nearest even tile size (so 100 instead of 30 voxels along the edge).
Should definitely fix that.

You can use a non-tiled recast graph as a temporary fix.

This still seems to be a problem with tiled recast graphs. I’m doing runtime baking of pretty large maps, so I can’t really afford to not use tiles. Could you point me in the right direction for where I’d need to fix this locally in the Astar code?


Okay, I’ve managed to reduce the rounding to the nearest world unit instead of the nearest tile by changing the following code in RecastGenerator.cs:

        Bounds CalculateTileBoundsWithBorder (int x, int z) {
			var bounds = new Bounds();

			float maxX = Mathf.Min(forcedBoundsSize.x, (x + 1) * TileWorldSizeX);
			float maxZ = Mathf.Min(forcedBoundsSize.z, (z + 1) * TileWorldSizeZ);
			bounds.SetMinMax(new Vector3(x*TileWorldSizeX, 0, z*TileWorldSizeZ),
				new Vector3(maxX, forcedBoundsSize.y, maxZ)
				);

			// Expand borderSize voxels on each side
			bounds.Expand(TileBorderSizeInWorldUnits * 2 * new Vector3(1, 0, 1));
			return bounds;
		}

and

		protected NavmeshTile BuildTileMesh (Voxelize vox, int x, int z, int threadIndex = 0) {
			AstarProfiler.StartProfile("Build Tile");
			AstarProfiler.StartProfile("Init");

			vox.borderSize = TileBorderSizeInVoxels;
			vox.forcedBounds = CalculateTileBoundsWithBorder(x, z);
			vox.width = Mathf.CeilToInt(vox.forcedBounds.size.x / cellSize);
			vox.depth = Mathf.CeilToInt(vox.forcedBounds.size.z / cellSize);

			if (!useTiles && relevantGraphSurfaceMode == RelevantGraphSurfaceMode.OnlyForCompletelyInsideTile) {
				// This best reflects what the user would actually want
				vox.relevantGraphSurfaceMode = RelevantGraphSurfaceMode.RequireForAll;
			} else {
				vox.relevantGraphSurfaceMode = relevantGraphSurfaceMode;
			}

			vox.minRegionSize = Mathf.RoundToInt(minRegionSize / (cellSize*cellSize));

			AstarProfiler.EndProfile("Init");

            .....

Specifically the lines that calculate the min/max dimensions of the bake bounds and the width/depth of the voxelizer. Not sure if this is absolutely the best solution or if it will behave well in all cases, but it’s what I was able to figure out, in case it helps somebody.