How to prevent terrain changes from being permanent after playback?

I have a custom component, and I’m modifying the terrain data at run time:

terrainData.SetAlphamaps(0, 0, alphaMaps);

This works, but when I stop playback, my changes are permanent on the terrain data. Is there a way to make my changes non-permanent, and only applied during playback?

Thanks!

Unfortunately, the terrain data is stored in the Unity library. There isn’t any way (I’m aware of) to ‘sandbox’ the runtime changes. Perhaps something to petition Unity about (http://feedback.unity3d.com/unity/all-categories/1/hot/active) :slight_smile:

You could potentially save the original terrain data in memory and restore it when leaving play mode. You can be notified when leaving play mode with Unity - Scripting API: MonoBehaviour.OnApplicationQuit()

There is an option, but you have to make some steps manually. You can make an editor script to copy the terrain data before the game starts, but if you release the game, then you have to be sure the terrain data is up to date.

using UnityEngine;


/// <summary>
/// Problem: I want to change the mesh and the texture on the terrain. And this must be safe!
/// Lot of examples using OnApplicationQuit() for setting back textures, meshes, but a blue screen
/// does not call that method and the terrain has then permanent changes. Think about that 
/// happens at a customer...
/// 
/// Idea: Duplicate the TerrainData for backup purposes. The TerrainCollider script
/// references to it. Is on the same GameObject as the Terrain script. Click on the 
/// TerrainData and you see where it is. Duplicate the TerrainData (Strg + D).
/// Create a folder "Resources" (Unity scans for this folder name and uses all Resources
/// as Resources folder, which can be accessed in a running game.). Create under the 
/// "Resources" folder a "TerrainData" folder and drag and drop the duplicated
/// TerrainData in this folder.
/// 
/// Add the TerrainPreserver.cs script on the Terrain GameObject (in the scene).
/// 
/// If you start the game, then this script copies all the terrain data from the duplicate
/// to the current terrain data. So, if you change the terrain, then at the next start
/// all the data will be overridden with the backupped terrain data.
/// 
/// The only one thing what you have to do is, to always create a backup (Strg + D and copy 
/// it to the Resource/TerrainData folder), if you change the terrain data.
/// Keep in mind, the terrain data are:
/// - Trees, grasses, height and texture changes placed with unity terrain tool.
/// 
/// If you just places prefabs (Tree, grass prefab) on the terrain (without the terrain tool), 
/// then you don't have to update the backup/duplicate TerrainData.
/// 
/// If crash happens, then just start and stop the game to reset the terrain for the editor mode.
/// </summary>

namespace metadesc {
    public class TerrainPreserver : MonoBehaviour {

        TerrainData td1;
        TerrainData td2;


        void Awake () {

            Terrain terrain = GetComponent<Terrain>();
            if (terrain == null) {
                Debug.LogError("No terrain on GameObject: " + gameObject);
                return;
            }

            td1 = terrain.terrainData;

            // This is the backup name/path of the cloned TerrainData.
	        string tdBackupName = "TerrainData/" + td1.name + " 1";
            td2 = Resources.Load<TerrainData>(tdBackupName);
            if (td2 == null) {
                Debug.LogError("No TerrainData backup in a Resources folder, mussing name is: " + tdBackupName);
                return;
            }

            // If blue screen, we still have to copy, for sure. It is a fast operation.
            resetTerrainDataChanges();
        }

        void OnApplicationQuit() {
            // To reset the terrain after quite the application.
            resetTerrainDataChanges();
        }

        void resetTerrainDataChanges() {
            // Terrain collider
            td1.SetHeights(0, 0, td2.GetHeights(0, 0, td1.heightmapWidth, td1.heightmapHeight));
            // Textures
            td1.SetAlphamaps(0, 0, td2.GetAlphamaps(0, 0, td1.alphamapWidth, td1.alphamapHeight));
            // Trees
            td1.treeInstances = td2.treeInstances;
            // Grasses
            td1.SetDetailLayer(0, 0, 0, td2.GetDetailLayer(0, 0, td1.detailWidth, td1.detailHeight, 0));
        }
    }
}