Clean Code and Boss Battles

Programming, Unity

14 Aug, 2015

By Dave Ward

Like it? Share it!
Share on FacebookTweet about this on TwitterShare on Google+Share on TumblrPin on PinterestShare on LinkedInEmail this to someone

This post is definitely one for the coders. Other professions shy away now. My aim for this post is to put forward a simple method for writing maintainable, adaptable and tidy boss fights.

Picture a boss battle and you’ll most likely have an enemy that has a limited number of offensive moves and attacks with these moves in a pre determined pattern.

Barry

Barry the boss. Nice name for a dragon.

 

Lets kick this off by creating Barry the boss who can move, headbutt or breathe fire. Trying to write the quickest code would result in something like this:

enum State
{
	moving,
	headbutt,
	breathFire
}

State currentState;

Update()
{
	switch(currentState)
	{
		case State.moving:
		//Move the enemy
		if(movingComplete)
		{
			currentState = State.headbutt;
		}
		break;
		...
	}
}

Our boss has a list of states he can be in. We switch the state dependant on various conditions throughout the class and do some action dependant on the current state. This isn’t bad code yet but can get much worse when our states get more complicated. We’d have many member variables. Most of them only used in one or two of the states. Our class can balloon in size and become a bit of a nightmare to maintain. We need to look at better solutions.

The following is something I picked up from Game Programming Patterns (excellent book) and is used in Castle Invasion to implement the first boss.

The command pattern!

A little Wikipedia quote to brighten your day:

“…the command pattern is a behavioural design pattern in which an object is used to encapsulate all information needed to perform an action or trigger an event…”

What we aim for with this pattern is simply to move each state of our boss into its own nice neat little class.

We can abstract out information about what each of our states needs to do, leaving us with:

  • Constructor. Set up the command. Initialise variables.
  • Update. Called every frame the command is active.
  • Complete callback. Our command calls this when it’s happy with its work and wants to reliquish control.

For the moving state, this would be:

  • Initialise. Choose a target position we want our boss to move to.
  • Update. Every frame move our boss slightly closer to the target.
  • Complete callback. When the boss reaches their target this function is called.

Put into pseudocode we have:

abstract class BossCommandBase BossCommandBase
{
	public abstract void Update(float delta);
	public event CommandComplete;
}

class BossMoveCommand : BossCommandBase
{
	const int SPEED = 10; 
	Position targetPosition;

	public BossMoveCommand()
	{
		targetPosition = Random.position;
	}	
	
	public override void Update(float delta)
	{
		currentPosition += (SPEED * delta);
		if(currentPosition == targetPosition)
		{
			CommandComplete();
		}
	}
}

This has simply and cleanly moved all code to do with moving out of our cluttered boss class. Our boss can now just act as a parent and creator of commands.

BossCommandBase currentCommand;

public BarryTheBoss()
{
	currentCommand = new MoveCommand();
	//Set the local function to be called when the command tells us it
        // is complete
	currentCommand.CommandComplete += CommandComplete;
}

void Update(float delta)
{
	currentCommand.Update(delta);
}

void CommandComplete()
{
	//Choose and set the next command
}

Happy Barry, the clean code mascot.

Barry loves clean code. He’s weird like that.

 

And that’s pretty much that. You can add extra levels of sophistication by running more than one command at once (E.g. move and throw) or by having commands respond to external message (E.g. enemy is damaged so stops attacking). It’s really cool how one simple design pattern can declutter huge classes, making them easier to reason about and maintain. Remember this pattern when you’ve got a headache from a similar scenario, it’s saved me a few times.

Logo

See ya!

Like it? Share it!
Share on FacebookTweet about this on TwitterShare on Google+Share on TumblrPin on PinterestShare on LinkedInEmail this to someone

Comments

< Back to blog feed

Get the latest posts delivered to your inbox