- Home /
TerrainData uses too much memory
I'm generating in-game terrain with a script. Each terrain as a size of 2000 width, 100 or 300 height, 2000 lenght and each terrain as a heighmap of 128. If i create 100 terrain Unity uses 1,6GB of memory when i expected to use max 50MB for all terrain... I made a search and i found that is a TerrainData problem that uses a lot of memory... How can i fix this problem? This is my generator script. To run it in real-time i put it inside an empty game object.
public class WorldGenerator : MonoBehaviour {
private int worldSize;
private const int tileSize=2000;
private GameObject tGameObject;
// Use this for initialization
void Start () {
worldSize=10;
for (int i=0; i<worldSize; ++i)
for(int j=0; j<worldSize; ++j){
TerrainData tData = new TerrainData();
tData.SetDetailResolution(2048,8);
tData.baseMapResolution=1024;
tData.heightmapResolution=128;
tData.size=new Vector3(tileSize,600,tileSize);
tGameObject = Terrain.CreateTerrainGameObject(tData);
tGameObject.name="Tile "+((i*worldSize)+j);
tGameObject.transform.position=(new Vector3((float)tData.size.x*j,(float)0,(float)tData.size.z*i));
}
Debug.Log("World generated in: "+Time.realtimeSinceStartup);
}
}
The memory used by Unity is like this:
Startup Unity -> 130 MB
Start Generator -> 1,7 GB
Stop Generator -> 400 MB
Start again Generator -> 1,7 GB
Stop it -> 750MB
Why i get this error?
If i restart unity it uses again 130MB. If i put Destroy(tData) at the end of for it uses 538MB but no terrain is displaied! If i use DestroyImmediate(tData) it uses 151MB but get a MissingReferenceException!
How can i generate terrains without using all that memory?
Answer by jc_lvngstn · Jun 15, 2012 at 04:38 PM
I actually use a separate unity project to create my terrains. I then store all terrain data to files. One file for the height data, one for the tree data, one for the details, etc etc.
In my player, I just have four terrains in the game world, each one 128x128. My scale is different than yours, but that's fine. As the player moves, I find out if the player has moved far enough to bring more terrains into view. I don't delete the "far" terrains and recreate the new "close" ones the player is moving into. So let's say you have something like this:
1 2
3 4
And the player is inside those 4 terrains, and moves east. The regions they are moving into are A and B
1 2 A
3 4 B
I just load the terrain for A and B DATA into 1 and 3, and move 1 and 3 over to their new positions (where A and B would be placed). I don't recreate the new terrain game objects and destroy the old ones. That seems to gobble up memory badly.
So, when your player starts, you can load all the world terrain data from the files in advance, or you can just load the ones you need in the background. Maybe do some caching.
So...your gameworld can be huge, you are only dynamically loading the terrain data for those 4 terrains, as the player moves around.
That's it in a nutshell, at least how I am doing it. You basically have to serialize and deserialize the terrain data. I just used the BinaryWriter and BinaryReader to do this in C#. Works great.
I guess if you are actually storing the terrain in your project, then may be able to pull that data from the assets. I haven't done that, it really wouldn't suite my needs for my project.
Good, this is what i thought :D I will try to do the same with my project... i don't create terrain in other project 'cause i want to generate different worlds at runtime. So all obaject are set in one project
Hello, I'm trying to save terrainData, but it doesn't work. What are you doing exactly ? Can you share you code for this part ?
Thanks...
Answer by Wolfram · Jun 15, 2012 at 11:54 AM
Well, with tData.SetDetailResolution(2048,8); you are specifying a detail resolution of 2048x2048 per terrain. That's your 2048x2048x100x4=1.68GB right there. You'll have to reduce that.
Also note that one "splat map" (the data structure storing your painted details, which is essentially a RGBA texture (hence the "x4")) can hold only 4 different detail types (painted textures, tree details, grass details) I think, so if you are using for example one basemap, 3 additional painted maps, two grass types and one rock type, you would already need (3+2+1)=6 detail types, so you would need two 2048x2048 detail maps for that terrain.
I see, i changed SetDetailResolution in tData.SetDetailResolution(1024,8). Unity uses 1,3GB of memory when 400$$anonymous$$B was expected! (1024x1024x100x4=400$$anonymous$$B)
What is wrong?
There still a problem, when i stop the test unity doesn't clean all memory... why?
Please don't post comments as answers.
To answer your question, it's a combination of base map resolution, detail map resolution, and height map resolution. All three need to be stored, and all three contribute to the total size. I might be wrong about the distribution I explained in my answer (e.g., maybe the painted maps belong to the base maps), and there might be additional datastructures required for the detail objects (orientation, size per object, ...), so it might use more than 4 bytes per "pixel" for the detail map. Also, the second parameter "resolutionPerPatch" also influences the memory amount required (the smaller, the higher).
1024 basemap (=400$$anonymous$$iB=420$$anonymous$$B) plus 1024 detailmap (assu$$anonymous$$g 4 bytes per "pixel") plus 128 heightmap@16bit plus Unity Editor plus other overheads bring you much closer to the 1.3GB you are seeing.
Suffice it to say, the only way to reduce the amount of memory required is to reduce the individual resolutions. You might want to consider storing the tiles as Assets only, and just loading the full resolution tiles on demand, and otherwise using a low resolution map for tiles further away, etc.
When the Player stops, the memory is marked as available, and the GarbageCollection might actually free some of it immediately. But not necessarily all of it, mostly due to memory fragmentation. This doesn't mean there is a memory leak - that's why if you hit "Play" again, the size doesn't increase further than your previous max value.
I see... so a good way to play with terrains is to load max 3/4 of them and load only terrains near the player! How can i store item as Assets and load them dynamically and how have i to modify my script to reach this? Before reading i was thinking about to store in a file all the info of all terrains and create one when player is near and delete one when is far or not inside player's view!
Your answer
Follow this Question
Related Questions
Create Terrain withTerrainData 0 Answers
**Trying to access out-of-bounds terrain height information** When calling TerrainData.GetHeights(); 0 Answers
copy smaller size terrain's data, and seet the heights on bigger Terrain at click position 0 Answers
Unity imports heightmaps in kind of upside down order. 0 Answers
Default TreeInstance.color 1 Answer