From the perspective of difficulty progression, I had two objectives with
Bloodjak II (which is out now on itch.io and Game Jolt). The game had to be infinitely playable while also never ceasing to become more difficult.
Practically, this is due to the game's nature as a single score attack game type - the game cannot have a score ceiling or end, but the game's difficulty must, at some point, be able to overcome the player's skill. If there were a score ceiling, then the high score competition would end as soon as somebody hit it. If the game's difficulty plateau'd, then a player of a certain skill level would be able to play indefinitely.
The difficulty of pursuing both of these objectives simultaneously is that the game must always be fair. This is, of course, almost always an essential rule of thumb for game design, especially for a skill based game. The game's difficulty must constantly escalate, but never to the extent that it becomes impossible. Not only does this introduce a skill ceiling, but it also makes the game needlessly frustrating. Part of what makes the
Bloodjak games work is that player death is always the result of player error. That is what keeps people making run after run after run.
Bloodjak the first technically accomplished both of these goals, but it had other issues. Not only were waves of enemies generally homogenous (they usually consisted of a collection of the same enemy type), but there was a lot of dead time in-between waves, and enemies also had a tendency to trickle it one at a time. I borrowed a lot of code and concepts from the original when making the sequel, but the enemy spawn algorithm was something that needed to be rewritten from scratch.
Bloodjak II's Spawn Algorithm - The First Iteration
|
Enemies spawn in waves at gradually shortening intervals |
Every set number of seconds, a wave of enemies spawns. If there are no enemies on the screen, then the next wave spawns within a half second to reduce dead time. Waves spawn more quickly over time.
However, the player does need a breather every once in a while. Every group of waves is collected into a group of waves, or a meta-wave. Each meta-wave consists of at least 6 waves, and on average gets longer as the game progresses. There are an extra few seconds of dead time in between one meta-wave finishing and another beginning that are unskippable. The game's background also has a chance of changing during this time to better punctuate the end of the sequence.
After certain waves, new enemy types are introduced, and the maximum number of enemies that can spawn increases. The chances of more difficult enemies and larger waves to spawn increases over time.
This overall served as a solid foundation from which I was able to build the rest of the game, but it produced some immediate problems:
- There was no inverse correlation between how many enemies spawn and how powerful those enemies are. At times, you would be as likely to spawn a pair of small "fighters" as you would six large "destroyers."
- Enemy combinations would be produced that were excessively difficult. If they are arranged on the screen in just the right way, it may be impossible to survive when ten "heavy" ships clutter the screen with diagonal shots..
- Enemy combinations that were otherwise surmountable would suddenly fill the screen with lasers in ways that were nearly impossible to dodge. Firing patterns were often synchronized within large groups of enemies.
- The early game would sometimes lack variety, even if it were hypothetically possible for the game to produce a wide variety of encounters. If an enemy's first appearance can be as early as wave 14, they still may not appear until wave 30.
Imposing Hard Limits
|
Some hypothetical enemy combinations are needlessly difficult |
An obvious solution to the first problem would have been to assign a difficulty value to each enemy type, impose a total limit on the overall game difficulty, and increase that difficulty limit over time. For example, you could decide that a fighter is worth 1 point while a destroyer is worth 10. The difficulty limit would start out at 5, meaning that the most difficulty encounter would consist of 5 fighters. When it increases to 10, anywhere up to 10 fighters could spawn, or a single destroyer. When it increases to 20, two destroyers could spawn. This prevents the problem I had where it was hypothetically possible for 10 destroyers to spawn in a single moment.
The problem with this system is that it wasn't compatible with the system I already had in place. As it stood, I already had a hard limit on the number of enemies that could spawn at a time. Combining this with a point based difficulty system above did not produce the desired results - enemy waves just got blander.
Instead, whenever a wave spawns, the game counts each enemy that spawns within it. Only the first two ships of a wave has a chance of becoming a powerful enemy like a destroyer. The first four or so can become intermediate enemies like heavies. Any enemy has the chance of becoming a "popcorn" enemy like a fighter or a bomb - the screen can flood with them without any serious consequence.
This mostly produced the desired effects. Small waves were more likely to consist primarily of larger foes. Larger waves were more likely to consist mostly of smaller ships, but may contain a managable number of more powerful enemies within them.
Furthermore, certain hard limits on the total number of enemies of a type had to be imposed. The game is manageable with a single heavy on the screen, tricky with two, difficult with three, and often a nightmare with four. Generally speaking, only three heavies will be on the screen at a time.
However, I had to be very careful when imposing hard limits on the difficulty of enemy spawns - if I impose too many limits, it would create a difficulty ceiling, which, as stated, needs to be avoided! Thankfully, enough difficulty parameters were able to increase indefinitely that I was able to maintain my objective of infinitely progressing difficulty while imposing a handful of limits.
Making the Impossible, Possible
Regarding the third problem, one solution would have been to further limit the number of enemies that appeared on the screen at once.
That would be a boring solution for a boring game. An important aspect of the game is to make the player feel as though they must, and are able to, overcome great odds.
|
Enemies will often "take turns" firing |
I did a number of things to preserve a high enemy count while not making it a death sentence. The first was that I put a limit on the number of enemy projectiles on the screen. If that limit were breached, then any ships present on the screen would have to wait to fire again until the screen clears of projectiles.
But doesn't that make the difficulty created by flooding the game with enemies illusory? No. A screen with twice as many enemies will still mean that the player is bombarded with twice as many projectiles - it's just that the player is attacked more frequently instead of being attacked all at once.
Furthermore, remember how the game counts each enemy that spawns in a wave? Enemies that are counted first will fire before those that are counted later. Enemy 1 attacks first, then 2, then 3. The result is that firing patterns are more staggered. Not only does this make individual waves of attacks easier to manage, but it also means that the player is spending more time dodging lasers.
Not only do both of these solutions make unwinnable situations winnable, but they also, counterintuitively, make the game
more intense. There is less dead time between volleys of attacks, meaning that the player is spending more time responding to fire and less time waiting.
A Designer's Touch
The fourth problem is one that is hard to solve with procedural generation. While all of the problems are caused by randomness, that the early game is occasionally dull is a problem that is more inherent to procedural generation itself.
When a game randomly generates content, there is a range of possibilities. In the early game, it is necessary that some of those possibilities are relatively uninteresting (oh no! a single enemy fighter spawned!). The problem is that, sometimes, the game
only generates bland situations. Sometimes, you only roll ones.
There are ways to change the spawn algorithm to correct for this, but not anything that I could achieve during the last week of a brief, two month development cycle.
|
The game's first four waves |
So, there are a handful of waves in the game that are predetermined. The first wave is always a fighter. The second wave is always two or three. The third is always a heavy (the earliest moment a heavy could've otherwise been spawned randomly). The random generation kicks in on wave 4, and the rest of the predetermined waves are scattered throughout.
It's not an ideal solution for a game where each run is supposed to be unique, but the predetermined waves are rare enough and feature enough random variation within themselves they are mostly invisible, and when they are visible, they are justified.
Conclusion
How did I do overall? Improvements could always be made, but not only did I succeed in achieving my most critical design goals (achieving theoretically infinite playtime with theoretically infinite difficulty), but I did so in a way that improved upon the difficulty algorithm of the last game. Compared to the original,
Bloodjak II is more varied. The player spends less time waiting for enemies to spawn or do something, and more foes can spawn at a time.
If you want to evaluate my work as a designer for yourself, feel free to get Bloodjak II from either
itch.io or
Game Jolt. Thanks!