Tutorial 2: Taking advantage of OOP in agents

The scenario

So I just described the behaviour of a soldier in a tree. I would like to be able to keep this behaviour centralised in that behaviour tree, but some of my soldiers need to express some of their actions slightly differently.

I tried to see if I could create parameters which would describe the differences, but I quickly realised that enforcing this idea on a solution would only lead to a world of pain.

Another idea would be to have a variable specifying which type of soldier I was dealing with and then have a switch statement check the type at the relevant action implementation – reacting accordingly. This, however, would mean that my agent class would not only contain the general action implementations, but also it would include extra action implementations for each action handled differently by a certain agent using the same tree.

Combining behaviour trees and OOP

Ok so those solutions pretty much sucked. At the beginning I would probably be pretty happy with them, but as my characters received more and more actions and special cases, the code would become huge and incredibly hard to manage.

But hang on a minute. We’ve got an agent unit which needs to be somewhat like another agent unit, but re-implement some parts of the original agent unit. That’s called inheritance and overriding! Your two best friends from OOP!

So say I create a base agent class called "Soldier" and then another class "Commando" with the "Soldier" class as parent. This would mean that if I simply define the "Commando" class and use it, it would act exactly like the "Soldier" class.

public class Soldier : MonoBehaviour, Agent
{
// …
        public BehaveResult TickAction( int action, Tree sender, Object data )
        {
                switch( ( BLWargame.Actions )action )
                {
                        case BLWargame.Actions.ActionOne:
                                Debug.Log( "ActionOne" );
                        return BehaveResult.Success;
                        case BLWargame.Actions.ActionTwo:
                                Debug.Log( "ActionTwo" );
                        return BehaveResult.Success;
                        case BLWargame.Actions.ActionThree:
                                Debug.Log( "ActionThree" );
                        return BehaveResult.Success;
                        case BLWargame.Actions.ActionFour:
                                Debug.Log( "ActionFour" );
                        return BehaveResult.Success;
                }

                Debug.LogError( "Unknown action ID: " + action );
                return BehaveResult.Failure;
        }
// …
}

public class Commando : Soldier
{}

Action overriding

As you see I’ve got four actions implemented by my Soldier class. Now I would like my actions ActionOne and ActionFour to be implemented differently for my Commando agents without having to re-implement the other actions.

Simply overriding the TickAction method would hook the Commando into the action handling of the Soldier, but to only override some actions, I’d need to forward non-overridden actions to the Soldier base class. Using the base keyword, though, this is easily achieved.

The code below shows how the initial problem is solved while maintaining simple, easy to read code and doing absolutely minimal typing. Yay for OOP!

public class Commando : Soldier
{
        new public BehaveResult TickAction( int action, Tree sender, Object data )
        {
                switch( ( BLWargame.Actions )action )
                {
                        case BLWargame.Actions.ActionOne:
                                Debug.Log( "Commando::ActionOne" );
                        return BehaveResult.Success;
                        case BLWargame.Actions.ActionFour:
                                Debug.Log( "Commando::ActionFour" );
                        return BehaveResult.Success;
                }
               
                return base.TickAction( action, sender, data );
        }
}