Hello everyone,
I'm trying here, unsuccessfully, to duplicate terrains in real-time.
I have 2 GameObjects, named "Terrain00" and "Terrain01". Each of them have a Terrain object, created dynamically from resources I get from a socket stream. I want now to, at certain event in my application, to remove Terrain00 terrain (but keeping the GameObject) and duplicate, or move, the terrain inside Terrain01.
I managed to get a instance of each terrain, managed to make a copy of the terrain with Instantiate, and even managed to delete the terrain in Terrain00, but I didn't found a way so far to insert the copied terrain in Terrain00.... All I could do so far is to create a new blank terrain with GameObject.AddComponent.... I'm trying to avoid to create a new terrain in Terrain00 and then export everything to my new terrain, as terrainData, terrainCollider, and so on.
Is there any way to do this?
Eric is correct, but I too enjoy being lazy.
I am setting up my height map and my splat map via code. Therefore, all I really need is a shallow copy of all the TerrainData properties that I setup in the designer.
I threw together this prototype and tested that it works.
To my fellow lazy coders, enjoy:
// this is garbage test code, but you get the idea
public class TerrainSpawner : MonoBehaviour {
public GameObject terrainPrefab;
// Use this for initialization
IEnumerator Start () {
GameObject terrainGameObject = (GameObject) Instantiate (terrainPrefab, new Vector3 (-1000.0F, 0.0F, -1000.0F), terrainPrefab.transform.rotation);
yield return new WaitForSeconds (5.0F); // waiting for 5 seconds so I can watch it spawn the 2nd terrain in the scene view while it's running
Terrain terrain = terrainPrefab.GetComponent<Terrain> ();
TerrainData terrainData = new TerrainData ();
CopyFrom (terrainData, terrain.terrainData); // copy the basic getable/setable properties
GameObject newTerrainGameObject = (GameObject) Instantiate (terrainPrefab, new Vector3 (-3000.0F, 0.0F, -1000.0F), terrainPrefab.transform.rotation);
Terrain newTerrain = newTerrainGameObject.GetComponent<Terrain> ();
newTerrain.terrainData = terrainData;
}
// previously an extension method
public static void CopyFrom<T1, T2>(T1 obj, T2 otherObject)
where T1: class
where T2: class
{
PropertyInfo[] srcFields = otherObject.GetType().GetProperties(
BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty);
PropertyInfo[] destFields = obj.GetType().GetProperties(
BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty);
foreach (var property in srcFields) {
var dest = destFields.FirstOrDefault(x => x.Name == property.Name);
if (dest != null && dest.CanWrite)
dest.SetValue(obj, property.GetValue(otherObject, null), null);
}
}
}
There's no point trying to avoid copying everything over, because that's what you have to do. There isn't a Terrain.Copy function, but you can make one yourself fairly easily. (Terrain.CreateTerrainGameObject just links to an existing TerrainData, so it's not helpful here.)