Hello people!
Today i bring something i cant get my head around. I cant understand why it wont work. So i have trees in my scene that i want to delete from the TerrainData and spawn a prefab which can be chopped down. It spawns the prefab no problem and all but it just wont remove the treeinstance from the terrain? Any help is appreciated! Thanks in advance!
Code snippet
if (Input.GetKeyDown(KeyCode.L)) //Change cloesest tree to prefab
{
RaycastHit hit;
if(Physics.Raycast(Player.transform.position, Vector3.down,out hit)) //Shoot a raycast under the player and get the collider of the terrain and assign it underneath
{
ActiveTerrain = hit.collider.gameObject.GetComponent<Terrain>(); //Assign the terrain that is under the player to Active Terrain (For smoother operation)
}
TreeInstance[] trees = ActiveTerrain.terrainData.treeInstances; //Take current terrain and assign all trees to that array
var data = ActiveTerrain.terrainData; //Data = current terrainData function on that terrain
if (trees.Length > 0) //If there is more then 1 tree in that map
{
TreeInstance Nearest = trees[0]; //Nearest = 0 (Latest tree in the list)
treeSave = data.treeInstances; //save current terrain trees will be changed later
float width = (float)data.heightmapWidth; //Take width
float height = (float)data.heightmapHeight; // take height (this is where the map is)
Vector3 NearPosition = Vector3.Scale(Nearest.position, data.size) + ActiveTerrain.transform.position; //We make it in to a vector3 and call it near pos
foreach (TreeInstance Location in trees) //For each treeinstance in the tree list
{
Vector3 position = Vector3.Scale(Location.position, data.size) + ActiveTerrain.transform.position; //Take pos
if (Vector3.Distance(position, Player.transform.position) < Vector3.Distance(NearPosition, Player.transform.position)) //Compare tree pos to player pos and get closest!
{
Nearest = Location;
NearPosition = position;
}
}
NearPosition.y = ActiveTerrain.SampleHeight(NearPosition);
Instantiate(TreeId[Nearest.prototypeIndex], NearPosition, Quaternion.identity, ParentHolder.transform); //ParentHolder is where they are made
var CurTrees = new List<TreeInstance>(trees);
data.treeInstances = CurTrees.ToArray();
float[,] heights = data.GetHeights(0, 0, 0, 0);
data.SetHeights(0, 0, heights);
CurTrees.RemoveAt(0);
}
}
}
Can you just make it an empty array like so?
data.treeInstances = new TreeInstance[];
Here’s an old script I made for replacing terrain trees with prefabs on the old terrain. Replaces trees on a terrain with prefab. · GitHub
when i change
data.treeInstances = CurTrees.ToArray();
to
data.treeInstances = new TreeInstance[];
it will remove all the trees in the terrain. which wont work. I want only the closest tree to the player to be removed from the terrain and replaced. My problem must lay within these lines which is what i cant figure out why it wont work. It should be noted that we are talking about a lot of trees, hence why I dont want to make them all to prefabs but just the ones the player will interact with
var CurTrees = new List<TreeInstance>(trees);
data.treeInstances = CurTrees.ToArray();
float[,] heights = data.GetHeights(0, 0, 0, 0);
data.SetHeights(0, 0, heights);
CurTrees.RemoveAt(0);
//edit
I changed
data.SetHeights(0, 0, heights);
to
trees[0].heightScale 0.0f; since the data changes all trees on the map
So I think i’ve done some progress. I have changed some of the code and i can now acess what tree in the array needs to be removed and it those all that but how do i overwrite the treeinstance with the new one that has that tree removed?
Code
if (trees.Length > 0) //If there is more then 1 tree in that map
{
TreeInstance Nearest = trees[0]; //Nearest = 0 (Latest tree in the list)
treeSave = data.treeInstances; //save current terrain trees will be changed later
float width = (float)data.heightmapWidth; //Take width
float height = (float)data.heightmapHeight; // take height (this is where the map is)
Vector3 NearPosition = Vector3.Scale(Nearest.position, data.size) + ActiveTerrain.transform.position; //We make it in to a vector3 and call it near pos
TreeInstance Location = trees[0]; //Get the location for the tree
int ClosestNumber = 0; //Save the array int from closest tree
// foreach (TreeInstance Location in trees) //For each treeinstance in the tree list
for (int i = 0; i < trees.Length; i++) //Loop it
{
Location = trees[i]; //Change the location every iteration
Vector3 position = Vector3.Scale(Location.position, data.size) + ActiveTerrain.transform.position; //Take pos
if (Vector3.Distance(position, Player.transform.position) < Vector3.Distance(NearPosition, Player.transform.position)) //Compare tree pos to player pos and get closest!
{
Nearest = Location;
NearPosition = position;
ClosestNumber = i; //If the player is close to a tree change the number, Latest change = closest tree
}
}
Debug.Log(ClosestNumber);
NearPosition.y = ActiveTerrain.SampleHeight(NearPosition);
Instantiate(TreeId[Nearest.prototypeIndex], NearPosition, Quaternion.identity, ParentHolder.transform); //ParentHolder is where they are made
var newtree = new List<TreeInstance>(trees); //<-- so how do i overwrite the treeinstance with this?
newtree.RemoveAt(ClosestNumber);
trees[ClosestNumber].widthScale = 0.0f;
trees[ClosestNumber].heightScale = 0.0f;
}
I have solved it! I have no clue why it dident work the first few times but i’ve gotten it to work every time with this code, I will leave it here for people to see.
if (Input.GetKeyDown(KeyCode.L)) //Change cloesest tree to prefab
{
RaycastHit hit;
if(Physics.Raycast(Player.transform.position, Vector3.down,out hit)) //Shoot a raycast under the player and get the collider of the terrain and assign it underneath
{
ActiveTerrain = hit.collider.gameObject.GetComponent<Terrain>(); //Assign the terrain that is under the player to Active Terrain (For smoother operation)
}
// TreeInstance[] trees = Maps[3].GetComponent<Terrain>().terrainData.treeInstances; //Grab all trees Deprecated
TreeInstance[] trees = ActiveTerrain.terrainData.treeInstances; //Take current terrain and assign all trees to that array
var data = ActiveTerrain.terrainData; //Data = current terrainData function on that terrain
if (trees.Length > 0) //If there is more then 1 tree in that map
{
TreeInstance Nearest = trees[0]; //Nearest = 0 (Latest tree in the list)
treeSave = data.treeInstances; //Make a backup of all the trees and apply when app is quit (Will be deleted in full version)
TempTree = data.treeInstances; //This makes a new instance that we later remove from and apply to the current instance
float width = (float)data.heightmapWidth; //Take width
float height = (float)data.heightmapHeight; // take height (this is where the map is)
Vector3 NearPosition = Vector3.Scale(Nearest.position, data.size) + ActiveTerrain.transform.position; //We make it in to a vector3 and call it near pos
TreeInstance Location = trees[0]; //Get the location for the tree
int ClosestNumber = 0; //Save the array int from closest tree
// foreach (TreeInstance Location in trees) //For each treeinstance in the tree list
for (int i = 0; i < trees.Length; i++) //Loop it
{
Location = trees[i]; //Change the location every iteration
Vector3 position = Vector3.Scale(Location.position, data.size) + ActiveTerrain.transform.position; //Take pos
if (Vector3.Distance(position, Player.transform.position) < Vector3.Distance(NearPosition, Player.transform.position)) //Compare tree pos to player pos and get closest!
{
Nearest = Location;
NearPosition = position;
ClosestNumber = i; //If the player is close to a tree change the number, Latest change = closest tree
}
}
Debug.Log(ClosestNumber); //index number on closest tree
Instantiate(TreeId[Nearest.prototypeIndex], NearPosition, Quaternion.identity, ParentHolder.transform); //ParentHolder is where they are made
var newtreeInstance = new List<TreeInstance>(TempTree); //We make the temptree instance in to a list so we can remove the closest tree
newtreeInstance.RemoveAt(ClosestNumber); //Delete the tree at the closestnumber int
data.treeInstances = newtreeInstance.ToArray(); //Apply the new tree instance to the scene
}
}
}
private void OnApplicationQuit()
{
ActiveTerrain.terrainData.treeInstances = treeSave;
}
And to remove the collider from the tree that was instanced you can use this
Terrain.activeTerrain.GetComponent<TerrainCollider>().enabled = false;
Terrain.activeTerrain.GetComponent<TerrainCollider>().enabled = true;
1 Like