I was talking briefly on Twitter the other day about how I was using polymorphism in C++ to handle different types of AI in Drifter and someone suggested I should make a blog post about it. As I’ve been wanting to post something new for a while, I figured why not!
I apologize in advance if this is not the Right Way to do this, but it works, and that’s the main thing. Also I reference C++ but any object-oriented language that supports sub-classing should be able to implement something similar.
Anyway in Drifter there will be NPC ships which are flying around doing any manner of things which could be anything from delivering goods, mining in an asteroid belt, to trying to hunt the player or even other NPCs! With so many potentially different behaviors it would be nice to make it easy to add new ones as well as make it easy to use any AI with any ship without changing vast quantities of code.
In Red Nova it was a bit of a mess with one AI class and each enemy type’s behavior described by a couple of functions in that class. It is messy and a pain in the ass to modify, and considering how much more expansive Drifter is I really wanted to do something better and easier to manage this time.
First of all, all small ships in Drifter are all controlled by a generic ShipObject class. This object tracks things like location, heading, weapons, shield strength, hull integrity and cargo and presents a number of “inputs” to the ship such as steering, throttle and firing control. In the case of the player ship these inputs are operated by the touch controls, whereas with NPCs we can point some code at the associated ship object and let it manipulate these inputs.
This is where the generic AI class comes in. Each ship object has a pointer to an AI object and during the game logic loop every ship has its “process” function called. In that function a similar “process” function in the AI object is called. This is defined as a virtual function in the super class, which means that all derived classes will implement their own “process” function which will describe the behavior of that type of AI. To that effect, the generic AI super class also has pointers to aspects of the game state that would interest a potential AI agent such as the state of the current star system, other ships, asteroid fields and space debris such as jettisoned cargo and floating ore.
So now whenever we add a new NPC into the current star system we merely pick one of the existing AI classes as appropriate, create a new instance of said and attach it to the ship object and leave it to its own devices (or give it some specific task). Another neat feature that emerges from this is that we can add an autopilot for the player’s ship using an AI module.
Finally, if for whatever reason I feel the need to replace the hard coded AI behavior with scripting this gives a very clean entry point where I can replace the AI objects with a scripting system which performs a similar task.