Search code examples
c#unity-game-enginecollision-detectioninstantiation

Instantiate Spawning Hundreds of items instead of 1


I have written a script that detects when something collides with my game object. When something collides with it, the collision may last 1 - 2 seconds but I only want one game object to spawn. When I run the scene and collide with it hundreds of game objects spawn instantly and everything crashes.

I have tried using Thread.Sleep() and IEnumerator waitor() but no luck so far.

Any ideas would be appreciated, I will attach the code below

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

public class ColliderHit : MonoBehaviour
{
    public GameObject topLevelMenu;
    public GameObject sublevel;
    public GameObject topMenuItem;
    public GameObject menuItemTouched;
    public GameObject itemToSpawn;
    public bool topLevelItem;

    void OnTriggerEnter(Collider col)
    {
        if (col.gameObject.name != "" || col.gameObject.name == "RightHandVisual" || col.gameObject.name == "OculusHand_R" || col.gameObject.name == "HandGrabInteractor" || col.gameObject.name == "OVRHandPrefab")
        {

            if (topLevelItem)
            {
                topLevelMenu.SetActive(false);
                sublevel.SetActive(true);
                sublevel.transform.position = topMenuItem.transform.position;
                sublevel.transform.rotation = topMenuItem.transform.rotation;
            }
            else
            {
                StartCoroutine(waiter());
                var itemsPos = menuItemTouched.transform.position;
                var itemsRot = menuItemTouched.transform.rotation;
                var itemsSca = menuItemTouched.transform.localScale;
                GameObject spawned = Instantiate(itemToSpawn);
                spawned.transform.rotation = itemsRot;
                spawned.transform.localScale = itemsSca;
                var zpos = itemsPos.z - (0.1f);
                spawned.transform.position = new Vector3(itemsPos.x, itemsPos.y, zpos);
            }
        }
    }
    IEnumerator waiter()
    {
        //Wait for 4 seconds
        yield return new WaitForSeconds(4);;
    }
}


Solution

  • Solution to fix your code as is

    That's not how Co-routines work. The code that needs to be await needs to be within the coroutine:

    bool isOnCollideCooldown = false;
    
    void OnTriggerEnter(Collider col)
    {
        if (col.gameObject.name != "" || col.gameObject.name == "RightHandVisual" || col.gameObject.name == "OculusHand_R" || col.gameObject.name == "HandGrabInteractor" || col.gameObject.name == "OVRHandPrefab")
        {
    
            if (topLevelItem)
            {
                topLevelMenu.SetActive(false);
                sublevel.SetActive(true);
                sublevel.transform.position = topMenuItem.transform.position;
                sublevel.transform.rotation = topMenuItem.transform.rotation;
            }
            else
            {
                // Return if it is instantiated
                if (isOnCollideCooldown) return;
                
                // Otherwise spawn and wait
                StartCoroutine(waiter());
                
            }
        }
    }
    IEnumerator waiter()
    {
        // Go onto cooldown
        isOnCollideCooldown = true;
        
        // Spawn the object
        var itemsPos = menuItemTouched.transform.position;
        var itemsRot = menuItemTouched.transform.rotation;
        var itemsSca = menuItemTouched.transform.localScale;
        GameObject spawned = Instantiate(itemToSpawn);
        spawned.transform.rotation = itemsRot;
        spawned.transform.localScale = itemsSca;
        var zpos = itemsPos.z - (0.1f);
        spawned.transform.position = new Vector3(itemsPos.x, itemsPos.y, zpos);
        
        //Wait for 4 seconds
        yield return new WaitForSeconds(4);
        
        // Go off cooldown
        isOnCollideCooldown = false;
    }
    

    Additional Solution

    The issue is that when you spawn the object, it instantly collides with the other objects. So on the script with the object that spawns you can use the following:

    Collider col;
    public float noCollisionTime = 1;
    
    void Awake() {
        col.enabled = false;
        StartCoroutine(EnableCollider());
    }
    
    
    IEnumerator EnableCollider()
    {
        yield return new WaitForSeconds(noCollisionTime);
        col.enabled = true;
    }