Search code examples
c#listunity-game-enginespawn

Spawning the desired number of prefabs


How can I instantiate the desired number of prefabs using the following code? I need to spawn ONE (and only one) player prefab, X enemies and ONE (and only one) end game prefab.

    private void GenerateEnemies(int xMax, int zMax)
    {
        GameObject landscape = new GameObject("ENEMIES");


        for (int z = 0; z < zMax; z++)
        {
            for (int x = 0; x < xMax; x++)
            {
                randNum = Random.Range(0, 100);

                if (randNum < 10 )
                {
                    Instantiate(enemy1, new Vector3(x * 10, 0, z * 10), Quaternion.Euler(0, 0, 0));//, landscape.transform);
                }
                else if (randNum < 20)
                {
                    Instantiate(enemy2, new Vector3(x * 10, 0, z * 10), Quaternion.Euler(0, 0, 0));//, landscape.transform);
                }

                else if (randNum < 30)
                {
                    Instantiate(enemy3, new Vector3(x * 10, 0, z * 10), Quaternion.Euler(0, 0, 0));//, landscape.transform);
                }

                else if (randNum < 40)
                {
                    Instantiate(enemy4, new Vector3(x * 10, 0, z * 10), Quaternion.Euler(0, 0, 0));//, landscape.transform);
                }

                else if (randNum < 50)
                {
                    Instantiate(enemy5, new Vector3(x * 10, 0, z * 10), Quaternion.Euler(0, 0, 0));//, landscape.transform);
                }

            }
        }
    }

Solution

  • Well simply do your one-time things outside the loop!

    Also

    randNum = Random.Range(0, 100);
    

    and then you use only 5 different cases and only if the value is smaller 50 (so about in half of the cases nothing happens at all ...). If this was intended .. ok-ish .. otherwise I would rather use a list and random indices:

    // HINT: Rather have a list for your prefabs
    // this shrinks your code a lot
    public List<GameObject/*or whatever type*/> eneymPrefabs = new List<GameObject>();
    public Gamebject playerPrefab;
    public GameObject endGamePrefab;
    
    private void GenerateEnemies(int xMax, int zMax)
    {
        var landscape = new GameObject("ENEMIES");
    
        // Do these only once
        // store the references in case you need them later
        var player = Instantiate(playerPrefab);
        var endGame = Instantiate(endGamePrefab);
    
        for (int z = 0; z < zMax; z++)
        {
            for (int x = 0; x < xMax; x++)
            {
                // simply pick a random index from the prefab list
                int randIndex = Random.Range(0, eneymPrefabs.Count);
    
                // and get the according random prefab
                var enemyPrefab = enemyPrefabs[randIndex];
    
                if(enemyPrefab) Instantiate(enemyPrefab, new Vector3(x * 10, 0, z * 10), Quaternion.identity /*, landscape.transform*/);
            }
        }
    }
    

    Or an example for the weighted list mentioned by Draco18s

    [Serializable]
    public class WeightedPrefab
    {
        public GameObject Prefab;
        public int Weight = 1;
    }
    
    public List<WeightedPrefab> weightedEnemyPrefabs;
    public Gamebject playerPrefab;
    public GameObject endGamePrefab;
    
    private void GenerateEnemies(int xMax, int zMax)
    {
        // create a temp list using the weights and random index on this one
        var enemyPrefabs = new List<GameObject>();
        foreach(var item in weightedEnemyPrefabs)
        {
            for(var i = 0; i < item.Weight; i++)
            {
                enemyPrefabs.Add(item.Prefab);
            }
        }
    
        // Rest stays the same
    
        var landscape = new GameObject("ENEMIES");
    
        // Do these only once
        // store the references in case you need them later
        var player = Instantiate(playerPrefab);
        var endGame = Instantiate(endGamePrefab);
    
        for (int z = 0; z < zMax; z++)
        {
            for (int x = 0; x < xMax; x++)
            {
                // simply pick a random index from the prefab list
                int randIndex = Random.Range(0, eneymPrefabs.Count);
    
                // and get the according random prefab
                var enemyPrefab = enemyPrefabs[randIndex];
    
                if(enemyPrefab) Instantiate(enemyPrefab, new Vector3(x * 10, 0, z * 10), Quaternion.identity /*, landscape.transform*/);
            }
        }
    }
    

    If it was intentional that not in every case an enemy is instantiated you can still use both approaches and simply leave a prefab reference empty → for that index nothing will be instantiated.


    the enemy and end game should be part of the grid

    In this case I would first write the entire grid combinations into a list. Pick two random entries from this list and place the player and the endGame there. Then block these two grid positions and do not spawn enemies there:

    [Serializable]
    public class WeightedPrefab
    {
        public GameObject Prefab;
        public int Weight = 1;
    }
    
    public List<WeightedPrefab> weightedEnemyPrefabs;
    public Gamebject playerPrefab;
    public GameObject endGamePrefab;
    
    private void GenerateEnemies(int xMax, int zMax)
    {
        // Create a list of all awailable grid positions
        var gridPositions = new List<Vector2Int>();
        for (int z = 0; z < zMax; z++)
        {
            for (int x = 0; x < xMax; x++)
            {
                gridPositions.Add(new Vector2Int(x,z));
            }
        }
    
        // pick the two random positions for player and endgame
        var playerPosIndex = Random.Range(0, gridPositions.Count);
        var playerPos = gridPositions[playerPosIndex];
        gridPositions.RemoveAt(playerPosIndex);
    
        var endGamePosIndex = Random.Range(0, gridPositions.Count);
        var endGamePos = gridPositions[endGamePosIndex];
        gridPositions.RemoveAt(endGamePosIndex);
    
        // create a temp list using the weights and random index on this one
        var enemyPrefabs = new List<GameObject>();
        foreach(var item in weightedEnemyPrefabs)
        {
            for(var i = 0; i < item.Weight; i++)
            {
                enemyPrefabs.Add(item.Prefab);
            }
        }
    
        var landscape = new GameObject("ENEMIES");
    
        // Do these only once
        // store the references in case you need them later
        var player = Instantiate(playerPrefab, new Vector3(payerPos.x * 10, 0, playerPos.y * 10), Quaternion.identity /*, landscape.transform*/);
        var endGame = Instantiate(endGamePrefab, new Vector3(endGamePos.x * 10, 0, endGamePos.y * 10), Quaternion.identity /*, landscape.transform*/);
    
        for (int z = 0; z < zMax; z++)
        {
            for (int x = 0; x < xMax; x++)
            {
                // Now simply ignore the playerPos and endGamePos
                if(x == playerPos.x && z == playerPos.y) continue;
                if(x == endGamePos.x && z == endGamePos.y) continue;
    
                // pick a random index from the prefab list
                int randIndex = Random.Range(0, eneymPrefabs.Count);
    
                // and get the according random prefab
                var enemyPrefab = enemyPrefabs[randIndex];
    
                // do nothing if enemyPrefab is null otherwise instantiate
                if(enemyPrefab) Instantiate(enemyPrefab, new Vector3(x * 10, 0, z * 10), Quaternion.identity /*, landscape.transform*/);
            }
        }
    }