Can someone fix this mass tree placement script so it will work on newer unity versions (e.g. 2019 etc)
using UnityEngine;
namespace UnityContrib.UnityEngine
{
/// <summary>
/// Values for placing random trees.
/// </summary>
/// <remarks>
/// <see cref="T:UnityContrib.UnityEditor.MassTreePlacementEditor"/> does the actual work.
/// </remarks>
[AddComponentMenu("Terrain/Mass Tree Placement")]
[RequireComponent(typeof(Terrain))]
public class MassTreePlacement : MonoBehaviour
{
#pragma warning disable 0414 // The field 'XYZ' is assigned but never used
/// <summary>
/// The number of trees to place.
/// </summary>
[Tooltip("The number of trees to place.")]
[SerializeField]
private int count = 70000;
/// <summary>
/// The lowest point to position a tree.
/// </summary>
[Tooltip("The lowest point to position a tree.")]
[SerializeField]
private float minWorldY = 1.0f;
/// <summary>
/// The highest point to position a tree.
/// </summary>
[Tooltip("The highest point to position a tree.")]
[SerializeField]
private float maxWorldY = 5200.0f;
/// <summary>
/// The minimum allowed slope of the ground to position a tree.
/// </summary>
[Tooltip("The minimum allowed slope of the ground to position a tree.")]
[SerializeField]
private float minSlope = 0.0f;
/// <summary>
/// The maximum allowed slope of the ground to position a tree.
/// </summary>
[Tooltip("The maximum allowed slope of the ground to position a tree.")]
[SerializeField]
private float maxSlope = 40.0f;
/// <summary>
/// The minimum value to scale the width of a tree.
/// </summary>
[Tooltip("The minimum value to scale the width of a tree.")]
[SerializeField]
private float minWidthScale = 0.9f;
/// <summary>
/// The maximum value to scale the width of a tree.
/// </summary>
[Tooltip("The maximum value to scale the width of a tree.")]
[SerializeField]
private float maxWidthScale = 2.0f;
/// <summary>
/// The minimum value to scale the height of a tree.
/// </summary>
[Tooltip("The minimum value to scale the height of a tree.")]
[SerializeField]
private float minHeightScale = 0.8f;
/// <summary>
/// The maximum value to scale the height of a tree.
/// </summary>
[Tooltip("The maximum value to scale the height of a tree.")]
[SerializeField]
private float maxHeightScale = 3.5f;
/// <summary>
/// The maximum number of seconds for the placement process to take.
/// The process is aborted if it takes any longer.
/// </summary>
[Tooltip("The maximum number of seconds for the placement process to take. The process is aborted if it takes any longer.")]
[SerializeField]
private double maxTime = 30.0d;
#pragma warning restore 0414 // The field 'XYZ' is assigned but never used
/// <summary>
/// Gets or sets the number of trees to place.
/// </summary>
public int Count
{
get
{
return count;
}
set
{
count = value;
}
}
/// <summary>
/// Gets or sets the lowest point to position a tree.
/// </summary>
public float MinWorldY
{
get
{
return minWorldY;
}
set
{
minWorldY = value;
}
}
/// <summary>
/// Gets or sets the highest point to position a tree.
/// </summary>
public float MaxWorldY
{
get
{
return maxWorldY;
}
set
{
maxWorldY = value;
}
}
/// <summary>
/// Gets or sets the minimum allowed slope of the ground to position a tree.
/// </summary>
public float MinSlope
{
get
{
return minSlope;
}
set
{
minSlope = value;
}
}
/// <summary>
/// Gets or sets the maximum allowed slope of the ground to position a tree.
/// </summary>
public float MaxSlope
{
get
{
return maxSlope;
}
set
{
maxSlope = value;
}
}
/// <summary>
/// Gets or sets the minimum value to scale the width of a tree.
/// </summary>
public float MinWidthScale
{
get
{
return minWidthScale;
}
set
{
minWidthScale = value;
}
}
/// <summary>
/// Gets or sets the maximum value to scale the width of a tree.
/// </summary>
public float MaxWidthScale
{
get
{
return maxWidthScale;
}
set
{
maxWidthScale = value;
}
}
/// <summary>
/// Gets or sets the minimum value to scale the height of a tree.
/// </summary>
public float MinHeightScale
{
get
{
return minHeightScale;
}
set
{
minHeightScale = value;
}
}
/// <summary>
/// Gets or sets the maximum value to scale the height of a tree.
/// </summary>
public float MaxHeightScale
{
get
{
return maxHeightScale;
}
set
{
maxHeightScale = value;
}
}
/// <summary>
/// Gets or sets the maximum number of seconds for the placement process to take.
/// The process is aborted if it takes any longer.
/// </summary>
public double MaxTime
{
get
{
return maxTime;
}
set
{
maxTime = value;
}
}
}
}
and this is for the editor…
using System;
using System.Reflection;
using UnityContrib.UnityEngine;
using UnityEditor;
using UnityEngine;
using Random = UnityEngine.Random;
namespace UnityContrib.UnityEditor
{
/// <summary>
/// Places random trees on a terrain.
/// </summary>
[CanEditMultipleObjects]
[CustomEditor(typeof(MassTreePlacement))]
public class MassTreePlacementEditor : Editor
{
/// <summary>
/// Reference to the <see cref="T:UnityEngine.TerrainData"/> type
/// for later invocation using reflection.
/// </summary>
private static readonly Type terrainDataType = typeof(TerrainData);
/// <summary>
/// Draws the "Place" and "Clear" buttons.
/// </summary>
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
var mtp = this.target as MassTreePlacement;
var terrain = mtp.GetComponent<Terrain>();
if (GUILayout.Button("Place"))
{
PlaceRandomTrees(terrain, mtp);
}
if (GUILayout.Button("Clear"))
{
Clear(terrain);
}
}
/// <summary>
/// Clears the terrain of all trees.
/// </summary>
/// <param name="terrain">
/// The terrain who's trees to remove.
/// </param>
public static void Clear(Terrain terrain)
{
terrain.terrainData.treeInstances = new TreeInstance[0];
RecalculateTreePositions(terrain.terrainData);
terrain.Flush();
}
/// <summary>
/// Places random trees on the terrain.
/// </summary>
/// <param name="terrain">
/// The terrain to place trees on.
/// </param>
/// <param name="mtp">
/// The settings for the tree placement.
/// </param>
public static void PlaceRandomTrees(Terrain terrain, MassTreePlacement mtp)
{
var data = terrain.terrainData;
var num = data.treePrototypes.Length;
if (num == 0)
{
Debug.LogWarning("Can't place trees because no prototypes are defined. Process aborted.");
return;
}
Undo.RegisterCompleteObjectUndo(data, "Mass Place Trees");
var start = DateTime.Now;
var array = new TreeInstance[mtp.Count];
var i = 0;
while (i < array.Length)
{
// stop if process have run for over X seconds
var delta = DateTime.Now - start;
if (delta.TotalSeconds >= mtp.MaxTime)
{
Debug.LogWarning("Process was taking too much time to run");
return;
}
var position = new Vector3(Random.value, 0.0f, Random.value);
// don't allow placement of trees below minWorldY and above maxWorldY
var y = data.GetInterpolatedHeight(position.x, position.z);
var worldY = y + terrain.transform.position.y;
if (worldY < mtp.MinWorldY || worldY > mtp.MaxWorldY)
{
continue;
}
// don't allow placement of trees on surfaces flatter than minSlope and steeper than maxSlope
var steepness = data.GetSteepness(position.x, position.z);
if (steepness < mtp.MinSlope || steepness > mtp.MaxSlope)
{
continue;
}
var color = Color.Lerp(Color.white, Color.gray * 0.7f, Random.value);
color.a = 1f;
var treeInstance = default(TreeInstance);
treeInstance.position = position;
treeInstance.color = color;
treeInstance.lightmapColor = Color.white;
treeInstance.prototypeIndex = Random.Range(0, num);
treeInstance.widthScale = Random.Range(mtp.MinWidthScale, mtp.MaxWidthScale);
treeInstance.heightScale = Random.Range(mtp.MinHeightScale, mtp.MaxHeightScale);
array[i] = treeInstance;
i++;
}
data.treeInstances = array;
RecalculateTreePositions(data);
terrain.Flush();
}
/// <summary>
/// Invokes <see cref="M:UnityEngine.TerrainData.RecalculateTreePositions"/> using reflection.
/// </summary>
/// <param name="data">
/// The instance to invoke the method on.
/// </param>
/// <remarks>
/// Some say it is enough to simply call <see cref="M:UnityEngine.Terrain.Flush"/>
/// however I'm not sure what the difference is, so I'm calling this just to be on the safe side.
/// </remarks>
private static void RecalculateTreePositions(TerrainData data)
{
terrainDataType.InvokeMember(
"RecalculateTreePositions",
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.InvokeMethod,
null,
data,
null
);
}
}
}