3
votes

How can I scale up the size of my world/level to include more gameobjects without causing lag for the player?

I am creating an asset for the asset store. It is a random procedural world generator. There is only one major problem: world size.

I can't figure out how to scale up the worlds to have more objects/tiles.

  • I have generated worlds up to 2000x500 tiles, but it lags very badly.
  • The maximum sized world that will not affect the speed of the game is around 500x200 tiles.
  • I have generated worlds of the same size with smaller blocks: 1/4th the size (it doesn't affect how many tiles you can spawn)

I would like to create a world at least the size of 4200x1200 blocks without lag spikes.

  • I have looked at object pooling (it doesn't seem like it can help me that much)
  • I have looked at LoadLevelAsync (don't really know how to use this, and rumor is that you need Unity Pro which I do not have)
  • I have tried setting chunks Active or Deactive based on player position (This caused more lag than just leaving the blocks alone).

Additional Information: The terrain is split up into chunks. It is 2d, and I have box colliders on all solid tiles/blocks. Players can dig/place blocks. I am not worried about the amount of time it takes for the level to load initially, but rather about the smoothness of the game while playing it -no lag spikes while playing.

question on Unity Forums

1
I am currently generating the entire world at once. I am not trying to make it "Infinite" like Minecraft, but rather create larger levels -a size closer to Terraria's size- while keeping memory usage low so as to not bother the player. With the way my script is set up, I can instantiate the chunks at different times as needed, it just made the most sense to instantiate everything at once. I am looking for a technique to achieve this, not necessarily a snippet of code (although it could be a snippet of code). - Edge Developers
The general technique for this is definitely the third item you describe under your failed attempts: "have tried setting chunks Active or Deactive based on player position (This caused more lag than just leaving the blocks alone)." Not sure what else can be said besides that's the path to sniff down... - slumtrimpet
Ok. With this technique are you talking about generating the world only a couple chunks at a time (the ones closest to the player) as opposed to generating the entire world, then setting Active/Deactive? Some clarity as to the actual technique would be helpful. With the failed attempt, I generate the entire world on the initial load, then deactivate chunks farther from the player, and activate the ones closest. This caused a lot of lag whenever the player crossed a chunkline which is pretty often. Thanks for the info so far - Edge Developers
Is something like this answers.unity3d.com/questions/762174/… the correct technque? - Edge Developers

1 Answers

1
votes

If you're storing each tile as an individual GameObject, don't. Use a texture atlas and 'tile data' to generate the look of each chunk whenever it is dug into or a tile placed on it.

Also make sure to disable, potentially even delete any chunks not within the visible range of the player. Object pooling will help significantly here if you can work out the maximum number of chunks that will ever be needed at once, and just recycle chunks as they go off the screen.

DETAILS:

There is a lot to talk about for the optimal generation, so I'm going to post this link (http://studentgamedev.blogspot.co.uk/2013/08/unity-voxel-tutorial-part-1-generating.html) It shows you how to do it in a 3D space, but the principales are essentially the same if not a little easier for 2D space. The following is just a rough outline of what might be involved, and going down this path will result in huge benefits, but will require a lot of work to get there. I've included all the benefits at the bottom of the answer.

Each tile can be made to be a simple struct with fields like int id, vector2d texturePos, bool visible in it's simplest form. You can then store these tiles in a 2 dimensional array within each chunk, though to make them even more memory efficient you could store the texturePos once elsewhere in the program and write a method to get a texturePos by id.

When you make changes to this 2 dimensional array which represents either the addition or removal of a tile, you update the chunk, which is the actual GameObject used to represent the tiles. By iterating over the tile data stored in the chunk, it will be possible to generate a mesh of vertices based on the position of each tile in the 2 dimensional array. If visible is false, simply don't generate any vertices for it.

This mesh alone could be used as a collider, but won't look like anything. It will also be necessary to generate UV co-ords which happen to be the texturePos. When Unity then displays the mesh, it will display specific points of the texture atlas as defined by the UV co-ords of the mesh.

This has the benefit of resulting in significantly fewer GameObjects, better texture batching for Unity, less memory usage, faster random access for any tile as it's not got any MonoBehaviour overhead, and a genuine plethora of additional benefits.