Enemies Worth Fighting: Devlog #5


This Again?

         With far too much free time on my hands, I'm going to make this post a lot longer than usual. I've talked about my very generalizable game design philosophies, but now I want to get into to nitty-gritty of Enchanterland and it's mechanics. This devlog in particular will double as a personal implementation plan for the Enemy AI, which is my next step now that I have a completed player character and something that resembles a level. 

         I've noticed that most indie games tend to take the easy way out when it comes to enemies. There's usually nothing wrong with this; the "AI" behind a goomba from Super Mario Bros is both very easy to implement and very effective in context. Developers of games centered around combat, however, don't have the same luxury of simple enemies like the goombas. I've mentioned before that Enchanterland is set in stone as a beat-em'-up style action platformer, so it's about time I talk about the things you'd  actually be beating up in the game. Enemies are very important and very difficult, so this essay will explain my process on designing them to be worth fighting. This will be in both a broader context and in the specific context of Enchanterland. Let's run it.


The Random Dilemma

         Let's say you're creating a dungeon crawler rouge-like. You've already added a huge selection of items to the pool, and every run-through of your game feels both unique and uniquely interesting. That's great! You've already implemented one of the main hooks of the rogue-like genre. Now it's time to move on to the obstacles the players face. It's here that you notice a big problem. If the enemies behave unpredictably, the game as a whole can start to lose its sense of predictability and cohesion entirely. The outcomes of runs start to get more and more out of the players control the more chaotic the dungeon inhabitants become. Then, of course, as you add predictability to the enemies, the game starts to become less interesting. All the fancy items you added start to become means of making the game easier instead of more fun, especially to skilled players. What's worse is that it seems nearly impossible to find the sweet-spot where your obstacles are interesting and varying, but not chaotic.

         Enter the Gungeon is a wonderful dungeon crawler rouge-like, but even it seems to fall into both of these traps in different parts of the game.  When it comes to the chaotic part, its difficult to deny that the game gets exponentially easier when the game gives you good items. It also gets exponentially harder when the game gives you bad items. A lot of the time, when I had successfully finished a run, I felt like I had only done so because the game had given me several rare items and a high-damage synergy.  On the other hand, the bosses are specifically designed to be entirely predictable. One of my friends who has played Enter the Gungeon quite a  bit has also noted several times that he has memorized the patterns of every boss, and has even come up with full-proof strategies to avoid damage from certain attacks. At that point, it might start becoming more of a chore to fight a boss than anything else. The items you're given certainly help in the boss but they're almost entirely unnecessary if you understand the boss patterns and have the sleight of hand to dodge their bullets. In a non rogue-like, this might be okay. But, in a game that you are meant to replay several times over, giving bosses obvious patterns will lead high-skilled players to boredom. Even if you aren't that good at dodging the onslaught of bullets in a Gungeon boss, you will still notice that there isn't much difference between fighting the Beholster the first time as opposed to fighting it the fiftieth time. The only thing that usually changes is how fast you drain its healthbar.

While the bosses in Enter the Gungeon are by no means easy, they rely on obvious and non-randomized attack patterns to work as they do.

         RNG, or "Random Number Generation", is a tool used in games to make them less predictable and often more replayable. It's what determines the items and room layouts in Enter the Gungeon, or what cards you draw in Slay the Spire. When a player can predict everything that is about to happen with 100% certainty, they are more-so doing chores than they are playing games. Unpredictability and the need for inductive reasoning is the only reason why games are fun, but too much unpredictably in certain contexts can make a player feel out of control. Sometimes, RNG can even simultaneously make a player feel like they are performing monotonous tasks while also making them feel out of control. RNG is notoriously difficult to balance, and its no wonder that it tends to get ridiculed by players when things don't go their way.

         Making interesting, variable enemies while understanding that RNG is a double-edged sword has felt futile to me for a long time. However, I think I've managed to do it once. Despite its extensive list of flaws and violations of the code of ethics, Thousand Millimeter Climb has enemies that I would say strike this sort of balance between chaos and determinism. Wasps will appear somewhere along the top of the screen and move around sporadically, but they also move slowly and smoothly, and tend to stay in relatively the same section of the screen before they attack. Then, after moving around for a bit, they will always charge directly towards the player. After they do this for the first time, their attack rate will go up a little bit, but they will still behave in this exact way until they die. Spiders are even more predictable than the wasps. They will again spawn from the top of the screen, but they will then climb a random distance down their webs at a constant speed. After they reach their target distance, they will stop entirely and start  firing bullets at a set rhythm. Finally, groups of enemies will always spawn at a fixed interval, no matter how high up you are in-game.



