Balanced Spawning System
How to Spawn based on a distribution table
As stated in the title today we will create a spawning system based on a distribution table. This will allow us to spawn simple enemies more frequent and harder enemies less frequent. Same goes for the powerups.
We could just hard code all the chance something happens, but that will be difficult to adjust the values later on when balancing our game. This is why we use a distribution table. A distribution table will hold all values is which distribution they will occur.
Common 5, Rare 2, Epic 1
Our total of all values combined will be 5+2+1=8.
Next we roll an 8 sided dice. Then look at our table like below. So if the outcome is 6 we drop a rare item.
When we adjust the values like Common to 10, the distribution changes. The total will change and the random number range(dice sides) changes.
In our code we will add all the weights in the table and calculate the total and based on the total we will calculate the random number range.
Distribution Table Script
Only the script for the distributed spawning system of the powerups is displayed below. The same logic and nearly the same code is used for spawning of enemies.
The code for the variables are below. The last two variables are only SerializedFields for debug purposes.
[SerializeField] private float _minPosX, _maxPosX, _startPosY;
//power up config
[SerializeField] private float _minSpawnIntervalPowerup = 3f;
[SerializeField] private float _maxSpawnIntervalPowerup = 7f;
[SerializeField] private int _powerUpWeightTable;
[SerializeField] private GameObject powerUpsPrefabs;
[SerializeField] private int _total = 0;
[SerializeField] private int randomNumber;
Assign in Inspector
In the Inspector it looks like this after assigned the weight values and prefabs:
Calculation which Powerup to spawn
First we calculate the total of the weights with an foreach loop in Start().
foreach (var weight in _powerUpWeightTable)
_total += weight;
Next have a while loop to continuously spawn power ups with some random time between each spawn and a random position to spawn.
The distribution table based calculation starts from the comment :
//spawn random powerup
A random number is generated based on the total. Using a for loop we check if the generated number is smaller or equal to the 1st value of the table. If not (else) we subtract the value of from the random number. By repeating this till the value is equal or smaller then the current leftover random number we can figure out which powerup is linked to the value and spawn it.
The break is needed so the game doesn’t continue to subtract and find following powerups to spawn.
while (_stopSpawning == false)
//random spawn time
float spawnIntervalPowerup = Random.Range(_minSpawnIntervalPowerup, _maxSpawnIntervalPowerup);
yield return new WaitForSeconds(spawnIntervalPowerup);
//random spawn pos
Vector3 spawnPos = new Vector3(Random.Range(_minPosX, _maxPosX), _startPosY, 0);
//spawn random powerup
randomNumber = Random.Range(0, _total);
for (int i = 0; i < _powerUpWeightTable.Length; i++)
if (randomNumber <= _powerUpWeightTable[i])
Instantiate(powerUpsPrefabs[i], spawnPos, Quaternion.identity);
randomNumber -= _powerUpWeightTable[i];