You are probably very familiar with the Actor Model as a game designer. It is the structural idea behind most of the game code I have seen, though this is slowly changing.
The Actor Model is, unsurprisingly, a structure based on the concept of Actors. An Actor is an object which contains both data, referred to as properties, and logic, referred to as methods. It is an intuitive idea when it comes to building simulation-oriented applications such as video games. We think of each “thing” in the game as a self-contained Actor which can be acted upon externally via methods to manipulate their state. For example, in the game Asteroids, we could think of the game this way: the ship is an Actor, the bullets the ship fires are Actors, the asteroids are Actors, and so on.
Unfortunately, things aren’t quite this simple when it comes to more complex games.
As programmers we want to re-use code as much as possible. Every bit of duplication is an opportunity for bugs to lurk in our program. Actor Model code accomplishes re-use with a concept called inheritance. With inheritance, classes can be partially based on other classes. Maybe a Ball class has a position and a velocity and a bounciness property. A BouncyBall would inherit from Ball and have a greater value in its bounciness property. Simple enough, right?
But we soon run into problems. In game development we often wish to mix-and-match behaviors. Suppose I have an Actor where it would make sense to inherit from two different classes. Now… we are hosed! Why? If two parent classes have a property or a method with the same name, now our child object has no idea what to do. Most object-oriented systems, in fact, forbid multiple inheritance, and the ones that don’t forbid it require very complex definitions to make it work. So we end up having to share code via helper functions, or giant manager classes, or other awkward patterns.
We also run into an issue called tight coupling. Actors that reference each other’s properties or methods directly become a problem when we change the structure in any way. If we modify the structure of Actor B, and Actor A references Actor B, then we have to also modify Actor A. In a particularly poorly structured system, we might have to modify a dozen objects just to make a slight modification to the behavior of a single object.
Tight coupling is our worst nightmare as game programmers. Games are, by nature, extremely complex simulations. The more coupling we have between objects, the more of the entire environment of the game we have to understand before we can pick apart the specific behavior of a single object, let alone the whole game itself. It is very possible that we can surprise ourselves by unexpectedly changing the behavior of different objects when modifying a single object. And we hate surprises.
We want our architecture to encourage us to create as little coupling as possible between different elements of the game, so that we can look at any individual element of the game and easily understand how it behaves without needing to deeply understand the other elements of the game. We also wish to modify behavior without introducing unexpected changes in other seemingly unrelated objects. The Actor Model might work in very trivial games or specific encapsulated cases, but it is not a satisfactory structure for implementing complex game behavior.
It turns out there is an architecture that addresses our desire to be able to compose objects out of several different behaviors. Let’s learn about it.