So I'm creating a unity3D game with infinite terrain and it's going great! However, I'm struggling with a specific but important part of the 'infinite' terrain. Make the terrain actually infinite. I got it working but it's very slow and I've looked through the code and at the profiler that unity has built in and came to the conclusion that it's my 2D for-loop that's slowing down the game. (VERY small freezes)
Let me explain... so I have a total of 3 lists keeping track of all the chunk gameobjects in the scene for that player. One for the active chunks, one for the inactive chunks, and one for all the chunks in the scene. In the beginning I spawn the chunks around the player in a circle around the player (size defined by a radius variable) and then all those chunks get added to the active and all chunks lists. Then I check if the player has moved one chunk of distance every frame (which isn't really slow at all) and if the player has moved one chunk of distance in any direction it calls a infinite terrain method that does this:
It loops through the ALL chunks list and checks their distance if they are farther then the radius variable. If it is then it adds it to the unloaded chunk list queue. THEN: It's a 2D for-loop (nested for loops) that loop through a square area around the players current position. It then checks if that position in that point in the loop has a chunk by looking finding if the loaded chunks list (it's a dictionary) has that current position in it. If it doesn't then that means that positions needs a chunk.. it dequeues a chunk from the unloaded chunks queue and loads it in that position and then adds it to the loaded chunks list.
private List<GameObject> allChunks = new List<GameObject>();
private Dictionary<GameObject, Vector2> loadedChunks = new Dictionary<GameObject, Vector2>();
private Queue<GameObject> unloadedChunks = new Queue<GameObject>();
private void InfiniteWorld()
{
// Check for unloaded chunks and unload them
for (int i = 0; i < allChunks.Count; i++)
{
if (loadedChunks.ContainsKey(allChunks[i]))
{
float distFromCenter = Vector2.Distance(new Vector2(Mathf.FloorToInt(allChunks[i].transform.position.x / chunkSize), Mathf.FloorToInt(allChunks[i].transform.position.z / chunkSize)), new Vector2(Mathf.FloorToInt(transform.position.x / chunkSize), Mathf.FloorToInt(transform.position.z / chunkSize))); //Mathf.Sqrt(Mathf.Pow(Mathf.Abs(Mathf.FloorToInt(allChunks[i].transform.position.x / chunkSize) - Mathf.FloorToInt(transform.position.x / chunkSize)), 2) + Mathf.Pow(Mathf.Abs(Mathf.FloorToInt(allChunks[i].transform.position.z / chunkSize) - Mathf.FloorToInt(transform.position.z / chunkSize)), 2));
if (distFromCenter >= worldRadius)
{
// If there is a chunk farther then our render distance unload it.
loadedChunks.Remove(allChunks[i]);
unloadedChunks.Enqueue(allChunks[i]);
allChunks[i].GetComponent<Chunk>().Unload();
}
}
}
// Check for positions that chunks need to be loaded in to
for (int x = -worldRadius; x < worldRadius; x++)
{
for (int y = -worldRadius; y < worldRadius; y++)
{
Vector2 chunkGridPosition = new Vector2(x + Mathf.FloorToInt(transform.position.x / chunkSize), y + Mathf.FloorToInt(transform.position.z / chunkSize));
if (!loadedChunks.ContainsValue(chunkGridPosition))
{
float distFromCenter = Vector2.Distance(chunkGridPosition, new Vector2(Mathf.FloorToInt(transform.position.x / chunkSize), Mathf.FloorToInt(transform.position.z / chunkSize)));//Mathf.Sqrt(Mathf.Pow(Mathf.Abs(chunkGridPosition.x - Mathf.FloorToInt(transform.position.x / chunkSize)), 2) + Mathf.Pow(Mathf.Abs(chunkGridPosition.y - Mathf.FloorToInt(transform.position.z / chunkSize)), 2));
if (distFromCenter < worldRadius)
{
// If a chunk isn't in a position in our render distance then load one from the unloaded list
GameObject chunk = unloadedChunks.Dequeue();
chunk.transform.position = new Vector3(chunkGridPosition.x * chunkSize, 0, chunkGridPosition.y * chunkSize);
chunk.GetComponent<Chunk>().Load();
loadedChunks.Add(chunk, chunkGridPosition);
}
}
}
}
}
This works perfectly except it has a VERY slight spike and it's barely noticeable but it's there. (only really noticeable with high radius values) but I need a decently high radius value so the player can actually see the terrain around it. So I need your help to make this faster or just make a new infinite terrain system because I can see myself that this isn't really a good way of doing things especially with 3 lists constantly being modified EVEN if they are the correct list types for that job.
Yes I have thought of threading this code but the issue is since this is unity its hard to keep track of things in a list and then access it to do something for example: chunk.LoadChunk(position);
And I got it working but not completely and it was overall more messy because of unity's limitations and my poor threading skills. Not that this is any better but I definitely understand it more.
Any help guys? Thanks!