This is honestly how I felt writing this section, but I do think it's a good example. Besides, it would feel like a disservice to myself not to include it.

         These systems are extremely simple, so how could they possibly fix the problem? To me, the original problem stems from there being different kinds of unpredictability in games. This seems crazy, because you either use a random number generator or you don't, right? Well, sort of. The context in which you use them, however, matters a lot. Specifically, what matters is how much information the player has access to at the point where a random number generator is used. According to Game Maker's Toolkit, randomness can be divided into two categories: Input Randomness and Output Randomness. Input randomness occurs when the player is given access to information about a random event before they have to make a decision based on that information. Take the act of drawing cards in a card game: you draw at the start of your turn in nearly every card game every made. The exact card you drew is based on an element of unpredictability, but the decision you make after that draw is an informed one. Output Randomness occurs when you make a decision that has randomized consequences afterwards. An example of this is rolling a twenty-sided-dice in Dungeons and Dragons. You make the decision first, and then your DM tells you how your character just died from it. This is how contexts of RNG have been typically differentiated by developers for a long time.

         The locations and types of the enemies that spawn in Thousand Millimeter Climb are examples of variables determined by input randomness. However, there is a lot of output randomness in the game as well, which should theoretically be causing a lot of problems. It does sometimes for sure, but this is where I would like to differentiate between two different types of output randomness. Think about the obvious differences between rolling a six-sided dice and rolling a dice when you have no idea how many sides there are on it. In one case, the player can make informed decisions based on their knowledge of risk and reward. In the other, they've been transported to Las Vegas and are wasting their time hoping for a jackpot that they don't even understand the chances of. The enemy attack patterns in Thousand Millimeter Climb are clearly determined by output randomness. However, the difference is that there's enough of a pattern in how they behave that the player can make guided inferences about their artificial intentions, and can still make informed decisions. After spawning in, a wasp will take anywhere between 3 and 5 seconds to attack. After attacking once, it will take anywhere between 1 and 3 seconds to attack. These are relatively tight ranges in practice, but it's enough for players to notice an implicit pattern without being completely sure of everything. 

         I'll call this type of randomness Informed Output Randomness. This type of randomness occurs whenever a player is suitably informed about the risks of a situation, and are able to make strategic cost-benefit analysis decisions accordingly. In other words, the player would have to be made explicitly or implicitly aware of the possible consequences of an action and their relative likelihoods. Input randomness allows players to make informed decisions based on their circumstances, while informed output randomness allows them to make informed decisions based on a fair understanding of risk. Informed output randomness typically deals with relatively high likelihoods, such as the one in six chance to roll any given number on a six-sided dice. This is because the more times you run a random number generator, the more its pattern starts to show itself: about one in every six dice will roll a three.

