Script feedback/advice - tree spawning, using 'regions'

Hi all,

New here + new to Unity/C# in general. Thanks for having me and if I seem confused, it’s because I am. =)

I’m currently mucking around with spawning trees on a piece of terrain on first run, so far it’s been fairly straight forward - a ‘spawn’ script attached to a gameobject which, using a for loop, selects a tree from an array, finds a point on the terrain, checks a couple of things to make sure it’s not going to spawn somewhere stupid, adjusts the height to be on the terrain and then instantiates the tree. It’s been working fine, with a few post-spawn changes to the height, etc, based on the model (which is done by the individual object, not the spawner).

The spawn script looks a little something like this:

public class TreeSpawner : MonoBehaviour
{

    //Amount of Trees to spawn
    public int treesToSpawn = 500;

    //Tree array
    public GameObject[] treeTypes = new GameObject[0];
   
    //Tree height min/max
    public int treeMaxHeight;
    public int treeMinHeight;
   
    // Use this for initialization
    void Start ()
   
    {
        SpawnTrees();
    }
   
    // Update is called once per frame
    void Update ()
    {
   
    }
   
    void SpawnTrees()
    {
        //While new variable i = 0 is < treesToSpawn
        for (int i=0; i < treesToSpawn;)
        {
            //Select random tree from treeTypes array
            GameObject treeToSpawn = treeTypes[Random.Range(0, treeTypes.Length)];
           
            //Create a new vector3 to hold the spawn location
            Vector3 terrainPoint = new Vector3 (0,0,0);
           
            //Find the overall size of the terrain
            terrainPoint = Terrain.activeTerrain.terrainData.size;
           
            //Set randSpawn to use a random point from terrainPoint
            Vector3 randSpawn = new Vector3 (Random.Range (0,terrainPoint.x), 0, Random.Range (0,terrainPoint.z));
           
            //Find terrain height at randSpawn
            float spawnHeight = Terrain.activeTerrain.SampleHeight(randSpawn);
   
            //Adjust spawnHeight to be on terrain
            randSpawn.y = spawnHeight;

            //Only spawn and increment the for loop if the height is above/below the min/max heights
            if (spawnHeight < treeMaxHeight && spawnHeight > treeMinHeight)
            {
                //Spawn
                Instantiate (treeToSpawn, randSpawn,  Quaternion.identity);
               
                //Increment spawnTrees
                i++;
               
            }
           
        }
    }

So, I’m trying to get a little tricky now. I’d like to start spawning the trees in ‘biomes’ (or defined areas) but I’m having trouble working out how I can get this to ‘flow’ in the script.

I’m thinking of creating a bunch of ‘regions’ out of cubes then adjusting the above script to do the following:

  • Loop starts.

  • Find the spawn position, in the same way that it already does and then adjust the height.

  • Perform checks to height, etc.

  • Check at that position to see if there’s a ‘region’ there - thinking something like ‘Physics.OverlapSphere’ but not sure if/how this would work when checking for a tag?

  • From this, I’d ideally want to get something returned that I can use, so not just ‘yes, there is a collision’ but, ‘there’s a collision and this is the tag, or name of the object I collided with’.

  • Depending on that return, perform a function (so, void TreeArray1(), TreeArray2()), which would select a random tree from the relevant array and then instantiate it at the previously selected position.

This last part is where I’m getting stuck, I’ve found that my IF statements inside a for loop work a bit weird, so I’m having trouble breaking the script up. Normally I’d do something like ‘IF collision is with tag ‘blah’, then perform function blah()’ but even when using ‘else if’ it doesn’t seem to flow through correctly inside the loop - as in, if the initial IF condition fails, it just resets the loop, or doesn’t run the remaining code.

I guess what I’m asking is, does anyone know how I might be best going about this, is this even a good way to go about it or am I heading for headaches? I would greatly appreciate any suggestions, advice or examples that could be offered.

Thanks!

Lol, ok NM, I got it. I was just looking at it the wrong way.

For anyone interested, the fix was to handle the IF statement outside the for loop - not sure why it didn’t work inside, can anyone explain that to me?

The script now looks like this:

using UnityEngine;
using System.Collections;

public class TreeSpawner : MonoBehaviour
{

