Modify existing Mass Place Trees function?

I’ve used the mass place trees functionality lots of times and has been very useful but now I’d like to use it when I have water in my scene.

Currently it places a lot of them below the sea level. I’m sure there’s a way to remove them at runtime but I’d prefer to be able to do it before then without manually erasing them.

So is there a way to change how mass placing trees decides where to put them? Or how to make some editor extension to remove trees with a y value less than a number before run time

Cheers!

Ok, because this is a good question but the answer is complicated, I have written a couple of scripts.

Disclaimer : use at your own risk. Back up your terrain before using. I won’t be held responsible for lost trees !

STEP ONE : save your terrain! Export it by right-clicking on it in the Project Window, then select Export Package…

#To set this up :#

In the Project Window, check if you have a folder named Editor. If you don’t have this folder, create a new folder, name it Editor

Create a new script in the Editor folder, name it RemoveUnderwaterTrees

Copy in the first section of code below and save it.

Now create another script in the Editor folder, name it RemoveUnderwaterTreesMenuItem

Copy in the second section of code below and save it.

Sometimes Unity doesn’t update the menu’s after creating a menu item script the first time. Simply re-open the project.

#How to use this :#

In Unity, browse to Terrain > Remove Underwater Trees

An empty gameObject will be added to the scene called RemoveUnderwaterTrees. Click on this then look in the inspector.

There are 3 variables exposed :

Terrain : if you leave this, then the script will use the current active terrain. This is ok to leave (only tested with one terrain in the scene)

Water Level : this is the world-space transform position Y of your water. Put the value of your water Y position here

Perform Action : this is a drop-down menu with 3 options

BackupCurrentTrees
RestoreBackupTrees
RemoveUnderwaterTrees
  • So the first step is to set Perform Action to BackupCurrentTrees

  • Right-click on the script component in the Inspector, then a drop-down selection box appears. Example : http://docs.unity3d.com/Documentation/Images/manual/Inspector-3.jpg

  • There should be an option Modify Tree Data, click on this. Now the current terrain trees are stored in a variable.

  • Next, set the value for Water Level, change Perform Action to RemoveUnderwaterTrees

  • Again, Right-click on the script component in the Inspector, then a drop-down selection box appears. Click on Modify Tree Data, and hopefully all your underwater trees have disappeared !

  • Finally, if you make any mistake or want to change the water level and do it again, change Perform Action to RestoreBackupTrees

  • Right-click on the script component in the Inspector, then a drop-down selection box appears. Click on Modify Tree Data, and hopefully all your original trees are back where you started.

If there are any problems, luckily you followed STEP ONE and exported your terrain. Simply re-import this backup if there are problems. I have done limited testing on this, but am happy to submit my scripts. Please let me know what you think, and don’t forget to accept the answer if you like it, and upvoting always makes me happy too =]

Now for the scripts :

=========================================================================
RemoveUnderwaterTrees.js

#pragma strict
import System.Collections.Generic;

public var terrain : Terrain;

public var waterLevel : float = 0.0;

private var backupTreeInstances : TreeInstance[];
private var newTreeInstances : List.< TreeInstance >;

// enum : backup tree data, remove trees below water level, restore tree backup data
enum allTreeActions 
{
	BackupCurrentTrees,
	RestoreBackupTrees,
	RemoveUnderwaterTrees
}

public var performAction : allTreeActions;


#if UNITY_EDITOR

@ContextMenu( "Modify Tree Data" )

function ModifyTreeData() 
{
	// check if there is no terrain in the inspector, then use the current active terrain
	if ( !terrain )
	{
		terrain = Terrain.activeTerrain;
	}
	
	switch( performAction )
	{
		// backup the current trees
		case allTreeActions.BackupCurrentTrees :
			backupTreeInstances = terrain.terrainData.treeInstances;
			Debug.Log( "Current trees have been Stored in backup data" );
		break;
		
		// restore the backed up trees
		case allTreeActions.RestoreBackupTrees :
			if ( backupTreeInstances )
			{
				terrain.terrainData.treeInstances = backupTreeInstances;
				Debug.Log( "Trees have been Restored from the backup data" );
			}
			else
			{
				Debug.Log( "NO backup data FOUND ...." );
			}
		break;
				
		// remove trees below water level
		case allTreeActions.RemoveUnderwaterTrees :
		
			Debug.Log( "Removing trees below water level ...." );
			
			// get the width and depth of the terrain
			var terrainSize : Vector3 = terrain.terrainData.size;
			//Debug.Log( "terrainSize : " + terrainSize );
			
			// get the tree data from the terrain data
			var treeInstances : TreeInstance[] = terrain.terrainData.treeInstances;
			Debug.Log( "Old : Total Trees = " + treeInstances.length );
			
			// create a list to store the modified information
			newTreeInstances = new List.< TreeInstance >();
			
			// calculate the normalized Water Level
			var normalizedWaterLevel : float = waterLevel / terrainSize.y;
			
			// cycle through each tree
			for ( var t : int = 0; t < treeInstances.length; t ++ )
			{
				// check if the tree Y is lower than the water level
				if ( treeInstances[t].position.y > normalizedWaterLevel )
				{
					// if not, add tree to newTreeInstances List
					newTreeInstances.Add( treeInstances[t] );
				}
			}
			
			// apply newTreeInstances List to terrain data
			terrain.terrainData.treeInstances = new TreeInstance[ newTreeInstances.Count ];
			terrain.terrainData.treeInstances = newTreeInstances.ToArray();
			Debug.Log( "New : Total Trees = " + terrain.terrainData.treeInstances.length );
			
		break;
	}
}

#endif

=========================================================================
RemoveUnderwaterTreesMenuItem.js

#pragma strict

#if UNITY_EDITOR

@MenuItem( "Terrain/Remove Underwater Trees" )
static function NewRemoveUnderwaterTrees() 
{
	var go : GameObject = new GameObject( "RemoveUnderwaterTrees" );
	go.transform.position = Vector3.zero;
	go.AddComponent( RemoveUnderwaterTrees );
}

#endif

=========================================================================