[Solved] Placing trees with scripts on terrain

It’s a pretty simple survival game, you go up to trees and it adds to your wood count. But I can’t be expected to manually place the 20,000 trees I have on the map (via mass tree placement terrain tool). And if I use that tool the scripts on the trees don’t work. So I’m really confused on what it is I should do, and I certainly don’t have any money to spend on an editor extension. Thank you.

EDIT:

As you can see I’ve already done the terrain to how I’d want, and as you can clearly see it’d be too much work to place trees down one by own

EDIT:
Discovered a solution. It is to grab all the TreeInstances in the terrain’s TerrainData and place prefabs. Then remove all of the trees from the TerrainData. Word of caution (As I’ve just found out the hard way) changing TerrainData in-game effects the TerrainData in the editor. So when I stopped playing the game all the trees were gone in the editor as well. But it still works.

(In short it removes all trees on the map and replaces them with the GameObject you set, however the trees will also be removed from the map on the editor so use this carefully)

Here’s the code I developed:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class StartGame : MonoBehaviour {

    public GameObject theTree;

    // Use this for initialization
    void Start () {
        // Grab the island's terrain data
        TerrainData theIsland;
        theIsland = GameObject.Find ("PrimeIsland").GetComponent<Terrain> ().terrainData;
        // For every tree on the island
        foreach (TreeInstance tree in theIsland.treeInstances) {
            // Find its local position scaled by the terrain size (to find the real world position)
            Vector3 worldTreePos = Vector3.Scale(tree.position, theIsland.size) + Terrain.activeTerrain.transform.position;
            Instantiate (theTree, worldTreePos, Quaternion.identity); // Create a prefab tree on its pos
        }
        // Then delete all trees on the island
        List<TreeInstance> newTrees = new List<TreeInstance>(0);
        theIsland.treeInstances = newTrees.ToArray ();
    }
}
8 Likes

bump

some info here,

If you paint trees on terrain, then you need to access them through terraindata,

How would I go about seeing which tree I’ve interacted with from the tree instances array?

take closest tree to player,
or if you create separate colliders for each tree position,
then could assign tree index number for each collider…(to keep track in script).

but in that case, shouldnt remove trees, just replace with another object (maybe tree stump),
so that treecount and indexes wont change… *havent tested if would work actually

Are there any alternatives to this? I still want everything that comes along with a unity tree (Billboard at distance, easy placement) but all the features of a prefab. I think there may be some extensions on the store but I have no cash to pay for that sort of thing

Like, how does The Forest do it? They have very interactive trees and they seem to hold all the features of a normal Unity tree

bump

1 Like

You could still paint trees with terrain tools, then take their positions and replace terrain trees with prefab trees.
Can use LODs on tree models to improve performance.

Theres also mesh terrain alternatives, most likely those are more flexible… (not sure if any free ones)

Alright, how would I replace them? I hate to ask so much but there is so little information on this

1 Like

bump

Fixed it - fix posted in thread

Cool Solution! I have a flamethrower in my game, and I wanted to be able to set trees on fire with it. Can I use your code? I’ll give credit.

Sure, and I’d love to see the final version. Message me with where I can check out development!

Ive been working on systems to make trees more interactive Ex. chop-able, burnable, etc for over a year and it looks like I handled it pretty similar to this forum. I used to have a kinda large script for handling chopping a tree down but im in the process of reworking my tree chopping system to be more streamline.

I’d love to hear more.

I’m currently working on tree chopping, as well. And every other post I find on the topic directs me to this post (http://answers.unity3d.com/questions/650308/how-do-i-interact-with-terrain-trees.html) but it doesn’t deal with the collider (a.k.a invisible higher terrain where the tree was).

The only “collider fix” I found runs through the entire tree array upon interaction causing temporary freeze of the game.

Hi all, not sure who might find it useful but I made an improved version of this code that takes into account the terrain trees’ position and rotation, as well as putting them under a parent object so they can be managed more easily:

void ConvertTerrainTreesToPrefabs()
    {
        // Grab the island's terrain data
        TerrainData thisTerrain;
        thisTerrain = GetComponent<Terrain>().terrainData;

        //GameObject to be the parent
        GameObject treeParent =  new GameObject("treeParent");

        // For every terrain tree on the island
        foreach (TreeInstance terrainTree in thisTerrain.treeInstances)
        {
            // Find its local position scaled by the terrain size (to find the real world position)
            Vector3 worldTreePos = Vector3.Scale(terrainTree.position, thisTerrain.size) + Terrain.activeTerrain.transform.position;
           
            // Create the new tree out of the prefab
            GameObject prefabTree = Instantiate(theTree, worldTreePos, Quaternion.identity, treeParent.transform); // Create a prefab tree on its pos

            //Then set the new tree to the randomized size and rotation of the terrain tree
            prefabTree.transform.localScale = new Vector3(terrainTree.widthScale * 50, terrainTree.heightScale * 50, terrainTree.widthScale * 50);
            prefabTree.transform.rotation = Quaternion.AngleAxis(terrainTree.rotation * 57.2958f, Vector3.up);
        }
        // Then delete all the terrain trees on the island
        List<TreeInstance> newTrees = new List<TreeInstance>(0);
        thisTerrain.treeInstances = newTrees.ToArray();
    }
2 Likes

Why delete all the trees when you can just set tree distance to 0 on the terrain and save them incase you need to edit them? In theory you can do the prefab swap at runtime as well, then set tree distance to 0.

1 Like

Made something like this:

using System.Collections.Generic;
using UnityEngine;
public class TreeGenerator : MonoBehaviour
{
    [SerializeField] private bool createOnStart;
    [SerializeField] private List<GameObject> trees;
    [SerializeField] private float minSize,maxSize;
    private void Start()
    {
        if (!createOnStart) return;
        var terrain = GetComponentInChildren<Terrain>();
        var terrainData = terrain.terrainData;
        foreach (var terrainTree in terrainData.treeInstances)
        {
            var worldTreePos = Vector3.Scale(terrainTree.position, terrainData.size) + Terrain.activeTerrain.transform.position;
            var tree = Instantiate(trees[Random.Range(0,trees.Count)], worldTreePos, Quaternion.identity, transform); 
            tree.transform.localScale = Vector3.one*Random.Range(minSize,maxSize);
            tree.transform.rotation = Quaternion.AngleAxis(Random.Range(-360f,360f), Vector3.up);
        }
        terrain.treeDistance = 0;
    }
}

use guys)

1 Like

Good stuff, thank you, and all the other contributors. Tested, working effortlessly.