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?

2 Answers

2

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...

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 400MB was expected! (1024x1024x100x4=400MB) What is wrong? There still a problem, when i stop the test unity doesn't clean all memory... why?

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!