Random enemy spawn chance, with a list of enemies that expands on the run

Hello fellow community members,

This might be a bit tricky to phrase…

So I’m working on a spawning system that continuously spawns enemies, 1 every second. While that’s easy, different types of enemies have different chances to spawn, and they don’t all start to spawn from the beginning. I’m a bit stuck on how I should do the random picker, based on percentage, while all chances combined don’t form 100%. Though it still always should spawn an enemy.

So let’s give an example

At first it only is able to spawn one type of enemy. I’ll call this the defaultEnemy. So this prefab starts with 100% chance to spawn basically, although this is not a set chance.

Once enough time has passed, more enemies are allowed to spawn. Let’s say Enemy1 has a 10% chance, Enemy2 has 8%. (The numbers are defined by a variable in their script/scriptableObject.) That means when those two are allowed to spawn, it has 10% chance to pick Enemy1, 8% chance to pick Enemy 2. If it picks neither, it should spawn the defaultEnemy. So that one now has 82% chance to spawn.

And so more enemies become available to spawn over time. So the chance it picks nothing (the defaultEnemy) varies, as it decreases every time another type becomes available to spawn.

So at the end it could look like this:
Enemy1: 10% (time > 10)
Enemy2: 8% (time >12)
Enemy3: 12% (time > 60)
Enemy4: 6% (time > 70)
Enemy5: 4% (time > 120)
Chance to pick nothing (= defaultEnemy) after time has passed 120 : 60%

One goal I want with the script is that I can easily add more enemies, without making changes to the script, just by adding more in the array/list that it can spawn in the Unity inspector. That may be the hardest part even. So it would have to do a loop for each enemy in the array… but still resolve the pick chance of all of them at once…

Your advise would greatly appreciated.

Edit: using C# :stuck_out_tongue:

Found this:

List<KeyValuePair<string, double>> elements = ...some data
Random r = new Random();
double diceRoll = r.NextDouble();

double cumulative = 0.0;
for (int i = 0; i < elements.Count; i++)
{
    cumulative += elements[i].Value;
    if (diceRoll < cumulative)
    {
        string selectedElement = elements[i].Key;
        break;
    }
}

from: C# Pick Random Elements Based on Probability - Visual C# Kicks

While that’s useful, it doesn’t work if your elements don’t add up to 100. But this gives me an idea…

Create an EnemySpawn class that holds the Enemy to spawn, the percent chance of spawning, and its spawn time.
Then create a list of these EnemySpawns, and access the relevant EnemySpawn data.

Here, I did a little of the work for you.
Now, lets see you do the rest! :stuck_out_tongue:

To use this, create a single C# script named “Spawner” and replace all the code in it, with the code below, then attach it to a gameobject.

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

[System.Serializable]
public class EnemySpawn {
    [SerializeField]
    private GameObject _enemyPFB;

    [SerializeField]
    private float _spawnChance;

    [SerializeField]
    private int _spawnTime;

    public GameObject EnemyPrefab
    {
        get {return _enemyPFB;}
    }

    public float SpawnChance
    {
        get {return _spawnChance;}
    }

    public int SpawnTime
    {
        get {return _spawnTime;}
    }


}


public class Spawner : MonoBehaviour {
    [SerializeField]
    public List<EnemySpawn> enemies;


}

Sorry I actually wrote this code yesterday, before I saw your posts :stuck_out_tongue:
It works as I want it to. At least for now. It actually wasn’t hard at all, but without that website, I wouldn’t have figured it out that easily.

public class SpawnWaveGenerator : MonoBehaviour {
   
    [System.Serializable]
    public class enemy {
       
        public string name;
        public GameObject prefab;
        public float chance;
        public float delayTime; //a delay before they can start spawning
       
    }

    public Transform[] points;
    public enemy[] enemies;
    public float spawnDelay = 1.0f; //affected by difficulty

    enemy chosenEnemy;
   
    float random;
   
    float cumulative;
    public string selectedEnemy; //debug

    void Start () {
   
        InvokeRepeating("SpawnRandom", 1f, 1f); //might be temp
    }
   
    void Update () {
        //some stuff will go here later
   
    }

    void SpawnRandom (){
       
        random = Random.value;
        cumulative = 0f;
       
        for (int i = 0; i < enemies.Length; i++)
        {
            cumulative += enemies[i].chance;
            if (random < cumulative && Time.time >= enemies[i].delayTime)
            {
                selectedEnemy = enemies[i].name;
                chosenEnemy = enemies[i];
                Debug.Log("picked " + selectedEnemy);
                break;
            }
        }
   
        Instantiate(chosenEnemy.prefab, points[Random.Range(0, points.Length)].position,points[Random.Range(0, points.Length)].rotation);
   
    }
}

The enemies and their chances can be assigned in the inspector :slight_smile:

What you must do is have the lowest chance enemy first, and then build up.
The default enemy, comes always last in the array, with a chance of 1. (100%) So that one gets picked if all others fail.

Thanks for you help, @TonanBora , even though I didn’t get to use it, I greatly appreciate every post from those willing to help. Thanks again. :slight_smile:

1 Like

Glad you figured it out without seeing my script! :slight_smile:

I’ve had a look at your code @Stonewood1612 It’s pretty good but I can’t seem to change the spawn time. If I change the delay, in the inspector mind you, it doesn’t seem to change it in game. I think I may have missed something so I’m going to keep going through it for now (see if my friends have any ideas). Any help is appreciated though. Thanks in advance

@Bradley_FDA
The variable spawnDelay isn’t going to impact anything because it’s not being used anywhere in the code he posted. Take a look at the InvokeRepeating method he is using and see what you might be able to change there.