user7550418
user7550418

Reputation:

Checking collisions in near objects for random object spawning in Unity 5

I have almost finished my first game in Unity, which is a simple maze with characters collecting different coins. As it was a hassle to manually put all the different coins in the terrain, I decided to try and make a random generator for the coins.

The script is largely working, but the problem is that it's spawning objects most of the time inside walls. So I tried to add rigid bodies to coins, but this is messing with the coins position as the coins have a script for rotation. I also have the coins collision triggered because they perform a certain action when colliding with the character.

So after some research I saw that I need to check in my generator script for a near object collision and somehow change the spawn position of the coin if it's true. I'm trying to figure out how I can do that. My script is attached to my terrain and it looks like this:

using System.Collections;
using CustomArrayExtensions;
using UnityEngine;

public class ObjectSpawner : MonoBehaviour
{

[SerializeField] public GameObject[] letters;

    GameObject selectedObject;

    private int amount = 0;

    public int limit;

    void Start()
    {
        StartCoroutine(SpawnObjects());
    }


    IEnumerator SpawnObjects()
    {

        selectedObject = letters.GetRandom();

        while (amount < limit)
        {
            Instantiate(selectedObject, new Vector3(Random.Range(35.0f, 950.0f), 13.0f, Random.Range(35.0f, 950.0f)), Quaternion.identity);
            yield return new WaitForSeconds(5.0f);
            amount++;
        }
    }

    void OnCollisionEnter(Collision col)
    {
        if (col.gameObject.name == "Fox_Main")
        {
            Debug.Log("Collision Detected");
        }
    }
}

So i tried both answers and they didn't help me.Coins and other objects is still spawning close/inside the walls.My boxes have box collider and rigidbody with all positions/rotations locked(i need it that way so that i can destroy it later without moving it with the character),walls have box collider.So i tried to modify your solutions following Unity Documentation.I failed too.Here is what i have done so far. See here

using System.Collections;
using UnityEngine;

public class RandomCrates : MonoBehaviour
{
  public GameObject[] crates;
  private GameObject selectedCrate;
  public int limit = 0;
  public float x, y, z;
  public float forX_From,forX_To,forZ_From,forZ_To;     
  private int mask = 1 << 7;

  void Start()
  {
      StartCoroutine(SpawnObjects());
  }

  IEnumerator SpawnObjects()
  {
      for (int i = 0; i < crates.Length; i++)
      {
          for (int j = 0; j < limit; j++)
          {
              Vector3 freePos = GetFreePosition();
              Instantiate(crates[i], freePos, Quaternion.identity);
              yield return new WaitForSeconds(0.0f);
          }
      }

  }

  Vector3 GetFreePosition()
  {
      Vector3 position;
      Collider[] collisions = new Collider[1];
      do
      {
          position = new Vector3(Random.Range(forX_From, forX_To), 10.0f, Random.Range(forZ_From, forZ_To));
      } while (Physics.OverlapBoxNonAlloc(position, new Vector3(x, y, z), collisions, Quaternion.identity, mask) > 0);

      return position;
  }

}

Upvotes: 0

Views: 2067

Answers (2)

usenmez
usenmez

Reputation: 46

Unity provides a simple way to detect collisions in specific position with Physics.OverlapSphere:

public class ObjectSpawner : MonoBehaviour
{
    [SerializeField] public GameObject[] letters;
    GameObject selectedObject;
    private int amount = 0;
    public int limit;
    private float radius = 2f; //Radius of object to spawn

    void Start()
    {
        StartCoroutine(SpawnObjects());
    }


    IEnumerator SpawnObjects()
    {

        selectedObject = letters[Random.Range(0, letters.Length)];

        while (amount < limit)
        {
            Vector3 spawnPos = new Vector3(Random.Range(35.0f, 950.0f), 13.0f, Random.Range(35.0f, 950.0f));
            //Check collisions
            if (DetectCollisions(spawnPos) > 0)
                continue;

            Instantiate(selectedObject, spawnPos, Quaternion.identity);
            yield return new WaitForSeconds(5.0f);
            amount++;
        }
    }

    private int DetectCollisions(Vector3 pos) 
    {        
        Collider[] hitColliders = Physics.OverlapSphere(pos, radius);
        return hitColliders.Length;
    }

}

In this way, if there is a collision in spawn position, the coin will not be spawned.

Upvotes: 0

DerClef
DerClef

Reputation: 171

There are different ways of approaching this problem and given more information one could come up with a smarter approach. However, to provide you with a simple solution to your problem: You could just pick random positions until you find a "free spot". Assuming your coins to have a size of 1, you could do something like:

IEnumerator SpawnObjects()
{
    selectedObject = letters.GetRandom();

    while(amount < limit)
    {
        Vector3 freePos = GetFreePosition();
        Instantiate(selectedObject, freePos), Quaternion.identity);
        yield return new WaitForSeconds(5.0f);
        amount++;
    }
}

Vector3 GetFreePosition()
{
    Vector3 position;
    Collider[] collisions = new Collider[1];
    do
    {
        position = new Vector3(Random.Range(35.0f, 950.0f), 13.0f, Random.Range(35.0f, 950.0f));
    }
    while(Physics.OverlapSphereNonAlloc(position, 1f, collisions) > 0);

    return position;
}

Please note that Physics.OverlapSphereNonAlloc() can also accept a LayerMask for filtering collisions based on layers. You might want to look into this, to prevent detecting collision with your terrain, which may well keep the do-while loop from exiting and freeze Unity.

Upvotes: 0

Related Questions