I am currently working on a procedural terrain generator and I have come to an issue when procedurally spawning trees in my scene. Unity gives me one error but I don't really know how to go about fixing this issue.
Here is the code for my world generator and tree Generator.
World Generator
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TerrainGenerator : MonoBehaviour
{
[SerializeField]
private int terrainWidthInChunks, terrainLengthInChunks;
[SerializeField]
private GameObject ChunkPrefab;
[SerializeField]
private float centreVertex, maxDistance;
[SerializeField]
private TreeGenerator treeGenerator;
[SerializeField]
private RiverGenerator riverGenerator;
// Start is called before the first frame update
void Start()
{
GenerateWorld ();
}
void GenerateWorld()
{
//gets the chunk dimensions from the chunk prefab
Vector3 chunkSize = ChunkPrefab.GetComponent<MeshRenderer>().bounds.size;
int terrainHeight = (int)chunkSize.z;
int terrainWidth = (int)chunkSize.x;
//Calculates the number of vertices of the chunk in each axis of the mesh
Vector3[] terrainMeshVertices = ChunkPrefab.GetComponent<MeshFilter>().sharedMesh.vertices;
int terrainHeightInVertices = (int)Mathf.Sqrt(terrainMeshVertices.Length);
int terrainWidthInVertices = terrainHeightInVertices;
float distanceBetweenVertices = (float)terrainHeight / (float)terrainHeightInVertices;
//builds an empty object to be filled with the terrain chunks that are generated on run-time
WorldData worldData = new WorldData(terrainHeightInVertices, terrainWidthInVertices, this.terrainLengthInChunks, this.terrainWidthInChunks);
//for each chunk we will want to instantiate a chunk in the correct position
for (int x = 0; x < terrainWidthInChunks; x++)
{
for (int z = 0; z < terrainLengthInChunks; z++)
{
// calculates the chunk position based on x and z indices
Vector3 chunkPosition = new Vector3(this.gameObject.transform.position.x + x * terrainWidth, this.gameObject.transform.position.y, this.gameObject.transform.position.z + z * terrainHeight);
//instantiates new chunk
GameObject Chunk = Instantiate(ChunkPrefab, chunkPosition, Quaternion.identity) as GameObject;
//generates the chunk texture and stores it in ChunkData
ChunkData chunkData = Chunk.GetComponent<TerrainchunkGenerator>().GenerateChunk(centreVertex, maxDistance);
worldData.AddChunkData(chunkData, z, x);
}
}
//generates trees for the world
treeGenerator.GenerateTrees(this.terrainLengthInChunks * terrainHeightInVertices, this.terrainWidthInChunks * terrainWidthInVertices, distanceBetweenVertices, worldData);
//generates rivers for the world
riverGenerator.GenerateRivers(this.terrainLengthInChunks * terrainHeightInVertices, this.terrainWidthInChunks * terrainWidthInVertices, worldData);
}
}
//class to store merged chunk data
public class WorldData
{
private int terrainHeightInVertices, terrainWidthInVertices;
public ChunkData[,] chunksData;
public WorldData(int terrainHeightInVertices, int terrainWidthInVertices, int terrainLengthInChunks, int terrainWidthInChunks)
{
//builds the chunkdata matrix based on terrain depth and width in verts and chunks
chunksData = new ChunkData[terrainHeightInVertices * terrainLengthInChunks, terrainWidthInVertices * terrainWidthInChunks];
this.terrainHeightInVertices = terrainHeightInVertices;
this.terrainWidthInVertices = terrainWidthInVertices;
}
public void AddChunkData(ChunkData chunkData, int chunkZIndex, int chunkXIndex)
{
//saves the chunk data in the correct coordinates
chunksData[chunkZIndex, chunkXIndex] = chunkData;
}
public ChunkCoordinate ConvertToChunkCoordinate(int z, int x)
{
//the chunk index is calculates by dividing the index by yhr number of chunks in that axis
int chunkZIndex = (int)Mathf.Floor((float)z / (float)this.terrainHeightInVertices);
int chunkXIndex = (int)Mathf.Floor((float)x / (float)this.terrainWidthInVertices);
//the coord index is calculated by aquiring the remainder of the division operation above
//this translates te origin to the bottom left corner
int coordinateZIndex = this.terrainHeightInVertices - (z % this.terrainHeightInVertices) - 1;
int coordinateXIndex = this.terrainWidthInVertices - (x % this.terrainWidthInVertices) - 1;
ChunkCoordinate chunkCoordinate = new ChunkCoordinate(chunkZIndex, chunkXIndex, coordinateZIndex, coordinateXIndex);
return chunkCoordinate;
}
}
//class that represents a coordinate in the chunk coordinate System
public class ChunkCoordinate
{
public int chunkZIndex;
public int chunkXIndex;
public int coordinateZIndex;
public int coordinateXIndex;
public ChunkCoordinate(int chunkZIndex, int chunkXIndex, int coordinateZIndex, int coordinateXIndex)
{
this.chunkZIndex = chunkZIndex;
this.chunkXIndex = chunkXIndex;
this.coordinateZIndex = coordinateZIndex;
this.coordinateXIndex = coordinateXIndex;
}
}
*tree generator*
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TreeGenerator : MonoBehaviour
{
[SerializeField]
private NoisemapGenerator noisemapGenerator;
[SerializeField]
private Octave[] octaves;
[SerializeField]
private float terrainScale;
[SerializeField]
private float[] neighborRadius;
[SerializeField]
private GameObject[] treePrefab;
public void GenerateTrees(int height, int width, float distanceBetweenVertices, WorldData worldData)
{
//Generates a tree noise map using the perlin noise function from noise map generator
float[,] treeNoiseMap = this.noisemapGenerator.GeneratePerlinNoiseMap(height, width, this.terrainScale, 0, 0, this.octaves);
float chunkSizeX = width * distanceBetweenVertices;
float chunkSizeZ = height * distanceBetweenVertices;
for (int z = 0; z < height; z++)
{
for (int x = 0; x < width; x++)
{
//converts from the world coord system to the chunk coord system and retreieves the chunkdata
ChunkCoordinate chunkCoordinate = worldData.ConvertToChunkCoordinate(z, x);
ChunkData chunkData = worldData.chunksData[chunkCoordinate.chunkZIndex, chunkCoordinate.chunkXIndex];
int chunkWidth = chunkData.heightMap.GetLength(1);
//calculates the mesh vertex index
Vector3[] meshVertices = chunkData.mesh.vertices;
int vertexIndex = chunkCoordinate.coordinateZIndex * chunkWidth + chunkCoordinate.coordinateXIndex;
//gets the terrain region of this coord
TerrainRegion terrainRegion = chunkData.chosenHeightTerrainRegion[chunkCoordinate.coordinateZIndex, chunkCoordinate.coordinateXIndex];
//gets the current biome of the coord
Biome biome = chunkData.chosenBiomes[chunkCoordinate.coordinateZIndex, chunkCoordinate.coordinateXIndex];
//check if the coord is "water" terrain. We obviouslt don't want trees to be placed within the water
if (terrainRegion.name != "water")
{
float treeValue = treeNoiseMap[z, x];
int terrainRegionIndex = terrainRegion.index;
//compares the current noise value to the neighbouring values
int neighborZBegin = (int)Mathf.Max(0, z - this.neighborRadius[biome.index]);
int neighborZEnd = (int)Mathf.Min(height-1, z + this.neighborRadius[biome.index]);
int neighborXBegin = (int)Mathf.Max(0, x - this.neighborRadius[biome.index]);
int neighborXEnd = (int)Mathf.Min(width - 1, x + this.neighborRadius[biome.index]);
float maxValue = 0f;
for (int neighborZ = neighborZBegin; neighborZ <= neighborZEnd; neighborZ++)
{
for (int neighborX = neighborXBegin; neighborX <= neighborXEnd; neighborX++)
{
float neighborValue = treeNoiseMap[neighborZ, neighborX];
//saves the max tree noise values in the radius
if (neighborValue >= maxValue)
{
maxValue = neighborValue;
}
}
}
//if the current trees nise value is the max of one then place a tree in that location
if (treeValue == maxValue)
{
Vector3 treePosition = new Vector3(x * distanceBetweenVertices, meshVertices[vertexIndex].y, z * distanceBetweenVertices);
GameObject tree = Instantiate(this.treePrefab[biome.index], treePosition, Quaternion.identity) as GameObject;
tree.transform.localScale = new Vector3(0.05f, 0.05f, 0.05f);
}
}
}
}
}
}