Building on my work with procedural landscapes and vegetations (see http://forum.unity3d.com/viewtopic.php?t=30587), I thought now that I have the trees accessible by script, one can do funny things to them.
And here’s the code that does it all. Attach to the source of your explosion, trigger it however you want it triggered, blow up some trees.
Right now, it uses the same dead replacement for all trees. Obviously, for an actual game you would want to use an array of dead replacements, and then simply use tree.prototypeIndex as the index for your array.
The dead tree must have a collider and a rigidbody attached, otherwise it won’t fall correctly.
You also need to attach an explosion prefab.
Play with BlastRange and BlastForce to tune the effect. I’m sure you can also improve lots of other things, this script is just a proof-of-concept to show how it’s done.
Jaxas, I guess this is more intended like a prototype. Great stuff Tom! Also liked your other post about dynamic trees a lot, thanks for sharing that script!
Yes, it’s a demo to show that and how it can be done.
You’re welcome to add a better explosion effect, extend the replacement action (if the branches would break off, for example, the would be great) and of course fix the force and falling-down effect. I thought about a joint at the root so the tree just topples over, etc.
Feel free. I hope I’ve solved some of the more trickier problems and the rest can be done according to what your game requires.
Oh, one more thing: If you test things in the editor when using this, be beware that any changes to trees (i.e. tree removal) is permanent and will not be undone when you exit the game.
Excellent script. I’m using JS, and was easily able to convert to (basically):
var terrain = Terrain.activeTerrain.terrainData;
var instances = new ArrayList();
var blastRange = 10.0;
var DeadReplace : GameObject;
for (var tree in terrain.treeInstances) {
var distance = Vector3.Distance(Vector3.Scale(tree.position, terrain.size) + Terrain.activeTerrain.transform.position, transform.position);
if(distance<blastRange) {
//kill the tree
var deadTree = Instantiate(DeadReplace, Vector3.Scale(tree.position, terrain.size) + Terrain.activeTerrain.transform.position, Quaternion.identity) as GameObject;
DeadReplace.rigidbody.maxAngularVelocity = 1;
DeadReplace.rigidbody.AddExplosionForce(100, transform.position, blastRange*5, 0.0);
} else {
instances.Add(tree);
}
}
terrain.treeInstances = instances.ToArray(typeof(TreeInstance));
I use a separate function to instantiate the particle system prefab “explosion”…this is not copy/paste, either – you need to use a public “DeadReplace” var in your script to define the necessary “dead tree” object in the explorer. But a general JS translation for those who need it.
Thanks so much for the script; it was really helpful. Unfortunately, with this script the colliders remain. You can use the SetHeights function to remove the colliders also, however, as I am using it creates a significant delay. The line of code I am using is:
terrain.terrainData.SetHeights(0, 0, new float[,] { { } });
As I understand it this is setting the heights for the entire terrain. Could anyone help me in understanding what the input variables to SetHeights mean and what their ranges are. This would help me to only update the specific tile/zone where the tree was removed.
I know this an old thread but hopefully someone will answer me…
I cant understand why the trees are destroying themselves. I mean witch is the line of code that gives this command?
Basically the script iterates through all the trees in your terrain and then if they are within the blast radius, they get replaced. In the C# example, this is the line of code that is replacing the trees with the destroyed ones.
Now if you want, an addition to this code is to instantiate your terrain and its data. Then use that instantiated version to remove trees. This way, when you stop the runtime, you won’t lose any of your original terrain data. Here’s what I did and you’re welcome to use it. You may have to adjust variable names to your liking.
public GameObject primaryTerrain;
public TerrainData primaryTData;
private GameObject instanceTerrain;
private TerrainData instanceTData;
void Start()
{
//instantiate the terrain data
instanceTData = Instantiate(primaryTData);
//create a new terrain object using the instantiated terrain data
instanceTerrain = Terrain.CreateTerrainGameObject(instanceTData);
Vector3 position = new Vector3(0,0,0);
//getting the component of my original terrain so I can disable it. We don't need two identical terrains in the scene
Terrain terrain = primaryTerrain.GetComponent<Terrain>();
terrain.enabled = false;
}
There are two public variables, one for the terrain I’m going to instantiate, and one for the data related to that particular terrain. I already had a completed terrain, so I could just drag and drop my terrain and its data onto my script.
Now throughout your script, you can reference instanceTData for your instanced terrain data and instanceTerrain for the terrain game object. Hope this helps.