- Home /
How can I correctly offset seeded perlin noise over multiple terrains?
Hello,
I have been creating a randomly seeded world using a single terrain and perlin noise lately, which works as intended. However, I'm attempting to create a new terrain next to the original with an offset applied to the original seeded noise, which in theory should create a seamless tile next to the original but I'm having no luck.
I have a test project where I'd found the number 9.98 to be a working offset by trial and error however it's not working for my current terrain(s).
Here's a screenshot of the result I'm getting, the darker patches indicate areas below water.
As you can see, it's not right. The far-left terrain is the initial and is created with no offset and the seed, the right is created using the seed plus and offset of 9.98. Each terrain has a script attached to it, ready to set neighbours; the script also contains the offset information used upon world generation. See below.
Finally, each terrain is a child of a single object controlling world generation, the section relating to manipulating terrain data can be seen below.
IEnumerator CreateLandscape() { for (int i = 0; i < transform.childCount; i++) { TerrainData tData = transform.GetChild(i).GetComponent ().terrainData; Neighbour tNeighbour = transform.GetChild(i).GetComponent (); Vector2 tOffset = new Vector2(tNeighbour.OffsetX, tNeighbour.OffsetY);
loadingMessage = string.Format("Terrain_{0}: {1}", i, "Creating terrain heights, using random seed.");
yield return StartCoroutine(CreateTerrain(tData, tOffset));
yield return new WaitForSeconds(0.5f);
if (i == 0)
{
for (int w = 0; w < tData.heightmapWidth; w++)
{
for (int h = 0; h < tData.heightmapHeight; h++)
{
float cHeight = tData.GetHeight(w, h);
if (cHeight < terrainLowestElevation)
{
terrainLowestElevation = cHeight;
}
}
}
terrainWaterLevel = terrainLowestElevation + defaultWaterOffset;
}
loadingMessage = string.Format("Terrain_{0}: {1}", i, "Painting terrain based on global water level.");
yield return StartCoroutine(PaintTerrain(tData));
yield return new WaitForSeconds(0.5f);
//yield return StartCoroutine(CreateFoliage(tData, transform.GetChild(i), i));
//yield return new WaitForSeconds(0.5f);
yield return new WaitForSeconds(0.5f);
}
loadingMessage = string.Format("Environment: {0}", "Setting terrain neighbours, and removing seams.");
yield return StartCoroutine(SetTerrainNeighbours());
loadingMessage = string.Format("Environment: {0}", "Positioning global water source.");
TerrainData wData = GameObject.Find("Terrain_M").GetComponent<Terrain>().terrainData;
yield return StartCoroutine(PositionWater(wData));
yield return StartCoroutine(SetTerrainNeighbours());
loadingMessage = string.Format("Environment: {0}", "Positioning the player.");
yield return StartCoroutine(PositionPlayer());
yield return new WaitForSeconds(0.5f);
hasLoaded = true;
yield return null;
}
IEnumerator CreateTerrain(TerrainData tData, Vector2 tOffset)
{
int Digit_1 = int.Parse(Seed.ToString()[0].ToString());
int Digit_2 = int.Parse(Seed.ToString()[1].ToString());
int Digit_3 = int.Parse(Seed.ToString()[2].ToString());
int Digit_4 = int.Parse(Seed.ToString()[3].ToString());
int Digit_5 = int.Parse(Seed.ToString()[4].ToString());
float TileHeight = (Digit_1 < 5) ? defaultTerrainHeight + (Digit_1 * 2) : defaultTerrainHeight + Digit_1;
float TileSize = (Digit_2 > 5) ? (float)Digit_2 : 5f;
Vector3 TerrainSize = tData.size;
TerrainSize.y = TileHeight;
tData.size = TerrainSize;
// Create a new empty array which will hold the generated terrain data.
float[,] tHeights = new float[tData.heightmapWidth, tData.heightmapHeight];
for (int w = 0; w < tData.heightmapWidth; w++)
{
for (int h = 0; h < tData.heightmapHeight; h++)
{
tHeights[w, h] = Mathf.PerlinNoise((Seed + tOffset.y) + ((float)w / (float)tData.heightmapWidth) * TileSize, (Seed + tOffset.x) + ((float)h / (float)tData.heightmapHeight) * TileSize);
}
}
// Apply the generated data to the terrainData.
tData.SetHeights(0, 0, tHeights);
// Terrain heights have been generated, return from this function.
yield return null;
}
I have no issues with setting the neighbours or stitching terrains together as that is the final part of generating the world; before I can do so though, the noise needs to align properly.
So, to reiterate my question; Am I doing something wrong, is the offset of 9.98 just a coincidence in my text project? What would be the correct way to find the next offset to created a tiled perlin terrain?
Hopefully I've provided enough information, let me know if you need any more. Thanks, LD.
Answer by udk_lethal_d0se · Oct 15, 2015 at 02:39 PM
I have found the solution to this problem simply by trial and error; the new offset is simply 5.
However, I would like to know if there's a better solution by calculating the offset if for instance the size of the terrain or resolution changes.
EDIT: The solution is that the offset is the TileSize used in the perlin function.