Search code examples
unity-game-enginespawn

How to prevent same prefab from spawning twice in a row in Unity


Unity beginner here, I have a random prefab spawner attached to my game in Unity which randomly spawns 3 prefabs. The problem is, sometimes I get the same prefab like 5 times in a row. How can I prevent the same prefab from spawning twice in a row? Here is my code:

public class randomspawnscript : MonoBehaviour
{
    public GameObject prefab1, prefab2, prefab3;


    public float spawnRate = 2f;

    float nextSpawn = 0f;

    int whatToSpawn;

    void Update()
    {
        if (collisionbutton.end != true || gameoverscreenrestart.restartPressed==true || gameovermainmenu.menuPressed==true)
        {
            if (Time.time > nextSpawn)
            {
                whatToSpawn = Random.Range(1, 4);
                Debug.Log(whatToSpawn);

                switch (whatToSpawn)
                {
                    case 1:
                        Instantiate(prefab1, transform.position, Quaternion.identity);
                        break;
                    case 2:
                        Instantiate(prefab2, transform.position, Quaternion.identity);
                        break;
                    case 3:
                        Instantiate(prefab3, transform.position, Quaternion.identity);
                        break;
                }
                nextSpawn = Time.time + spawnRate;
            }
        }
        else
        {
            return;
        }   
    }
}

Solution

  • A simple way using the Unity's in build Random system is just to create a list of possible generated numbers, and pick a random number from that list, like so:

    public class randomspawnscript : MonoBehaviour {
        public GameObject prefab1, prefab2, prefab3;
    
        public float spawnRate = 2f;
    
        float nextSpawn = 0f;
    
        int whatToSpawn;
    
        private void Awake() {
            // To not get a null ref error when generating the controlled random
            // for the first time.
            whatToSpawn = 0;
        }
    
        void Update() {
            if (/* ... */) {
                if (Time.time > nextSpawn) {
                    whatToSpawn = GetControlledRandom();
                    Debug.Log(whatToSpawn);
    
                    switch (whatToSpawn) {
                        //...
                    }
                    nextSpawn = Time.time + spawnRate;
                }
            } else {
                return;
            }
        }
    
        int GetControlledRandom() {
            List<int> possibleChoices = new List<int> {
                1, 2, 3
            };
    
            // Removes what was spawned before from the possible choices.
            possibleChoices.Remove(whatToSpawn);
    
            return possibleChoices[Random.Range(0, possibleChoices.Count)];
        }
    }
    

    Alternatively, the more simpler way is to just keep generating a number until you get the one you are satisfied with, like so:

        int RetardedControlledRandom() {
            int generatedNumber;
            do {
                generatedNumber = Random.Range(1, 4);
            } while (generatedNumber == whatToSpawn);
    
            return generatedNumber;
        }
    

    This can help if you decide to use the .NET provided System.Random instead.

    Also, note that currently most of your values/variables are hardcode.
    (Aka it does not dynamically suit to spawning more than 4 types of prefab)

    Unity Inspector accepts an array too, so you can make use of that and refactor your code like so:

    public class randomspawnscript : MonoBehaviour {
        public GameObject[] possibleSpawnPrefabs;
    
        public float spawnRate = 2f;
    
        float nextSpawn = 0f;
    
        int whatToSpawn;
    
        private void Awake() {
            whatToSpawn = 0;
        }
    
        void Update() {
            if (collisionbutton.end != true || gameoverscreenrestart.restartPressed == true || gameovermainmenu.menuPressed == true) {
                if (Time.time > nextSpawn) {
                    whatToSpawn = GetControlledRandom();
                    Debug.Log(whatToSpawn);
    
                    var prefabToSpawn = possibleSpawnPrefabs[whatToSpawn];
                    Instantiate(prefabToSpawn, transform.position, Quaternion.identity);
    
                    nextSpawn = Time.time + spawnRate;
                }
            } else {
                return;
            }
        }
    
        int GetControlledRandom() {
            List<int> possibleChoices = new List<int>();
    
            for (int i = 0; i < possibleSpawnPrefabs.Length; ++i) {
                possibleChoices.Add(i);
            }
    
            // Removes what was spawned before from the possible choices.
            possibleChoices.Remove(whatToSpawn);
    
            return possibleChoices[Random.Range(0, possibleChoices.Count)];
        }
    }