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?

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.

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.