    //Amount of Trees to spawn
    public int treesToSpawn = 500;

    //Tree array
    public GameObject[] treeTypes1 = new GameObject[0];
    public GameObject[] treeTypes2 = new GameObject[0];
    public GameObject[] treeTypes3 = new GameObject[0];
    public GameObject[] treeTypes4 = new GameObject[0];
  
    //Tree height min/max
    public int treeMaxHeight;
    public int treeMinHeight;
  
    //randSpawn Vector3, holds the spawn point
    private Vector3 randSpawn;
  
    // Use this for initialization
    void Start ()
  
    {
        SpawnTrees();
    }
  
    // Update is called once per frame
    void Update ()
    {
  
    }
  
    void SpawnTrees()
    {
        //While new variable i = 0 is < treesToSpawn
        for (int i=0; i < treesToSpawn;)
        {
            //Create a new vector3 to hold the spawn location
            Vector3 terrainPoint = new Vector3 (0,0,0);
          
            //Find the overall size of the terrain
            terrainPoint = Terrain.activeTerrain.terrainData.size;
          
            //Set randSpawn to use a random point from terrainPoint
            randSpawn = new Vector3 (Random.Range (0,terrainPoint.x), 0, Random.Range (0,terrainPoint.z));
          
            //Find terrain height at randSpawn
            float spawnHeight = Terrain.activeTerrain.SampleHeight(randSpawn);
  
            //Adjust spawnHeight to be on terrain
            randSpawn.y = spawnHeight;

            //Only spawn and increment the for loop if the height is above/below the min/max heights
            if (spawnHeight < treeMaxHeight && spawnHeight > treeMinHeight) //&& spawnSlope <= spawnMaxSlope)
            {
                //Function to select array based on region
                selectArrayFromRegion();
              
                //Increment spawnTrees
                i++;
              
            }
          
        }
    }
  
    void selectArrayFromRegion()
    {
        //Check for collision with 'region' at randSpawn
        Collider[] regionCheck = Physics.OverlapSphere(randSpawn, 5);
      
        //For each collision, perform the following checks
        foreach (Collider regionHit in regionCheck)
      
        if (regionHit.tag == "TreeSpawnType1")
        {
            SpawnTreeType1();
        }
      
        else if (regionHit.tag == "TreeSpawnType2")
        {
            SpawnTreeType2();
        }
      
        else if (regionHit.tag == "TreeSpawnType3")
        {
            SpawnTreeType3();
        }
      
        else if (regionHit.tag == "TreeSpawnType4")
        {
            SpawnTreeType4();
        }
      
    }
  
    void SpawnTreeType1()
    {
        //Select random tree from treeTypes array
        GameObject treeToSpawn = treeTypes1[Random.Range(0, treeTypes1.Length)];  
      
        //Spawn
        Instantiate (treeToSpawn, randSpawn,  Quaternion.identity);
      
    }
  
    void SpawnTreeType2()
    {
        //Select random tree from treeTypes array
        GameObject treeToSpawn = treeTypes2[Random.Range(0, treeTypes2.Length)];  
      
        //Spawn
        Instantiate (treeToSpawn, randSpawn,  Quaternion.identity);
      
    }
  
    void SpawnTreeType3()
    {
        //Select random tree from treeTypes array
        GameObject treeToSpawn = treeTypes3[Random.Range(0, treeTypes3.Length)];  
      
        //Spawn
        Instantiate (treeToSpawn, randSpawn,  Quaternion.identity);
      
    }
  
    void SpawnTreeType4()
    {
        //Select random tree from treeTypes array
        GameObject treeToSpawn = treeTypes4[Random.Range(0, treeTypes4.Length)];  
      
        //Spawn
        Instantiate (treeToSpawn, randSpawn,  Quaternion.identity);
      
    }
  
}

This appears to work fine and I think it has the added advantage of preventing spawning if a second collision occurs with an object that’s not considered a region - e.g. another tree that’s already spawned - so you don’t get overlapping objects but I’m not 100% sure about this. I’m also not sure how efficient this all is…

Would be happy to hear any feedback anyone might be able to offer as I have absolutely no idea what I’m doing here. =P