Instatiating an object. Altering that new object causes prefab to be altered.

I am new to Unity3d and I’m playing around with a proof of concept project.

I’m currently stuck because I use a blank terrain prefab (easily instantiable) that I may want to include having its own scripts.

When my worldcreator object (blank object with script) starts instantiating the terrain pieces in a grid I’m noticing something very odd.

When I instantiate the terrain prefab, alter its height map and move on to instantiating the next piece, the next instantiating piece is carrying over heightmap terrain data from the previous instantiation. When I go to the unity3d editor and drag the prefab to the scene, it as well has the altered heightmap data from the last play.

I’m kind of lost as to why this would happen and I’m wondering if I’m not understanding what exactly instantiating really is or why the prefab in my project would be altered. Before today I thought prefabs were “read only” from a code perspective till they were instantiated and then altered based on in-game variables.

Here is my code.

#pragma strict

public var maxStrength: float = 1;
public var numOfTerrainBySide: int = 4;
public var terrainXsize = 500; //not used
public var terrainZsize = 500;
public var terrainYsize = 50;
public var terrainHeightMapSize = 512;
public var worldPiece: Terrain; //Prefab blank terrain linked from /prefabs/worldpiece

private var wploc : Vector3;
private var wprot : Quaternion;

function Start()
{
	//get starting position of blank object "WorldCreator"
	wploc = transform.position;
	wprot = transform.rotation;
	buildWorld();
}

function buildWorld()
{

	//for each numOfTerrainBySide on X axis
	for(var z = 1; z-1 < numOfTerrainBySide; z ++)
	{
		//for each numOfTerrainBySide on Z axis per X
		for(var x = 1; x-1 < numOfTerrainBySide; x++)
		{
		var newloc = Vector3(terrainXsize * x - terrainXsize, wploc.y, terrainZsize * z - terrainZsize);
		buildWorldPiece(newloc,wprot,x,z);		
		}

	}
}

function buildWorldPiece(wploc,wprot,gridx,gridz)
{
	//create terrain from prefab object
 	var newterrain : Terrain = new Instantiate(worldPiece, wploc, wprot);
 	
 	//name object based on it's grid location
 	newterrain.name = "wp_" + gridx + "_" + gridz;
 	
 	var xRes = newterrain.terrainData.heightmapWidth; 
 	var yRes = newterrain.terrainData.heightmapHeight;
 	var heights = newterrain.terrainData.GetHeights(0, 0, xRes, yRes);

	for (var x: int = 0; x < yRes; x++) 
	{
		for (var z: int = 0; z < xRes; z++) 
		{			
			//proof of concept to put border around edges of multiple terrains
			if (gridx == 1 && z == 0)
			{
			heights[x,z] = Random.Range(1.0f, terrainYsize);
			}
			
			if (gridz == 1 && x == 0)
			{
			heights[x,z] = Random.Range(1.0f, terrainYsize);
			}
			
			if (gridx == numOfTerrainBySide && z == terrainHeightMapSize)
			{
			heights[x,z] = Random.Range(1.0f, terrainYsize);
			}
			
			if (gridz == numOfTerrainBySide && x == terrainHeightMapSize)
			{
			heights[x,z] = Random.Range(1.0f, terrainYsize);
			}
			
		}
	}

	newterrain.terrainData.SetHeights(0, 0, heights);

}

Instantiate is just a normal method on UnityEngine.Object. The new keyword in front of Instantiate makes no sense, and I’m surprised it even compiles.

I’m not quite sure what’s happening, but I have an idea. Instantiate makes a copy of prefabs, but it doesn’t make a deep copy. Which means that if the prefab references some other object, the reference will be copied, not the referenced object.

So I’d guess that your terrain is being copied, but the terrainData object being referenced is separate, so newterrain.terrainData references the same data as the prefab.

Try to do this in Instantiate:

var newterrain : Terrain = new Instantiate(worldPiece, wploc, wprot);
print("terrain is the same object: " + (newterrain == worldPiece));
print("terrainData is the same object: " + (newterrain.terrainData == worldPiece.terrainData));

I’m 99% sure you’ll get true on the first one, and false on the second one.

To fix this, you’ll have to create a copy of the terrain data, and assign that to the instantiated Terrain copy. That’ll take some work on the Unity internals - post a reply if you can’t figure it out on your own.

For the sake of the community I’m including that I also dropped the initiate on terrain and ended up going the route of creating a new object.

//create terrain out of thin air
var newTerrainObject : GameObject = new GameObject();
newTerrainObject.transform.parent = transform;
var newTerrain : Terrain = new newTerrainObject.gameObject.AddComponent(“Terrain”);

//move new terrain to location
newTerrainObject.transform.position= newloc;

//layer tag object
newTerrainObject.layer = terrainLayerMask;

//create new terrainData to resolve problem with terrain data being copied to prefab
newTerrain.terrainData = new TerrainData();

//set terraindata to minimum requirements. heightmapResolution must be set before size.
newTerrain.terrainData.heightmapResolution = terrainHeightMapResolution;
newTerrain.terrainData.size = new Vector3(terrainXsize, terrainYsize, terrainZsize);

This ended up resolving a bunch of issues in the end.