Actual code of TerrainData.SetHeights (with ILSpy) :
public void SetHeights(int xBase, int yBase, float[,] heights)
{
if (heights == null)
{
throw new NullReferenceException();
}
if (xBase + heights.GetLength(1) > this.heightmapWidth || xBase < 0 || yBase < 0 || yBase + heights.GetLength(0) > this.heightmapHeight)
{
throw new Exception(UnityString.Format("X or Y base out of bounds. Setting up to {0}x{1} while map size is {2}x{3}", new object[]
{
xBase + heights.GetLength(1),
yBase + heights.GetLength(0),
this.heightmapWidth,
this.heightmapHeight
}));
}
this.Internal_SetHeights(xBase, yBase, heights.GetLength(1), heights.GetLength(0), heights);
}
But when we try this on a 513 * 513 heightmap :
terrain.terrainData.SetHeights (512, 511, new[,] { { 0.5f, 0.5f } });
We get the error : “Exception: X or Y base out of bounds. Setting up to 514x512 while map size is 513x513” while the exception should be thrown when we do :
terrain.terrainData.SetHeights (511, 512, new[,] { { 0.5f, 0.5f } });
So in SetHeights, GetLength (0) must be swapped with GetLength (1) :
public void SetHeights(int xBase, int yBase, float[,] heights)
{
if (heights == null)
{
throw new NullReferenceException();
}
if (xBase + heights.GetLength(0) > this.heightmapWidth || xBase < 0 || yBase < 0 || yBase + heights.GetLength(1) > this.heightmapHeight)
{
throw new Exception(UnityString.Format("X or Y base out of bounds. Setting up to {0}x{1} while map size is {2}x{3}", new object[]
{
xBase + heights.GetLength(0),
yBase + heights.GetLength(1),
this.heightmapWidth,
this.heightmapHeight
}));
}
this.Internal_SetHeights(xBase, yBase, heights.GetLength(0), heights.GetLength(1), heights);
}
While waiting for the fix, I’ve written this code to get around the bug :
public static class TerrainHack
{
private static MethodInfo internal_setHeights;
static TerrainHack ()
{
Init ();
}
private static void Init ()
{
if (internal_setHeights == null)
internal_setHeights = typeof (TerrainData).GetMethod ("Internal_SetHeights", BindingFlags.Instance | BindingFlags.NonPublic);
}
public static void SetHeightsCorrect (this TerrainData terrainData, int xBase, int yBase, float[,] heights)
{
SetHeightsCorrect (terrainData, xBase, yBase, heights.GetLength (0), heights.GetLength (1), heights);
}
public static void SetHeightsCorrect (this TerrainData terrainData, int xBase, int yBase, int width, int height, float[,] heights)
{
if (terrainData == null)
throw new ArgumentNullException ("terrainData");
if (heights == null)
throw new ArgumentNullException ("heights");
if (xBase < 0 ||
yBase < 0 ||
xBase + width > terrainData.heightmapWidth ||
yBase + height > terrainData.heightmapHeight
)
{
throw new Exception (string.Format (
"X or Y base out of bounds. Setting up to {0}x{1} while map size is {2}x{3}",
xBase + width,
yBase + height,
terrainData.heightmapWidth,
terrainData.heightmapHeight
));
}
_SetHeights (terrainData, xBase, yBase, width, height, heights);
}
private static void _SetHeights (this TerrainData terrainData, int xBase, int yBase, int width, int height, float[,] heights)
{
Init ();
internal_setHeights.Invoke (terrainData, new object[] { xBase, yBase, width, height, heights });
}
}
(sorry for posting here but i can’t use the bug reporter)