The component behavioral pattern allows you to add just the features to your objects that are needed and keep the features independent of each other.
The episode describes an example of how best to deal with an adventure game hero class. Instead of trying to make a particular game class, such as the hero, directly handle all the code needed to process input, detect collisions, update locations, and render images, a better solution is to put each of these operation into their own class.
The first step then is to separate these functional parts, or features, or domains, into their own classes. To bring the behavior back into the hero class, you could try to use inheritance but this leads to a rigid solution that’s defined at compile time. this episode explains how to accomplish this with components that are contained within the hero class. by doing this, the hero class becomes more generic and this same pattern can then apply to any game object.
When you decide to use composition to handle the various features, you’ll need to decide how the components will communicate with each other. One of the big goals of this design pattern is to keep the components independent of each other. How do you keep them independent but still allow communication?
You can use the game object class to mediate between the components. Either directly by storing the result of one component so it can be used by another component or by managing messages that can be sent from one component through the game object class and then to all the other components. The messaging approach is much more complicated and may not always be needed. Or maybe you can choose multiple solutions and have some components behaving one or multiple ways. common attributes that all game objects share can be updated directly. While less common attributes might be a good chance to use messages.
Listen to the full episode about this design pattern, or you can also read the full transcript below.
The basic description says that this pattern allows an object type to span multiple domains without coupling between the domains.
We’ve actually finished going through all the patterns in the Gang of Four book. There’s a really good book called Game Programming Patterns by Robert Nystrom that I recommend that documents several more patterns applicable to game development. This design pattern comes from this book.
In order to understand this pattern, we’re going to have to dig a bit into the adventure hero class. In episode 78, I explained the state design pattern and how it could change how the hero behaved based on some state that could change. We’ll consider two states in this episode, walking and climbing a ladder.
You also know about the game loop pattern from episode 83 and the update method pattern from episode 84. Let’s say we have a simple game loop that goes through input, update, and render operations in each frame. The game loop will also call into each game object instead of trying to do each of these three operations itself.
Our hero class then will have similar methods that the game loop can call when needed. But how will the hero class implement these methods?
A simple solution would look like this:
◦ The input method reads the keyboard or gamepad device to determine if a direction button is pressed. Or maybe the method pulls a command out of a queue if you’re following the command design pattern as described in episode 72. For this example, let’s say the up arrow is pressed to mean the hero should move forward. The input method then sets a velocity variable to show that the hero should be moving.
◦ When the update method is called, it looks at the velocity and changes the hero’s position. But it also needs to make sure the hero doesn’t walk into a wall which will stop the hero even though the velocity says the hero should be moving. If the hero does walk into a wall, maybe a sound needs to be played. Or maybe it isn’t a wall that the hero runs into but a ladder. This needs to change the mode of the hero from walking to ladder climbing.
◦ And when the render method is called, the current state of the hero needs to be looked at to determine how the hero should be drawn.
The problem with this design is all the interaction between input detection, collision detection, playing sounds, and drawing. It’s all in one class.
The first step then is to separate these functional parts, or features, into their own classes. You could try to use single inheritance for whenever an object needs multiple features. How would that look?
Let’s think about what things can go into a game. We’ve already looked at the hero. It needs all of these features. But not everything needs them all. It’s common in a game to have elements such as the grass that gently sways back and forth or a bush with all its leafy mass to be just images that are drawn but have no real interaction with anything else. In other words, while a bush might obstruct the hero’s view, the hero can just walk right through it. There’s no substance and no need for collision detection. And sometimes a game developer needs to put invisible collision detection into the game just to know when the hero enters some area. It doesn’t actually prevent the hero from entering. It’s sort of like a trigger that can cause something to happen in the game. And then there are actual elements that the hero can see and touch and interact with. These need to be rendered and detected for collisions. The ladder is a good example. No matter what class hierarchy you try to come up with that uses only single inheritance, you’re not going to be able to satisfy all the game objects with just the features they need.
You could use multiple inheritance and most descriptions write this option off as unworkable due to the dreaded diamond arrangement. This is when you have two or more parent classes that each have the same base class. It really does make things more complicated. It’s not completely unworkable. Just more complicated. A better approach would be to use small mix-in classes to avoid the dreaded diamond. This approach is good because each class that you inherit from has no other base class of its own. You’re just mixing each of these classes as needed. The nice thing about this approach is the new class becomes a specific type that the compiler can help you to coordinate. the bad thing is the structure is defined at compile time and you can’t change it dynamically at run time.
So instead of inheriting from the feature classes such as a sound component for playing sounds and a physics component for detecting collisions, a better approach will likely be to compose these. Composing and containing are usually terms that can be used interchangeably. You can listen to episodes 24 through 28 for more information about inheritance vs. containment.
Note that this is different than what the composite design pattern describes in episode 66. The composite pattern shows how to build arbitrarily complex structures through multiple levels of composition. We’re just talking about a single level of composition.
I’ll continue this explanation right after this message from our sponsor.
( Message from Sponsor )
What does the hero look like now with the concept of components in place? Well, first the hero class will need instance data members for each component. There’ll be an input component, a physics component, a sound component, and a graphics component. This is already helping to organize the code and separate these very different features into their own classes.
It means there’s going to be an extra level of indirection though. Whenever the hero’s input method is called, instead of performing the steps right there, the code will now call an input method in the input component instance. The same thing happens in the update method. The hero just calls the physics component to deal with collisions and movement.
We’ve made the code so standard now that it really could apply to other game objects and not just the hero. This is actually what a lot of game engines do. They organize game objects to be able to hold whatever components are needed for that particular instance. The hero can have one set of components while a bush has a different set of components. The components help determine the object’s behavior.
The original code in the input method was able to set the hero’s velocity. How will this new design accomplish this? Especially now that we’re not working with just a hero class type but a more general game object class. You have some options.
◦ One option is to pass the game object to the input component’s input method. This will allow the input component to call methods in the game object class to set the velocity. It couples the input component to the game object class though. This may not be so bad for certain common properties.
◦ Another option is to let the input component communicate directly with the physics component. You’ll need to connect the components somehow maybe when the input component is first constructed, it requires a reference to a physics component right away.
◦ Or you can go with a more complicated but much more flexible approach and design the game object to mediate between the components by managing messages. This is sort of a combination of the mediator pattern from episode 75 and the observer pattern from episode 77. The way it works is the input component sends a message to the game object, which is the hero in this case, that it should move forward. Then the game object mediates by sending the message to any other components that might be interested in receiving the message.
The same process happens in the update method. The hero calls the component’s update methods and the physics component can detect when the hero is up against a wall or a ladder. If it’s a ladder, then a message would be a good way to handle this and a mode component could switch the hero into ladder climbing mode instead of walking. This would affect how the hero gets drawn in the render phase.
Just remember that in software design, there’s no one right way to do things. You might even want to combine these approaches and have some components well known and directly update the game object or other components while others are more dynamic and send messages. Let’s say that the sound component is one of these dynamic components. Whenever the physics component detects a collision with another solid object, it can send a message instead of playing a sound directly. The sound component if it exists in the list of components for the game object can then receive the message and actually play a sound.
All of this is much better and more flexible than either putting all the code directly in the hero class or trying to inherit this behavior. The run-time flexibility gives you some interesting options too. You can swap components by replacing direct references to the components themselves with interfaces and then using different implementations of components. For example, maybe you want the hero to temporarily go into a ghost mode. Just replace the normal physics component with a different one that lets the hero avoid certain collisions.