1

I am trying to generate a grid across my map and add nodes depending on the perlin noise value. Depending on the value obtained from the perlin noise at a location, I will add a new Node which will be of a certain type e.g. Mountain, Water etc to represent terrian. Here I am trying to make it so that if the value is > 0.5, this mean it's only mountains and so a black coloured cubes should surround the mountain areas, However, my black cubes do not match the mountain areas from the perlin noise and I cannot seem to figure out why I am going wrong. Would appreciate any insight into how I could go about achieving this.

private void LocateWalkableCells()
{
    for(int z = 0; z < Height; z++)
    {
        for(int x = 0; x < Width; x++)
        {
            noise = GetNoiseValue(x, z);
            if(noise > 0.5) {
                grid[x,z] = new Node(new Vector3(x, 0, z), TerrainType.Mountain, 1);
            } 
            else {
                grid[x,z] = new Node(new Vector3(x, 0, z), TerrainType.Grass, 1);
            }
        }
    }
}

private float GetNoiseValue(int x, int z)
{
    int pos = (x * Width) + z;
    return Mathf.Round(terrainGenerator.noiseArray[pos] * 10) / 10;
}

// Draw gizmos to visualize colour
void OnDrawGizmos()
{
    Gizmos.DrawWireCube(transform.position, new Vector3(Width, 1, Height));
    if(grid != null)
    {
        foreach(Node n in grid)
        {
            if(n.TerrainType == TerrainType.Grass)
            {
                Gizmos.color = Color.green;
            } 
            else if(n.TerrainType == TerrainType.Mountain)
            {
                Gizmos.color = Color.black;
            } 

            Gizmos.DrawCube(n.Position, Vector3.one * (nodeDiameter - .1f));
        }
    }
}

noiseArray is used for the vertices of the terrain in the following code:

vertices = new Vector3[(Width + 1) * (Depth + 1)];
noiseArray = PerlinNoise();

int i = 0;
for(int z = 0; z <= Depth; z++)
{
    for(int x = 0; x <= Width; x++)
    {
        var currentHeight = noiseArray[i];

        if(currentHeight > HeightThreshold)
        {
            currentHeight *= HeightMultiplier;
        }

        vertices[i] = new Vector3(x, currentHeight, z);
        i++;
    }
}

Output enter image description here

Result from suggested answer

Still seems to miss some mountain areas, colouring green instead of black. enter image description here

Krellex
  • 613
  • 2
  • 7
  • 20
  • `terrainGenerator` and `terrainGenerator.noiseArray` are undefined so I can't reproduce this problem. See [mre] for more info. Anyway, I'd use [`Mathf.PerlinNoise`](https://docs.unity3d.com/ScriptReference/Mathf.PerlinNoise.html) to generate the height like this: `float height = Mathf.PerlinNoise(randomOffset+x*factor, randomOffset+z*factor);` where `factor` is a float whose value determines the "sharpness" of the height map and where `randomOffset` is a float randomly generated once per map that randomizes the "starting point" of the noise. – Ruzihm Feb 22 '22 at 21:21
  • And you can optionally clamp between 0 and 1 with `Mathf.Clamp01(height)` since `Mathf.PerlinNoise` may return values outside that range on occassion. – Ruzihm Feb 22 '22 at 21:25
  • Does this code also include how you are setting the height map of the terrain? There doesn't appear to be any connection between the code in the question and the height that the terrain uses such as calls to `TerrainData.SetHeights`. I only am seeing code to used to set the green/black color of the gizmo cubes. It would also help to include the definition for `Node`, especially `Node.Position` so the relationship between grid position and world position is better understood – Ruzihm Feb 22 '22 at 21:33
  • 1
    @Ruzihm Thanks for the reply! I have updated the post to include the Github link for the project. It has only 2 scripts (Grid.cs and TerrainGenerator.cs). I did not include some of the other code since I thought it would make it more difficult to understand. – Krellex Feb 22 '22 at 21:41
  • What's the relationship between `Grid.Height` and `TerrainGenerator.Depth`? Are these kept the same value under all conditions or something like that? – Ruzihm Feb 22 '22 at 21:47
  • 1
    @Ruzihm Grid.Height & Depth are the same and are always kept the same. They mean the same thing but I forgot to update Depth to be Height when I started to just use Height. Sorry about that – Krellex Feb 22 '22 at 21:50
  • 1
    Alright. What happens if you use `int pos = x + z * (Width + 1);`? This seems to be how [`TerrainGenerator.CreateMesh`](https://github.com/shabaazxh/SimplePathFinderProject/blob/main/Assets/TerrainGenerator.cs#L88) maps from x and z to i. – Ruzihm Feb 22 '22 at 21:52

1 Answers1

2

It think the issue is in

var pos = (x * Width) + z;

since x is you index on the width of the grid you would probably rather want

var pos = z * Width + x;

in other words you want to

  • skip z rows
  • each row has Width elements
  • then from there take the xth element

assuming your terrain is laid out row-wise.

Or if it is laid out column-wise (which is rather unusual but possible)

var pos = x * Height + z;

or in other words

  • skip x columns
  • each column has Height elements
  • then from there take the zth element

See also Converting index of one dimensional array into two dimensional array i. e. row and column


Update

Now that you have showed the terrain generation code it needs to be

var pos = z * (Width + 1) + x;

since the terrain array has actually Width + 1 elements per row.

derHugo
  • 83,094
  • 9
  • 75
  • 115
  • you mean `(z * Height) + x` ? – KYL3R Feb 22 '22 at 20:00
  • @KYL3R no .. I mean `(z * Width) + x` – derHugo Feb 22 '22 at 20:00
  • Thanks for the reply! The terrain is row-wise so I tried `z * Width + x` I updated the post to show the result. Still seems to miss some mountains areas for some reason. – Krellex Feb 22 '22 at 20:41
  • @Krellex updated after you have posted your generator code. Since you terrain array has rows of size `Width + 1` it needs to be `z * (Width + 1) + x;` – derHugo Feb 23 '22 at 05:00