Fps being all the rage nowadays, everyone wants to join the bandwagon and write the next money making gem. But whenever you want to take the easy path and make a multiplayer game, or a true believer and deliver a story based FPS single player adventure, you still need to be able to code down intelligent looking, computer controlled, opponents. Of course, programming being the art of having the computer doing most of the job, you might want some minimum effort – maximum result approach to the problem, and the answer is right here. So, no more words wasted, here’s how.
The whole gimmick is to give the AI a series of goals. These are objectives like guarding a post, patrol through a path, inspecting a suspecting noise and, of course, shooting opponents. Each single goal is easy to code by itself. The hard part comes when two goals go in conflict with each other. Say the AI has to follow a path (maybe it’s returning the flag), but also shoot an opponent. Simply coding them in a ‘one or the other’ fashion will result in the AI either ignoring the opponent (and ending up killed), or shooting him (thus ignoring the objective of the game).
Can’t it just do both?
To manage these situations, we define servos. A servo is a capability that any unit in the game has (you and the bot are no different, more on this later). In the scenario above, we’ll want a movement servo, allowing movement in a particular direction on the map, and a shoot servo. More tricky: torso twist servo. All FPS allows the player to freely move in any direction (wasd-wise), and look in any other direction (mouse-wise). So our bot in the scenario above will be using it’s movement servo to move along the map and return the flag, while using the torso servo to point itself at the enemy. Then the shoot servo to take care of him.
Each servo might or might not be available at a specific time. If the movement servo is being used to go in a direction it can’t be used to go in another. If the gun is reloading, the shoot servo can’t be used. To manage servo availability, each goal define the servos it needs (no need to try to execute the shoot-the-player goal if the torso servo isn’t available), and which goal it desires (if the shoot servo isn’t available the torso servo can still be of use to start pointing the gun).
Finally come activation conditions and weights. At each iteration of the game, the activation condition of all the goals are to be checked. These condition define when a goal can execute. As an example, the capture-the-flag goal can execute whenever the bot is not already holding the flag, and the flag is visible to it. The active goals are then put in a list, and the list ordered by goal weight, so that top priority goals are placed first.
Then come the easy part: the goal list is executed, top goals get the servos first. Each goal actually execute only if all of the servos it needs are available. Each time a servo is used, it’s marked as unavailable. This way, low priority goal only gets to execute when there is nothing more productive to do.
But how do the goals move the bots?
As written before, we don’t want the bots to behave anything differently from a player. Anything that a bot can do, the player can, and vice-versa. This include fancy stuff like driving vehicles and mounting turrets. So here’s the scenario: you’ve just coded the vehicle controls (say by keyboard or gamepad), and there’s no way you are going to double code that for the bots. What you want here are virtual keyboards.
A virtual keyboard is an array of boolean, each element representing a key (I’m talking about forward, back, shoot… not letters). So cars, turrets, everything down to bodies, is considered a vehicle, connected to a virtual keyboard, owned by a brain. The brain is either you, connected to your keyboard (ouch), or the AI, connected to a virtual one.
And thats it. These are the basis of any honest-to-god FPS engine.