- Home /
Procedural terrain pausing update with IEnumerator not working.
Hello, I am trying to make a procedural terrain in unity for an idea I have. I am able to get chunks to load and unload fine, but this causes lag. I have looked around and found that a good idea it to have the code wait for a frame each time it loads a new chuck. E.g it loads 5 new chunks in front of a player.
The code that I have that works, but causes lag is this.
//The marching cube algorythm that creates a mesh
//Based on a 3d scalar field
private MarchingCubes marchCube;
//Should we generate caves? Alot of calculations here
//very slow
public bool generateCaves = false;
//The size of each chunk
public int chunkSize;
public int buffer;
//The heightmap fractals that are combined to create a random heightmap
public cFractalNoise[] heightMapFractals;
//The 3d cave fractal generator
public cFractalNoise caveFractal;
//Holds a a simple object with a mesh renderer, mesh filter, and mesh collider
public GameObject chunkObj;
//Material to be assigned to the chunk obj's mesh
public Material mat;
public int iterator = 0;//Create the chunks
public Transform player;
// your current position
public int tileX;
public int tileZ;
public int planeCount;
//A list of the chunks
public Chunk[,] chunk;
private Chunk[] newTiles;
private Chunk[,] newTerrainTiles;
System.Collections.IEnumerator Start ()
{
planeCount = buffer * 2 + 1;
tileX = Mathf.RoundToInt(player.position.x / chunkSize);
tileZ = Mathf.RoundToInt(player.position.z / chunkSize);
marchCube = gameObject.GetComponent<MarchingCubes>();
if (chunk != null) {
foreach (Chunk t in chunk) {
Destroy(t.gameObject);
}
}
chunk = new Chunk[planeCount, planeCount];
for (int x = 0; x < planeCount; x++) {
for (int z = 0; z < planeCount; z++) {
chunk[x, z] = genChunk(tileX - buffer + x, tileZ - buffer + z);
iterator++;
yield return 1;
}
}
//Debug.LogError ("Itorator: " + iterator + " Buffer: " + ((buffer * 2 + 1)^2) + " Plane Count: " + planeCount);
//Used for mesh combining to minimize draw calls
//gameObject.GetComponent<CombineChildren>().Combine();
//An octree for future development
//gameObject.GetComponent<Octree>().Initialize();
}
private Chunk genChunk(int x, int y){
//refreshHM = false;
GameObject chunkInst = (GameObject)Instantiate(chunkObj, new Vector3((chunkSize-1) * x, 0, (chunkSize-1) * y), Quaternion.identity);
chunkInst.transform.parent = this.transform;
Chunk chun = new Chunk ();
chun = chunkInst.AddComponent<Chunk>();
chun.Assign(iterator, x, y);
chun.generateCaves = generateCaves;
chun.Initialize(heightMapFractals, caveFractal, new Vector3(chunkSize, 128, chunkSize), new Vector2(x, y));
marchCube.density = chun.density;
marchCube.Initialize(new Vector3(chunkSize, 128, chunkSize));
chunkInst.GetComponent<MeshFilter>().mesh = marchCube.mesh;
chunkInst.GetComponent<MeshCollider>().sharedMesh = marchCube.mesh;
chunkInst.GetComponent<MeshFilter>().mesh.RecalculateBounds();
chunkInst.GetComponent<MeshFilter>().mesh.RecalculateNormals();
chunkInst.GetComponent<MeshRenderer>().material = mat;
return chun;
}
public void Cull(int changeX, int changeZ){
int i, j;
newTiles = new Chunk[planeCount];
newTerrainTiles = new Chunk[planeCount, planeCount];
if (changeX != 0 || changeZ != 0) {
if (changeX != 0) {
for (i = 0; i < planeCount; i++) {
//yield return 1;
Destroy (chunk [buffer - buffer * changeX, i].gameObject);
//yield return 1;
chunk [buffer - buffer * changeX, i] = null;
newTiles [i] = genChunk (tileX + buffer * changeX + changeX, tileZ - buffer + i);
//yield return 1;
}
}
if (changeZ != 0) {
for (i = 0; i < planeCount; i++) {
Destroy (chunk [i, buffer - buffer * changeZ].gameObject);
//yield return 1;
chunk [i, buffer - buffer * changeZ] = null;
newTiles [i] = genChunk (tileX - buffer + i, tileZ + buffer * changeZ + changeZ);
//yield return 1;
}
}
}
updateTerrain (changeX, changeZ);
}
public void updateTerrain(int changeX, int changeZ){
// make a copy of the old terrainTiles to reference when creating the new tile map.
System.Array.Copy(chunk, newTerrainTiles, planeCount * planeCount);
//yield return 1;
// go through the current tiles on screen (minus the ones we just deleted, and reapply there
// new position in the newTerrainTiles array.
for (int i = 0; i < planeCount; i++) {
for (int j = 0; j < planeCount; j++) {
Chunk t = chunk[i, j];
if (t != null){
Debug.LogError( i + ", " + j + " : [" + (tileX - changeX + buffer + t.positionX) + ", " + (-tileZ - changeZ + buffer + t.positionZ) + "]");
newTerrainTiles[-tileX - changeX + buffer + t.positionX, -tileZ - changeZ + buffer + t.positionZ] = t;
}
}
}
// add the newly created tiles to this new array.
for (int i = 0; i < newTiles.Length; i++) {
Chunk t = newTiles[i];
newTerrainTiles[-tileX - changeX + buffer + t.positionX, -tileZ - changeZ + buffer + t.positionZ] = t;
}
// set the current map to the new array.
chunk = newTerrainTiles;
}
public void Update()
{
int newTileX = Mathf.RoundToInt(player.position.x / chunkSize);
int newTileZ = Mathf.RoundToInt(player.position.z / chunkSize);
if (newTileX != tileX) {
StartCoroutine(Cull(newTileX - tileX, 0));
tileX = newTileX;
//Debug.LogError("New Tile X" + " : " + (newTileX - tileX) + ", " + 0);
}
if (newTileZ != tileZ) {
StartCoroutine(Cull(0, newTileZ - tileZ));
tileZ = newTileZ;
//Debug.LogError("New Tile Y" + " : " + 0 + ", " + ( newTileZ - tileZ));
So I changed the void "Cull" to an IEnumerator and changed the code in "Cull" to this.
public System.Collections.IEnumerator Cull(int changeX, int changeZ){
int i, j;
newTiles = new Chunk[planeCount];
newTerrainTiles = new Chunk[planeCount, planeCount];
if (changeX != 0 || changeZ != 0) {
if (changeX != 0) {
for (i = 0; i < planeCount; i++) {
Destroy (chunk [buffer - buffer * changeX, i].gameObject);
yield return 1; //Wait after deleting.
chunk [buffer - buffer * changeX, i] = null;
newTiles [i] = genChunk (tileX + buffer * changeX + changeX, tileZ - buffer + i);
yield return 1; //Wait after creatation.
}
}
if (changeZ != 0) {
for (i = 0; i < planeCount; i++) {
Destroy (chunk [i, buffer - buffer * changeZ].gameObject);
yield return 1; //Wait after deletin
chunk [i, buffer - buffer * changeZ] = null;
newTiles [i] = genChunk (tileX - buffer + i, tileZ + buffer * changeZ + changeZ);
yield return 1; //Wait after creatation.
}
}
}
updateTerrain (changeX, changeZ);
}
With this simple change my script no longer works. I get an error on this line in the "updateTerrain" void. It is the line which has "newTerrainTiles" at the start.
// go through the current tiles on screen (minus the ones we just deleted, and reapply there
// new position in the newTerrainTiles array.
for (int i = 0; i < planeCount; i++) {
for (int j = 0; j < planeCount; j++) {
Chunk t = chunk[i, j];
if (t != null){
Debug.LogError( i + ", " + j + " : [" + (tileX - changeX + buffer + t.positionX) + ", " + (-tileZ - changeZ + buffer + t.positionZ) + "]");
newTerrainTiles[-tileX - changeX + buffer + t.positionX, -tileZ - changeZ + buffer + t.positionZ] = t;
}
}
}
The error reads:
IndexOutOfRangeException: Array index is out of range.
World.updateTerrain (Int32 changeX, Int32 changeZ) (at Assets/Assets/Scripts/World.cs:145)
World+<Cull>c__Iterator1.MoveNext () (at Assets/Assets/Scripts/World.cs:129)
I do not understand what has changed other that the code stopping for a while. I have a hunch it has to do with the "return" in yield, but I have no idea how to fix this.
Does anyone no how to fix this error, or could someone tell/show me a better way of loading chunks without causing a frame drop.
Thanks.
Your answer
Follow this Question
Related Questions
Instantiating too much at Start? Terrain Objects. 0 Answers
How can I get a Low Poly terrain to work in Unity? 2 Answers
Making A 2d game android 2 Answers
How do I go about texturing a flat-shaded generated mesh? 1 Answer
3d Perlin Noise? 3 Answers