Physics.OverlapSphere isn't behaving as expected

I’m randomly generating a layout of a forest that the player will have to navigate through. Each tree is a asset I have created in Blender and I’ve imported them into Unity and created a prefab for each tree. I have created a new layer called trees that is in the “User Layer 8” position in the layer editor.

The generation of each level is handled by a game object that has a script it runs.

Below is the script in its current form.

using UnityEngine;
using System.Collections;

public class generation2 : MonoBehaviour {

	public GameObject[] trees; 

	private int width;
	private int length;
	private int scale;
	private float size;
	private Transform forestHolder; //to be used later

	// Use this for initialization
	void Start () 
	{
		width = 10;
		length = 20;
		scale = 40;

		GenerateLayout ();
	}

	void GenerateLayout()
	{
		for(int x = 1; x < width; x++) //loop along the x axis
		{
			for(int z = 1; z < length; z++) //loop along the z axis
			{
				size = Random.Range (0, 2); //used for scaling trees
		
				//create tree
				GameObject instance = Instantiate (trees[Random.Range (0,5)], new Vector3(Random.Range(x*scale, (x*scale)+(scale)), 0, Random.Range(z*scale, (z*scale)+(scale))), Quaternion.Euler(0, Random.Range(0, 360), 0 )) as GameObject;
				instance.transform.localScale += new Vector3(size, size, size);

				//Ask instance for its sphere collider's radius and check to see if there is any overlapping colliders in that given radius. 
				Collider[] colliders = Physics.OverlapSphere(instance.transform.position, instance.transform.GetComponent<SphereCollider>().radius, 8);

                    foreach(Collider collider in colliders)
					{
						float distance = Vector3.Distance(collider.gameObject.transform.position, instance.transform.position);
						float cRad = collider.gameObject.transform.GetComponent<SphereCollider>().radius; 
						float iRad = instance.transform.GetComponent<SphereCollider>().radius;	
						
						Ray ray = new Ray(collider.gameObject.transform.position, instance.transform.position);
						instance.transform.Translate(ray.direction * (distance - ((distance - cRad) + (distance - iRad))));



				}
			}
		}
	}
}

When running the code the array colliders is always empty. I’ve messed with the layer that Physics.OverlapSphere looks at and I can get it to find the plane that is acting as the ground but not the other trees. Obviously I’m misunderstanding how this function is working.

Anyone able to shed some light on this for me?

Methinks a frame needs to pass before new colliders are detected. This implies at least _TREECOUNT+1 frames to complete placement. If you want to go that route, place your generator in a coroutine and yield WaitForEndOfFrame() between placements.

If this is all about placing trees, you should strongly consider ditching the collider-based approach anyway. Achieve separation by measuring the distance between origins of placed trees. Apart from being exponentially less expensive, that would not require an elapsed frame between trees. You could further mitigate the time cost of placement, improve your ratio of valid picks, and increase aesthetic appeal by placing trees in grid cells (N per square meter) and/or by using noise to suggest potentially valid placements.

You’re sending in a layer to the overlapSphere method like this:

Physics.OverlapSphere(instance.transform.position, instance.transform.GetComponent<SphereCollider>().radius, 8);

That’s wrong! OverlapSphere takes a layerMask, which is a binary number where the index of the 1’s is the layers that can be hit. In your case, that would be:

00000000000000000000000010000000 //Layer 8 marked.

The value 8, is, as you probably know, 2^3, so it would be this value in binary:

00000000000000000000000000000100

Which is a different value entirely!

The usual way to supply layerMasks is to bit-shift the value 1 by the number of layers you need to move the 1, in your case 8. So that would be:

int treeLayerMask = 1 << 8; //equal to the above 00000000000000000000000010000000

It’s usually easier to set the layermask from it’s name, through the LayerMask.LayerToName method:

int treeLayerMask = 1 << LayerMask.LayerToName("tree"); //The method returns 8 if tree is in the layer 8

Using the string-based name is also helpfull when you delete a layer that you thought wasn’t used - if you have used LayerToName on a name that doesn’t exist (anymore), you get a “The layer named X does not exist any more”. If you use the integer value, you don’t get any such messages.

You supply the layerMask as you already did, but with the correct layer instead of 8:

Physics.OverlapSphere(instance.transform.position, instance.transform.GetComponent<SphereCollider>().radius, LayerMask.NameToLayer("tree"));