What is Software Architecture? It’s less about writing code than it is about organizing it. Good architecture makes a huge difference in productivity. But, like all things in life, it doesn’t come free. Good architecture takes real effort and discipline. Every time you make a change or implement a feature, you have to work hard to integrate it gracefully into the rest of the program. You have to think about which parts of the program should be decoupled and introduce abstractions at those points. If there is any method that eases these constraints, it’s simplicity.

Design Patterns Revisited

Command

A command is a reified method call. “Reify”, in case you’ve never heard it, means “make real”. Both terms mean taking some concept and turning it into a piece of data — an object — that you can stick in a variable, pass to a function, etc. So by saying the Command pattern is a “reified method call”, what I mean is that it’s a method call wrapped in an object.

That sounds a lot like a “callback”, “first-class function”, “function pointer”, “closure”, or “partially applied function” depending on which language you’re coming from, and indeed those are all in the same ballpark. The Gang of Four later says: Commands are an object-oriented replacement for callbacks.

Configuring Input

1
2
3
4
5
6
7
void InputHandler::handleInput()
{
  if (isPressed(BUTTON_X)) jump();
  else if (isPressed(BUTTON_Y)) fireGun();
  else if (isPressed(BUTTON_A)) swapWeapon();
  else if (isPressed(BUTTON_B)) lurchIneffectively();
}

This function typically gets called once per frame by the game loop. This code works if we’re willing to hardwire user inputs to game actions, but many games let the user configure how their buttons are mapped. To support that, we need to turn those direct calls to jump() and fireGun() into something that we can swap out. We define a base class that represents a triggerable game command:

1
2
3
4
5
6
class Command
{
public:
  virtual ~Command() {}
  virtual void execute() = 0;
};

Then we create subclasses for each of the different game actions:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class JumpCommand : public Command
{
public:
  virtual void execute() { jump(); }
};

class FireCommand : public Command
{
public:
  virtual void execute() { fireGun(); }
};

// You get the idea...

In our input handler, we store a pointer to a command for each button:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class InputHandler
{
public:
  void handleInput();

  // Methods to bind commands...

private:
  Command* buttonX_;
  Command* buttonY_;
  Command* buttonA_;
  Command* buttonB_;
};

Now the input handling just delegates to those:

1
2
3
4
5
6
7
void InputHandler::handleInput()
{
  if (isPressed(BUTTON_X)) buttonX_->execute();
  else if (isPressed(BUTTON_Y)) buttonY_->execute();
  else if (isPressed(BUTTON_A)) buttonA_->execute();
  else if (isPressed(BUTTON_B)) buttonB_->execute();
}

Where each input used to directly call a function, now there’s a layer of indirection. This is the Command pattern in a nutshell.

The only thing the JumpCommand can make jump is the player. Instead of calling functions that find the commanded object themselves, we’ll pass in the object that we want to order around:

1
2
3
4
5
6
class Command
{
public:
  virtual ~Command() {}
  virtual void execute(GameActor& actor) = 0;
};

Here, GameActor is our “game object” class that represents a character in the game world. We pass it in to execute() so that the derived command can invoke methods on an actor of our choice, like so:

1
2
3
4
5
6
7
8
class JumpCommand : public Command
{
public:
  virtual void execute(GameActor& actor)
  {
    actor.jump();
  }
};

We’re just missing a piece between the input handler and the command that takes the command and invokes it on the right object. First, we change handleInput() so that it returns commands:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
Command* InputHandler::handleInput()
{
  if (isPressed(BUTTON_X)) return buttonX_;
  if (isPressed(BUTTON_Y)) return buttonY_;
  if (isPressed(BUTTON_A)) return buttonA_;
  if (isPressed(BUTTON_B)) return buttonB_;

  // Nothing pressed, so do nothing.
  return NULL;
}

It can’t execute the command immediately since it doesn’t know what actor to pass in. Here’s where we take advantage of the fact that the command is a reified call — we can delay when the call is executed. Then, we need some code that takes that command and runs it on the actor representing the player. Something like:

1
2
3
4
5
Command* command = inputHandler.handleInput();
if (command)
{
  command->execute(actor);
}

Adding a layer of indirection between the command and the actor that performs it has given us a neat little ability: we can let the player control any actor in the game now by changing the actor we execute the commands on.

Undo and Redo

We’re conveniently already using commands to abstract input handling, so every move the player makes is already encapsulated in them. For example, moving a unit may look like:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
class MoveUnitCommand : public Command
{
public:
  MoveUnitCommand(Unit* unit, int x, int y)
  : unit_(unit),
    x_(x),
    y_(y)
  {}

  virtual void execute()
  {
    unit_->moveTo(x_, y_);
  }

private:
  Unit* unit_;
  int x_, y_;
};

Our earlier input handler held on to a single command object and called its execute() method anytime the right button was pressed. Here, the commands are more specific. They represent a thing that can be done at a specific point in time. This means that the input handling code will be creating an instance of this every time the player chooses a move. Something like:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
Command* handleInput()
{
  Unit* unit = getSelectedUnit();

  if (isPressed(BUTTON_UP)) {
    // Move the unit up one.
    int destY = unit->y() - 1;
    return new MoveUnitCommand(unit, unit->x(), destY);
  }

  if (isPressed(BUTTON_DOWN)) {
    // Move the unit down one.
    int destY = unit->y() + 1;
    return new MoveUnitCommand(unit, unit->x(), destY);
  }

  // Other moves...

  return NULL;
}

To make commands undoable, we define another operation each command class needs to implement:

1
2
3
4
5
6
7
class Command
{
public:
  virtual ~Command() {}
  virtual void execute() = 0;
  virtual void undo() = 0;
};

An undo() method reverses the game state changed by the corresponding execute() method. Here’s our previous move command with undo support:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class MoveUnitCommand : public Command
{
public:
  MoveUnitCommand(Unit* unit, int x, int y)
  : unit_(unit),
    xBefore_(0),
    yBefore_(0),
    x_(x),
    y_(y)
  {}

  virtual void execute()
  {
    // Remember the unit's position before the move
    // so we can restore it.
    xBefore_ = unit_->x();
    yBefore_ = unit_->y();

    unit_->moveTo(x_, y_);
  }

  virtual void undo()
  {
    unit_->moveTo(xBefore_, yBefore_);
  }

private:
  Unit* unit_;
  int xBefore_, yBefore_;
  int x_, y_;
};

When a unit moves, it forgets where it used to be. If we want to be able to undo that move, we have to remember the unit’s previous position ourselves. Supporting multiple levels of undo isn’t much harder. Instead of remembering the last command, we keep a list of commands and a reference to the “current” one. When the player executes a command, we append it to the list and point “current” at it.

When the player chooses “Undo”, we undo the current command and move the current pointer back. When they choose “Redo”, we advance the pointer and then execute that command. If they choose a new command after undoing some, everything in the list after the current command is discarded.

In some ways, the Command pattern is a way of emulating closures in languages that don’t have them. For example, if we were building a game in JavaScript, we could create a move unit command just like this:

1
2
3
4
5
6
function makeMoveUnitCommand(unit, x, y) {
  // This function here is the command object:
  return function() {
    unit.moveTo(x, y);
  }
}

We could add support for undo as well using a pair of closures:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
function makeMoveUnitCommand(unit, x, y) {
  var xBefore, yBefore;
  return {
    execute: function() {
      xBefore = unit.x();
      yBefore = unit.y();
      unit.moveTo(x, y);
    },
    undo: function() {
      unit.moveTo(xBefore, yBefore);
    }
  };
}

Flyweight

Forest for the Trees

We’re talking thousands of trees, each with detailed geometry containing thousands of polygons. Each tree has a bunch of bits associated with it:

  • A mesh of polygons that define the shape of the trunk, branches, and greenery.
  • Textures for the bark and leaves.
  • Its location and orientation in the forest.
  • Tuning parameters like size and tint so that each tree looks different.

If you were to sketch it out in code, you’d have something like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class Tree
{
private:
  Mesh mesh_;
  Texture bark_;
  Texture leaves_;
  Vector position_;
  double height_;
  double thickness_;
  Color barkTint_;
  Color leafTint_;
};

The key observation is that even though there may be thousands of trees in the forest, they mostly look similar. They will likely all use the same mesh and textures. That means most of the fields in these objects are the same between all of those instances. We can model that explicitly by splitting the object in half. First, we pull out the data that all trees have in common and move it into a separate class:

1
2
3
4
5
6
7
class TreeModel
{
private:
  Mesh mesh_;
  Texture bark_;
  Texture leaves_;
};

The game only needs a single one of these, since there’s no reason to have the same meshes and textures in memory a thousand times. Then, each instance of a tree in the world has a reference to that shared TreeModel. What remains in Tree is the state that is instance-specific:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class Tree
{
private:
  TreeModel* model_;

  Vector position_;
  double height_;
  double thickness_;
  Color barkTint_;
  Color leafTint_;
};

We provide two streams of data. The first is the blob of common data that will be rendered multiple times — the mesh and textures. The second is the list of instances and their parameters that will be used to vary that first chunk of data each time it’s drawn. With a single draw call, an entire forest grows.

The Flyweight Pattern separate out an object’s data into two kinds. The first kind of data is the stuff that’s not specific to a single instance of that object and can be shared across all of them. The Gang of Four calls this the intrinsic state. The rest of the data is the extrinsic state, the stuff that is unique to that instance.

A Place To Put Down Roots

We’ll make the ground tile-based: the surface of the world is a huge grid of tiny tiles. Each tile is covered in one kind of terrain. Each terrain type has a number of properties that affect gameplay:

  • A movement cost that determines how quickly players can move through it.
  • A flag for whether it’s a watery terrain that can be crossed by boats.
  • A texture used to render it.

A common approach is to use an enum for terrain types:

1
2
3
4
5
6
7
enum Terrain
{
  TERRAIN_GRASS,
  TERRAIN_HILL,
  TERRAIN_RIVER
  // Other terrains...
};

Then the world maintains a huge grid of those:

1
2
3
4
5
class World
{
private:
  Terrain tiles_[WIDTH][HEIGHT];
};

To actually get the useful data about a tile, we do something like:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
int World::getMovementCost(int x, int y)
{
  switch (tiles_[x][y])
  {
    case TERRAIN_GRASS: return 1;
    case TERRAIN_HILL:  return 3;
    case TERRAIN_RIVER: return 2;
      // Other terrains...
  }
}

bool World::isWater(int x, int y)
{
  switch (tiles_[x][y])
  {
    case TERRAIN_GRASS: return false;
    case TERRAIN_HILL:  return false;
    case TERRAIN_RIVER: return true;
      // Other terrains...
  }
}

I think of movement cost and wetness as data about a terrain, but here that’s embedded in code. Worse, the data for a single terrain type is smeared across a bunch of methods. It would be really nice to keep all of that encapsulated together. After all, that’s what objects are designed for. It would be great if we could have an actual terrain class, like: