When it comes to programming Why Am I Dead At Sea, there is one feature that sticks out above all the others in terms of time investment and difficulty. It has ruined many a day with debugging. It has at times leeched me of all enthusiasm to get work done. It haunts my very dreams (well okay, maybe it’s not that bad).
It is, of course, AI.
It’s not that I regret including it – I still think it’s almost a necessity for the game, and despite how much time it has wasted I’m sure it’s been a worthwhile investment. However, I can confidently say that I would have designed other things in the game differently if I had been able to predict how much they would complicate the AI.
But those concerns are buried deep in the game’s systems. Let’s start with a bird’s eye view of the game’s AI, and work our way down.
As you might imagine, one object being inside another symbolizes encapsulation – that is, they are literally an instance of that class contained within the other class. The lines next to Behavior symbolize the classes that inherit from it (Behavior being an abstract class).
Not surprisingly, the Character class is at the top – it is one of the game’s linchpin classes. It contains a CharacterAI class, which contains and handles everything pertaining to that given character’s AI. In turn, CharacterAI contains a Behavior object and a CharacterCommand object. The former deals with the higher level concerns of getting the character to move around – this generally means interpreting and/or setting waypoints, and then using those to create CharacterCommands. The latter goes deeper into exactly how to get the character to move – it takes in waypoints and generates a path with the Pathfinder class, then interprets that information to figure out when the character should move, and in which direction.
To hit everything I’d like to talk about will take a lot of time and space, so I’ve decided to split this subject into multiple blog posts, possibly as many as three. We’ll begin with an overview of the CharacterBehavior class and its children.
With the main aim of creating human-ish walk patterns, I have three overall behaviors that are used in the game, ranging from simpler to more complex.
Quite simply, under this behavior the character moves from tile to tile completely randomly. Aside from allowing the option to set boundaries that will stop the character from wandering too far, there isn’t any other higher level of thought outside of the path-finding.
Probably 90% of the RPGs that I’ve seen or played , especially of the 2D variety, seemed to exclusively have randomly meandering NPCs. It’s simple, but effective in breathing a bit of life into the background.
Sometimes I want the character to have a bit more direction, but I still don’t need a particularly detailed behavior; in this case, there is the pacing behavior, which creates paths based on whether to go horizontal or vertical, and how far in each direction. It is also good at demonstrating when a character is brooding, thinking or panicking, based on frequency and distance.
It might be worth noting that the behavior simply thinks of reaching the end-points in the pacing path, rather than explicitly in walking in a straight line. This means that if there is a pacing behavior in a room with obstacles in the way, the character will still move around them rather than exclusively walk in a straight line.
And lastly is the most versatile and communicative behavior, which is used (as the name implies) for when a character has a routine of some sort. The behavior takes in an arbitrary number of way-points and information about them, and the character is then made to move through them in a certain order.
This has been the most commonly used mode since it can get a lot of meanings across. On the smaller side, it could simply be getting across the idea of a single action: for instance, the cook has a routine where he walks from one kitchen appliance to the other. On the larger side, it could mean a daily routine that takes quite a long time to complete: for instance, the first mate has a large circuit across most of the ship in which you can see him checking up on everyone.
All of these behaviors are created before the game actually runs, and are stored in a separate place called the BehaviorLibrary. This is generally done simply with Tiled; I put an AI object right on the map where I want the waypoint to be, then give it properties. When the game loads the map file it also parses these AI waypoints and creates Behavior instances based on them.
When they are required in game, the CharacterAI sets its current behavior to one of them. Every tick in the game, CharacterAI queries Behavior to see if the character needs to go somewhere new – if it does, a new CharacterCommand is created with information about the new destination.
There isn’t a huge amount of gritty detail in this post, since the Behavior classes themselves don’t care too much about the details. When we get into CharacterCommand and Pathfinder, that will be different!