Lucky Limbo is a deckbuilding FPS game where the player has to survive waves of enemies to beat the game. As the sole AI programmer I have spent many hours working on the waves system and this is a description of that process, where it started and where it is now.
Where it started
After months of development and an influx of new people to the team, the idea for Lucky Limbo changed from a roguelike with hand crafted enemy encounters to a wave based survival game, taking inspiration from games like COD zombies, Warhammer Vermintide, Left for Dead, etc. This change meant that a wave system needed to be created.
First Attempt
The first version of the wave system tried to reuse much of the work that had already been done, only creating a new enemy manager object to make waves. Each wave was a list of enemy types and it would use the existing spawn points in the level to place the enemies. Waves would also only spawn when there were no alive enemies currently in the level. While this system was simple, helping get it in the project quickly, it didn't provide the experience the team was hoping for. Having all the enemies spawn at once felt very overwhelming and it wasn't easy to change what enemies were in each wave. Another issue occurred when players realized that they could leave one enemy alive and start to explore the level without worrying about combat.
Making improvements
It was clear after the first attempt that the idea for waves needed to be changed significantly. The first step was to meet with the level designer and systems designer to get an idea of what they needed from the system. Two main ideas came from this meeting, having a new object to manage the different rooms in the level and using spreadsheets to contain information about waves. The room manager made it possible easy to use only the spawn points around the player for each wave and by creating a way to read wave information from a spreadsheet it was easy to change and iterate on what enemy types were in each wave. One big problem still happened with this system though, having waves only spawn when there were no enemies in the level. This slowed the pace of the game down near the end of waves as the player tried to find where the final enemies were, something that could be frustrating when enemies got stuck in parts of the level.
A new approach
While I was making the previous changes to the system, another programmer on the team made a prototype of the system where new waves spawned on a timer instead of waiting for no enemies to be in the level. This approach helped address the pace problem and encouraged the player to engage in combat with the enemies instead of running away, or else they would eventually be overwhelmed. Working with these ideas, I created a version of the wave system that both used a timer and waited until the player had kill a certain number of enemies. This meant that the pace of the timer was used while trying to stop the player from being overwhelmed if they weren't killing enemies fast enough.
Features!
Once there was a system that felt good enough much more of my focus was spent on increased the amount of control someone could have over a wave. This meant introducing optional randomness to the types of enemies that were in a wave, an easier workflow for the level designer when adding spawn points to rooms, and adding timers to stop all the enemies for a wave spawning at once. I was also able to solve problems where enemies would get stuck or the player would run away from them to make sure the player couldn't just avoid combat.
Feedback
Despite all of the improvements made to the system it still had problems. While it made a much better experience then the first version, it any interesting moments and each encounter just felt the same as any other. These problems primarily came from having added so many random elements, spawn points and timers between individual enemy spawns being the biggest problems.
Bursts
After taking some time to think about these problems the idea of using bursts of enemies seemed like the obvious choice. Each wave would be made up of bursts of enemies who would all spawn at the same time. Switching to burst required a large refactor on the whole wave system to incorporate the new idea. Waves now would be responsible for managing bursts and total number of enemies while bursts would primarily manage the spawning of enemies. Each burst also contained a priority, which allowed intros for enemy types the player hadn't seen before to play at the start of a wave.
Directional Spawning
The final piece of the feedback to be addressed was how the random spawn points prevented interesting moments from happing in the game because enemies would be attacking from every direction. Directional spawning was the idea to solve this problem, where each burst would spawn from a specific direction relative to the player.
When a burst is told to spawn it first has to decide what the "active" direction is. The "active" direction is determined by finding which direction, relative to the player, has the most enemies. Each burst contains a preferred direction it wants to spawn in and using the "active" direction it can spawn to the side or from behind where the player's attention is directed. This system allowed the bursts to create more interesting moments for the player where they would be focused on fighting enemies from one direction only to be attacking from a side.
Comments