I made a script and workflow to avoid permanent changes on terrains, that can be done in game mode, if the mesh or textures are changed. I tested it with mesh deformation and texture changes and tree/grass changes and it works fine. It would be nice, if more of you could test/use this script/workflow. I would be thankful for every improvements.
I was searching for a long time for a solution, and this is the most convenient and safe solution.
This code is free to use for commercial purposes !!!
These are just some lines for so much investigation…
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;
td2 = Resources.Load<TerrainData>(tdBackupName);
if (td2 == null) {
Debug.LogError("No TerrainData backup in a Resources folder, mussing folder is: Assets/Resources/TerrainData/");
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));
}
}
}
