- Home /
2D Procedural Terrain Generation + Pooling
I am in the process of trying to create a game similar to Terraria or Starbound. I have been trying out different methods of optimizing map creation over the last week. Somebody in the forums stated to me they could create a 4096 x 4096 map that only uses 128 MB memory total (according to Unity profiler) by using pooling.
Pooling is a new concept to me, and I have done some reading and checking out some tutorials on it. I think...I understand how it could be used, but the second I tried to use it I wound up with a 384 x 384 map that uses 750 MB memory and takes 30 seconds to load. So I obviously must be doing something wrong, because so far pooling appears to be crap.
Here is what I have done. I have 3 prefabs, Map, Chunk, and Tile. The tile prefab is a GameObject with a SpriteRenderer and Box Collider 2D. The chunk is an empty game object meant to serve as a parent to the tiles it contains (for organizational purposes). It contains a script that includes tile width, tile height, and a 2D tile array. On load, it initializes the tile array and instantiates all tiles it contains. Finally, the Map is an empty gameobject that holds chunks. It has a script attached that includes max chunks to create. On load it instantiates chunks up to its max (which in turn causes the chunks to instantiate their tiles).
The goal is that there will be a chunk above, below, left, right, upper left, upper right, lower left, lower right, of the player, including the chunk the player is in. As the player heads in a direction, the chunk gets moved to be ahead of the player. So I will never need more than 9 loaded at a time (unless I go multiplayer), and the player should in theory, be able to move in any direction endlessly.
What values the tiles on the map have (i.e. air, dirt, etc. and other info) is dictated by a MapData script. When the chunks move, the tiles within the moved chunk set themselves based on the MapData of the tile located at its position.
So that's my theory. Can anybody tell me where I might be misunderstanding the concept, or misusing it in any way? When the game releases, I want a load time of 1 minute or less for a computer that meets the minimum requirements. So having a map that takes 30 seconds to generate (without grass, trees, physics, enemies, npcs, etc.) on a beast computer is unacceptable.
In general, design/discussion question like this one are better asked on Unity Forums. They have a discussion format. Unity Answers is designed for single, specific technical questions.
Answer by BlueSin · Mar 04, 2014 at 08:19 PM
Actually going to answer my own question here since I managed to resolve the issue myself. Thanks to everybody who tried to help though. The solution to the problem was actually fairly straight forward.
I attach a rigid body 2d to my player and draw large 2d box colliders around every chunk in the map. When the map is generated, each chunk is created, although the blocks within are not(but the block data is). When the player's rigidbody enters a chunk trigger zone, the chunk will flag itself as a player containing chunk while the player remains within its confines. The player can be in as little as 1 chunk and as many as 4 chunks at once if they are standing on 4 corners.
Then at pre-defined intervals (for me it is every 1 second), my BlockPool script will poll all the chunks and add those chunks that contain the player to a list. The pool will also add the chunks surrounding all player chunks to the list as well, to act as a chunk buffer so the player never sees loading chunks. Then I take the old player chunks including buffer and remove any chunks that are not currently player chunks or buffer chunks.
Finally, the BlockPool will call two separate coroutines to run over time within the chunks, one tells garbage chunks to destroy its blocks, the other tells active chunks to create their blocks. When playtesting in Unity, as the player moves in one direction you will see chunks being created in front and around them as they move, while the chunks that are left behind are destroyed. Its quite a pretty sight.
Final statistics on map generation:
Large: 8192 x 2048 blocks Memory Used: 65 MB FPS: 60+
Small: 4096 x 1024 blocks Memory Used: 50 MB FPS: 60+
Chunk Size used: 32 x 32 blocks
Block Size used: 24 x 24 px
So as you can see, it is very thorough and there are no FPS spikes either. Hope this answer helps somebody else delving into this in the future!
http://studentgamedev.blogspot.com.ar/2013/11/unity-voxel-tutorial-part-7-loading.html
actually here you have an example as how to load the chunks while the player walks it will create chunks every 1 second at certain amount of distance. When i got time if you want i can give you my example
This is a very nice solutions, something I've considered as well, however, I am worried that for fast moving objects, (even players), there might not be events triggered as the player flies over the chunks, but perhaps, my fear is unfounded... with a large enough collision box around the player... anyone have any experience with this?
Answer by Chopan · Mar 03, 2014 at 06:48 AM
Check out this website: http://studentgamedev.blogspot.no/2013/08/unity-voxel-tutorial-part-1-generating.html You can adapt this to 2d I made it, and it works pretty good. The only catch is that you'll need to create your own outline in a Polygon Collider. I you manage to do this then tell me haha
Believe it or not I actually did this before! I was able to get small maps (4096 x 1024) working correctly. I basically took the code from the first 3 tutorials on that site there, and turned that into chunk generator code. Then I just created all the chunks I needed. Huge memory hog of a system though, large maps took upwards of 30 sec - 1 $$anonymous$$ to load and used about 900 - 1.5 GB of memory. It is not efficient =(
if you're going to work on massive map you could save the data as JSON output and save it like in blocks you know or quadrants. Then you load from disk small chunks as you need them, and you could have certain amount of chunks in the pool and when you need to unload and load you can reuse the old ones and regenerate the terrain on each chunk. So first you generate all your map (could possibly go over 30 sec) then save it, then load when needed on small steps. Didn't try it really but that could be some sort of workaround
Your answer
Follow this Question
Related Questions
2D Procedural Terrain with SpriteManager 0 Answers
How can I get a Low Poly terrain to work in Unity? 2 Answers
2D Tilemap room prefabs for dungeon generation. 0 Answers
Procedural terrain- of the 2D circular variety. 0 Answers
Procedural terrain generation ? 0 Answers