Slay the Spire doesn't pick a card at random for you: it gives you random cards for you to pick from.

         The opposite of informed output randomness would be Blind Output Randomness, which mimics the randomness of a highly secretive Las Vegas slot machine. This type of randomness obscures the predictability of an action in such a way that players cannot make many informed decisions based on the risks of that action; they just have to do it and hope it works out. This type of randomness is not always bad, but it is critical to control where it is present and to what extent. Notably, blind output randomness is often used when there's technically no risk involved in an action, such as opening a rewards chest. If there is a risk, especially a monetary one, that often becomes....... problematic. Blind output randomness necessarily deals with very low likelihoods. Since high likelihoods allow players to see patterns and make inferences about the chances of certain outcomes, low likelihoods are necessary to prevent the player from seeing these patterns. It would be essentially impossible to see a pattern on a six-thousand sided dice, even if you rolled it six-thousand times.

         The consequences of an action can still be determined by informed output randomness even if the player doesn't know the exact numbers, as long as they can get a good approximation through trial and error. Conversely, even though the exact numbers for the likelihoods of each chest rarity in Enter the Gungeon are available online, you can't make any informed decisions based on what chests you might get in a level because there is ultimately no pattern. Enter the Gungeon just makes the player roll with whatever they have, which gives that slot-machine feel to the chest rooms in each level.  There's no fine line between these types of randomness, and you ultimately have to observe them on a case-to-case basis to differentiate between them

         As you might have guessed, however, informed output randomness is my key to making enemies worth fighting. When someone you know is talking to you, you can likely make inferences about what they are going to say, but aren't able to predict their exact words. To me, the best enemies in combat-based games are the ones that have clear patterns, but don't behave like completely autonomous robots (even if that's what they are). I'm sure the bosses in Dark Souls come to mind for many. These bosses have distinct patterns, but they at least seem to offer some level of variability in their behavior. The informed output randomness they generate comes from their ability to both implicitly demonstrate their behavior to the player and make randomized choices. It's no wonder that the series ended up sparking an entire genre: the 'souls-like'. Balanced variability and predictability, combined with the input randomness used to encourage fast reaction times, make these bosses engaging obstacles.


Forgive me, but I still have to buy this game. I have no idea how they haven't taken away my indie-dev license for not owning it.

         Enchanterland enemies can't behave in the exact same ways as Dark Souls bosses or Thousand Millimeter Climb baddies, however. Keep in mind, while this is a beat-em-up game, it's also a platformer. Enemies need to move around in a two dimensional space and find ways to move around the physical obstacles inherent to the terrain. On top of this, their decisions need to be at least partially controlled by informed output randomness in order to have a sense of autonomy. This next section will cover the system I'm using to handle all of this.


Spatial Awareness

         The reason the AI for the enemies in Thousand Millimeter Climb can be summed up in only a few sentences is because the enemies in that game don't need to have much spatial awareness.  Neither of the variants have any sort of collision or interaction with the ground, so they don't need to take the constantly varying blocks into consideration. They only need to know the player location and nothing else. Suffice to say, this resembles the "goomba luxury" in the sense that the AI can be short of some typically necessary features while still serving their overall purpose just fine. Even a Dark Souls boss usually only has to take in a few external objects into consideration, since boss arenas in that series tend to be more open.

         Enchanterland does not have the goomba luxury, and neither do most games on the market. Enemies typically have to be "aware" of many objects in the environment besides just the player. When trying to consider how enemies might behave in a complex 2D platformer environment from the get-go, things start to seem too complicated to be possible. Luckily, I that's not what we can/should consider at the very beginning. As a mathematics student, I've been taught many times over that the trick to cracking a complex problem like this is to start with a Base Case. This term generically describes the simplest possible situation in an algorithm or formula. Imagine you're walking from your house to work, and we wanted to know how far you are away from work after zero seconds have passed since you left your house. Well... the answer is that the distance you are away from work must be same as the distance from your house to work. You couldn't have possibly travelled any distance towards work in zero seconds, so you still have the full distance walk ahead of you. We didn't even need any boring math to come to that conclusion, so we can consider the scenario of time passed being zero seconds as the base case. We can use that as a starting point to find the answers to slightly less obvious questions about this scenario, such as what your average speed was over the course of the trip.

         The concept of a base case is, unsurprisingly, used all the time in computer science and game development. Complex problems become easier to solve when you consider the simplest possible situations and work your way up from there. In the context of Enchanterland, the base case for enemy AI would probably be a completely flat environment with no elevation or pits. This would probably make for the worst platformer level of all time, but it does help us get started with enemy behavior, since it serves as that much needed base case. A single enemy in a flat environment would probably just try to book it towards the player depending on which value on the x-axis they are both on. It might also occasionally throw a jump or projectile into the mix in this state to keep the player guessing. When it gets close to the player, it could either attack directly or attempt to dodge an oncoming attack.

         Now, if there are multiple enemies in a flat environment, an enemy might be less prone to booking it towards the player if there's another enemy or two doing that already. The spatial environment around an enemy also includes other enemies. Accounting for this allows for groups of enemies to feel more coordinated and intelligent, but it also prevents the player from being swarmed with too many enemies at the same time. We've now moved past the base case and are building up from the foundation we've laid using it. When there's a pit between a charging enemy and the player, that enemy should check to see if it's a pit that can be jumped across when the enemy reaches it. If it is, you know the drill. If it isn't, the enemy probably can't get to the player and will likely just throw projectiles at them if applicable.

         And now for the grand finale (sort of): what does an enemy do in an environment with platforms, verticality, and pits? It is here that a lot of developers might use a navigation mesh that allows enemies to calculate paths around obstacles towards the player that they will follow to the best of their ability. This approach comes with a few problems, however. For one, it requires a lot of math. Of course, I'm all down for math, but when the computer has to do that math, it tends to struggle at least just as much as you would. Math takes a lot of computing power and tends to be inaccurate at times, at least when it comes to the more complex stuff. Not to mention that Godot's navigation mesh system is still only an experimental feature, so it might not be the best idea to use it for a full, long-term project. The better navigation system that I am likely going to use for this context is much simpler than the math behind navigation meshes. While the levels of Enchanterland will have verticality, they won't have that much verticality. I can divide a portion of a level into a few horizontal strips stacked on top of one another. These will be the "y-segments". If an enemy is trying to get to the player while they are on a different y-segment, the enemy should try to look for ways to get to the same y-segment as the player before launching a direct attack.  Just as enemies need to be aware of nearby pits, enemies could also be aware of nearby platforms that they could use to get to a higher y-segment. They could also use non-lethal pits or ledges to get to a lower y-segment as needed.

A demo location in Enchanterland with some verticality The lowest portion of this scene would be part of the lowest y-segment. Then, the scene could be divided into two other y-segments for the middle ground and the higher ground.

         This, of course, doesn't account for the differences between different kinds of enemies and other factors like smaller environmental obstacles. However, these cases would still likely involve systems built off of the one I just described. Analyzing the base case of a system that you are developing is vital to making that system function properly in the long-term. If your enemies don't work well in the perfectly flat environment, then I doubt they will work anywhere else. Another thing that might help with the design of complex systems is writing it all down piece-by-piece, sort of like how I did here. A diagram would work, but having it written out in English paragraphs through this devlog is what I figured would help me, even if it bores my readers. Sorry, not sorry.


Finally

         Input randomness, informed output randomness, and spatial awareness. These were the ingredients chosen to create the perfect enemies worth fighting. As you might have guessed, these things are still hard to implement in practice, and I almost certainly won't get things right the first time. That's why I'm hoping to post a very small demo on this project's page next week. It will be far from a fully functioning game on its own, but it will show the actual implementation of everything I've talked about on these devlogs. Well, except for the clean-coding one, but you'll just have to trust me there. In it, you'll have some respawning generic enemies to fight in a short demo level. Any and all feedback regarding the content of this demo will be appreciated. The best place to give that feedback would be in the comments of one of my devlogs, perhaps corresponding to the aspect you would like to criticize. The only issue with posting them on the main page is that new demos will more than likely be coming out afterwards, and old comments may quickly become outdated. With that, I leave you to the much more interesting thing you had going on with your day, and I will be back to post here in seven days. Deuces.

Get Enchanterland

Leave a comment

Log in with itch.io to leave a comment.