Seasonal Terrain Trees From Script

I’d like to add a bit of seasonal flare to my trees which are not placed gameObjects in the hierachy, they are trees added via the Terrain paint widget. I created a gameObject test tree that pingpongs between red and green via script, added that tree into the terrain tree painter and put a few down but at runtime they don’t change. I had a feeling it wouldn’t be that easy :wink:

Is there a method to access a TERRAIN tree and modify it via script/C#?

Putting together parts I found on UA, forums, general Google search etc I have came up with the following. I’m not sure if I should post it given the warnings associated with changing TerrainData but it does what I wanted and could be extended to do much more. On the other hand, if a moderator thinks it should be deleted that’s ok.

using UnityEngine;
using System.Collections;

// WARNING WARNING WARNING: The sources I found to assist with this indicate these were/might still be undocumented APIs,
// and there's no undoing the changes made to your tree and terrain - it WILL NOT SNAP BACK when you click Stop in the Editor.
// There could be other complications as well. Use at your risk.

// Step 1. Create a tree/prefab and put it into the Terrain Tree Painter
// Step 2. Create the same tree/model/etc but change the color to simulate a Fall color scheme and add it to Terrain Tree Painter
// Step 3. Attach this to something

public class QM_TreeSeasons : MonoBehaviour
{

	public TerrainData terrain; 			// drag the Terrain Data in Editor
	public int treeCount;
	public int treeTypes;
	public TreeInstance[] currentTreeList;
	private bool season;
	
	// These values are the prefab/prototype index value spat out by Start()
	
	public int springTreeIndex;
	public int fallTreeIndex;
	
	void Awake ()
	{
		treeCount = 0;	
		treeTypes = 0;
		springTreeIndex = -1;
		fallTreeIndex = -1;
	}
	
	void Start ()
	{
		
		//Set active terrain
		terrain = Terrain.activeTerrain.terrainData;		
		
		// Setup a holding array
		currentTreeList = new TreeInstance[terrain.treeInstances.Length];
		
		// Get some numbers
		treeTypes = terrain.treePrototypes.Length;
		treeCount = terrain.treeInstances.Length;
		
		// Displays some numbers
		Debug.Log ("Tree types (i.e # of prefabs in Terrain tree painter: " + treeTypes);
		Debug.Log ("They are: ");

		// Search the trees and print the name and index
		for (int cnt=0; cnt < terrain.treePrototypes.Length; cnt++) {
			Debug.Log ("name: " + terrain.treePrototypes [cnt].prefab.name + " @ prototype index " + cnt);
			
		}
		
	}
	
	// STOP STOP STOP: 
	// ChangeSeasons() changes your TerrainData and there's no going back.
	// If you run this next block you will be changing, permanently,
	// your TerrainData trees. Don't press "X" unless you are sure.
		
	
	void ChangeSeasons ()
	{
		
		// Copy existing TreeInstances array contents into holding array
		System.Array.Copy (terrain.treeInstances, currentTreeList, terrain.treeInstances.Length);
				
		if (terrain.treeInstances.Length == currentTreeList.Length) {
			
			for (int tcnt=0; tcnt < currentTreeList.Length; tcnt++) {
					
				if (season) {
					if (currentTreeList [tcnt].prototypeIndex == springTreeIndex) {
						currentTreeList [tcnt].prototypeIndex = fallTreeIndex;
					}
				}
				if (!season) {
					if (currentTreeList [tcnt].prototypeIndex == fallTreeIndex) {
						currentTreeList [tcnt].prototypeIndex = springTreeIndex;
					}
				}
			}
			terrain.treeInstances = currentTreeList;
			season = !season;
		}
		
	}
	
	// Having giving all the warnings above, if you want to toggle your trees, press X again
	// I don't know if other stuff in your terrain data might be affected. Use at your own risk.

	void Update ()
	{
		if (Input.GetKeyUp (KeyCode.X)) {
			if (springTreeIndex == -1 || fallTreeIndex == -1) {
				Debug.Log ("You have to set these values matching the desired models");
				Debug.Log ("prefab index values shown in Debug.Log");
			} else {
				ChangeSeasons ();
			}
		}
	}
}