- Home /
Destroy Terrain trees and their colliders.
Destroy Terrain Trees And Their Collider ok so in my game I have a mechanic where you can chop down trees using a script attached to the player and one on a game manager object but the trees are painted into the terrain so they are hard to access. These scripts sort of work but the collider stays. so the tree mesh is gone but you can still collide with it. this is the method I use for destroying the trees
public void RemoveATrees(int index)
{
// remove a specific tree by index
List<TreeInstance> instancesTmp = new List<TreeInstance>();
int i = 0;
foreach (TreeInstance tree in terrain.terrainData.treeInstances)
{
// loop a all of a tree on terrain
if (i != index)
{
// basically add every tree to a Tmp array, Except a tree with a specfic index
instancesTmp.Add(tree);
}
i++;
}
// replace a tree instances in terrain data with a Tmp array
terrain.terrainData.treeInstances = instancesTmp.ToArray();
// refresh the terrain, get rid of collider of that tree.
float[,] heights = terrain.terrainData.GetHeights(0, 0, 0, 0);
terrain.terrainData.SetHeights(0, 0, heights);
}
}
but the collider still stays. So does any one know how to fix this?
also if you want both scripts here: On the player:
using UnityEngine; using System.Collections; using System.Collections.Generic;
// Purpose: demonstrate script interface to interact with terrain trees
// Steps: Attach this to main(parent/top) Player gameObject, or adjust myTransform to your hierarchy,
// define Inspector values for harvestTreeDistance, respawnTimer
// Setup a prefab tree with CAPSULE collider, how to at bottom of
// http://docs.unity3d.com/Documentation/Components/terrain-Trees.html
// paint terrain tree
// Assign the non-collider prefab version of the tree to felledTree
// press Play, left click on terrain tree
// Harvested terrain tree info is passed to a manager object QM_ResourceManager for respawn management,
// you'll need that too or you could comment out any functionality related to manager
// Note: this is not a demo of modifying terrainData permanently - there's enough risk involved with that
// such that you shouldn't try it unless you know what you are doing.
public class QM_HarvestTerrainTree : MonoBehaviour
{
// Player, Range
public int harvestTreeDistance; // Set [Inspector] min. distance from Player to Tree for your scale?
public bool rotatePlayer = true; // Should we rotate the player to face the Tree?
private Transform myTransform; // Player transform for cache
// Terrains, Hit
private Terrain terrain; // Derived from hit...GetComponent<Terrain>
private RaycastHit hit; // For hit. methods
private string lastTerrain; // To avoid reassignment/GetComponent on every Terrain click
// Tree, GameManager
public GameObject felledTree; // Prefab to spawn at terrain tree loc for TIIIIIIMBER!
private QM_ResourceManager rMgr; // Resource manager script
public float respawnTimer; // Duration of terrain tree respawn timer
void Start()
{
if (harvestTreeDistance <= 0)
{
Debug.Log("harvestTreeDistance unset in Inspector, using value: 6");
harvestTreeDistance = 6;
}
if (respawnTimer <= 0)
{
Debug.Log("respawnTimer unset in Inspector, using quick test value: 15");
respawnTimer = 15;
}
myTransform = transform;
lastTerrain = null;
rMgr = FindObjectOfType<QM_ResourceManager>();
}
void Update()
{
if (Input.GetMouseButtonUp(0))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit, 30f))
{
// Did we click a Terrain?
if (hit.collider.gameObject.GetComponent<Terrain>() == null)
return;
// Did we click on the same Terrain as last time (or very first time?)
if (lastTerrain == null || lastTerrain != hit.collider.name)
{
terrain = hit.collider.gameObject.GetComponent<Terrain>();
lastTerrain = terrain.name;
}
// Was it the terrain or a terrain tree, based on SampleHeight()
float groundHeight = terrain.SampleHeight(hit.point);
if (hit.point.y - .2f > groundHeight)
{
// It's a terrain tree, check Proximity and Harvest
if (CheckProximity())
HarvestWood();
}
}
}
}
private bool CheckProximity()
{
bool inRange = true;
float clickDist = Vector3.Distance(myTransform.position, hit.point);
if (clickDist > harvestTreeDistance)
{
Debug.Log("Out of Range");
inRange = false;
}
return inRange;
}
private bool CheckRecentUsage(string _terrainName, int _treeINDEX)
{
bool beenUsed = false;
for (int cnt = 0; cnt < rMgr.managedTrees.Count; cnt++)
{
if (rMgr.managedTrees[cnt].terrainName == _terrainName && rMgr.managedTrees[cnt].treeINDEX == _treeINDEX)
{
Debug.Log("Tree has been used recently");
beenUsed = true;
}
}
return beenUsed;
}
private void HarvestWood()
{
int treeIDX = -1;
int treeCount = terrain.terrainData.treeInstances.Length;
float treeDist = harvestTreeDistance;
Vector3 treePos = new Vector3(0, 0, 0);
// Notice we are looping through every terrain tree,
// which is a caution against a 15,000 tree terrain
for (int cnt = 0; cnt < treeCount; cnt++)
{
Vector3 thisTreePos = Vector3.Scale(terrain.terrainData.GetTreeInstance(cnt).position, terrain.terrainData.size) + terrain.transform.position;
float thisTreeDist = Vector3.Distance(thisTreePos, hit.point);
if (thisTreeDist < treeDist)
{
treeIDX = cnt;
treeDist = thisTreeDist;
treePos = thisTreePos;
}
}
if (treeIDX == -1)
{
Debug.Log("Out of Range");
return;
}
if (!CheckRecentUsage(terrain.name, treeIDX))
{
// Success - all tests passed
// Place a cube to show the tree, the ResourceManager will remove it after a time
// Obviously tweak to your liking, just a visual aid to show it worked
RemoveATrees(treeIDX);
// Example of spawning a placed tree at this location, just for demo purposes
// it will slide through terrain and disappear in 4 seconds
GameObject fellTree = Instantiate(felledTree, treePos, Quaternion.identity) as GameObject;
// Add this terrain tree and cube to our Resource Manager for demo purposes
//rMgr.AddTerrainTree(terrain.name, treeIDX, Time.time + respawnTimer, marker.transform);
if (rotatePlayer)
{
Vector3 lookRot = new Vector3(hit.point.x, myTransform.position.y, hit.point.z);
myTransform.LookAt(lookRot);
}
// There are too many guesses to be made here about your game mechanics to code the rest
// For example, you might..
// Start Animation
// Play Sound Clip
// Give player an Inventory item
// Bump lumberjacking skill
// Random.Range[] spawn a Forest Protector Spirit of Woe
// etc.
}
}
public void RemoveATrees(int index)
{
// remove a specific tree by index
List<TreeInstance> instancesTmp = new List<TreeInstance>();
int i = 0;
foreach (TreeInstance tree in terrain.terrainData.treeInstances)
{
// loop a all of a tree on terrain
if (i != index)
{
// basically add every tree to a Tmp array, Except a tree with a specfic index
instancesTmp.Add(tree);
}
i++;
}
// replace a tree instances in terrain data with a Tmp array
terrain.terrainData.treeInstances = instancesTmp.ToArray();
// refresh the terrain, get rid of collider of that tree.
float[,] heights = terrain.terrainData.GetHeights(0, 0, 0, 0);
terrain.terrainData.SetHeights(0, 0, heights);
}
}
and on the game manager object:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
// Attach this to a Singleton-like GameObject of which there is only and will ever only be one,
// ala classic GameManager object.
public class QM_ResourceManager : MonoBehaviour
{
// Define List component
public class QM_Tree
{
public string terrainName { get; set; }
public int treeINDEX { get; set; }
public float respawnTime { get; set; }
public Transform marker { get; set; }
// Constructor
public QM_Tree(string _terrainName, int _treeINDEX, float _respawnTime, Transform _marker)
{
terrainName = _terrainName;
treeINDEX = _treeINDEX;
respawnTime = _respawnTime;
marker = _marker;
}
}
// Tree Harvest script access
public List<QM_Tree> managedTrees = new List<QM_Tree>();
void Start()
{
// Scan for tree to "respawn" (remove cube, make available again) every 15 seconds
// Adjust to your needs using a fast spawn here for demo
InvokeRepeating("RespawnTree", 15, 15);
}
private void RespawnTree()
{
if (managedTrees.Count == 0)
return;
// Removing the demo cube and allowing tree to be used again
for (int cnt = 0; cnt < managedTrees.Count; cnt++)
{
if (managedTrees[cnt].respawnTime < Time.time)
{
Destroy(managedTrees[cnt].marker.gameObject);
managedTrees.RemoveAt(cnt);
return;
}
}
}
public void AddTerrainTree(string _terrainName, int _treeIDX, float _respawnTime, Transform _marker)
{
managedTrees.Add(new QM_Tree(_terrainName, _treeIDX, _respawnTime, _marker));
}
}
Comment