Skip to main content

Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
TagsGame Engines

A Balancing act between too random and too hard

Author: Matthew Chadwick

Date: 11/16/2023


While preparing for the Alpha build of CyberSiege, I set my sights upon the waves of enemies. Currently, the spawners would spawn simply a random amount of enemies, which would often overwhelm the player. This resulted in most players losing on the early waves, resulting in a negative playing experience. I tasked myself to rebalance the spawners to make them fair while also challenging the player. To do this, I relied on three factors that would determine the difficulty of any given wave: the wave number, the difficulty (if any) that has been chosen, and providing a random amount of enemies so each wave doesn't feel the same. Also, I wanted the spawners to be capable of using a premade wave instead of randomly generating a wave.

The struct used for holding enemy spawn data

To start, I created a struct that holds an individual enemy's spawning statistics. This struct contained a reference to the enemy class, the enemy's base, current, and peak spawn probability, as well as the wave at which the enemy is unlocked, the wave they are most likely to spawn in, and a variable in case I wanted the enemy's spawn rate to decrease after their peak wave. I mainly used this last variable for the base enemy since they should be the main enemy in the early waves, but be replaced by more advanced enemies later on. Before each wave is spawned, the new probabilities of each enemy are calculated. To summarize the equation used, it takes the enemy's current probability and checks how many waves are left until we reached the enemy's "peak" spawn wave. It then uses this information to incrementally increase the enemy's current spawn probability so that it eventually arrives at its peak spawn rate on the proper wave. A similar equation was created to handle decreasing the enemy's spawn probability back to its base rate if the enemy was designated to do so. This resulted in a smooth increase in enemy spawn rates, allowing for a psuedo-random enemy spawn that maintained balance and difficulty throughout the wave progression.

The functions used for increasing and decreasing probabilities


After the probabilities are calculated, the spawner manager would then start picking enemies to spawn based on their current probability. The chosen enemies were then added to an array, which is later used to spawn the enemies. The spawn list array works as a queue and follows a FIFO behavior. After the spawn list is empty and the last enemy is killed, the whole process starts again with a new calculation for the enemy spawn probabilities. If desired, a number of premade waves can be fed to the wave manager, which in turn, will use the premade list instead of calculating its own. The premade spawn lists work exactly the same as the generated spawn lists, but instead of the enemies being randomly selected, they are handpicked by whoever created the premade wave. 

The logic for choosing between premade waves and generated waves
The logic for determining whether to increase or decrease probabilties


The result was a robust wave manager that was capable of creating balanced waves of enemies based on various factors while also being able to use premade waves that were crafted by hand. 

Support this post

Did you like this post? Tell us

Leave a comment

Log in with your itch.io account to leave a comment.