Components and Composition

An introduction to the component-based design pattern, allowing us to create extremely complex objects in a simple, maintainable way
3D art of a female blacksmith character
Ryan McCombe
Published

In our lessons on Inheritance, we introduced a way of managing complexity. There, we arranged our code into a hierarchy where children inherited all the capabilities of their parent. This additive process created more and more powerful objects.

Inheritance is very useful and, by itself, it is sufficient for managing the complexity of fairly simple projects. At larger scales however, problems start to arise. Lets imagine some capabilities our actors might need to have:

  • The ability to deform through skeletal animation
  • The ability to create and respond to physical forces, like collision
  • The ability to emit particles, such as fire and smoke effects
  • The ability to emit audio
  • The ability to generate UI elements, such as health bars and name plates

We shouldn't put all this capability in a single class. Doing so would create an unmanagably complex class. Additionally, most individual objects will only need a tiny fraction of these capabilities. Creating unnecessarily complicated objects that contain a huge amount of unused data is a waste of resources.

However, equally, we can't effectively use inheritance here. These capabilities don't fit into any sort of hierarchy - objects can have any combination of these abilities. A tree blowing in the wind might need deformation, physics and audio; a campfire might need particles and audio; an enemy monster might need deformation, UI and audio.

Were we to define a class for every possible combination of these 5 capabilities, we'd have over 30 classes. Add a 6th capability, and that doubles to over 60 possible combinations. We need a better design.

Composition

Composition is the simple idea of combining objects to create more complex entities. Typically, objects that are designed to be combined with other objects are called components.

Under composition, no longer is our enemy monster a single object - it is a combination of components working together to simulate a single, more complex entity

[img]

The benefits of this are immediately obvious. Our entities can pick and choose only the capabilities they need. There is no need for elaborate class hierarchies.

Implementing Composition

To set this up, there are generally 3 aspects:

  • The class that creates objects that can have components - eg, Actor
  • The base class of those components - eg ActorComponent
  • A class for each component type - eg PhysicsComponent, which inherits from ActorComponent

The class structure might look something like this:

A UML diagram showing 6 classes

Here, the Actor and ActorComponent class are responsible for managing the interaction between actors and their components. Actors can maintain a list of all their attached components, as well as methods to allow components to be added and removed. Our ActorComponent base class provided similar methods, which give all of its child objects the ability to access and modify the actor they are attached to.

With this design, the focus of our Actor class shifts away from implementing behaviour directly. It can still do that, if needed, but most of its focus is on managing a collection of components.

For example, every frame, our game will call Tick on our actor, letting it invoke its behaviour. Given that behaviour is now implemented in the components it has attached, the actor needs to call Tick on every one of those components.

Similarly, when our actor is deleted, it needs to make sure all of its components are deleted too.

Hierarchies and Attachment Points

Component systems can get arbitrarily complex when creating intricate software. One common requirement is for components not just to be attached to an actor, but rather attached to a specific position in that actor.

For example, consider an actor that represents a torch. To create the fire effect, we might need a particle component. To ensure the fire effect is created in the correct place, that component needs attached to a particular position on the actor.

This can get more complex still - now imagine the torch actor is being carried by a character that is walking around. We're now dealing with a hierarchy of actors and components. We have a complete torch actor with its own set of components, and that torch actor is attached to a skeletal animation component of a totally different actor.

But, the concepts of composition allow us to implement these hierarchies quite easily. For example, we could have our SkeletonComponent maintain its own list of ActorComponents, along with a bone they're attached to. This would give us the ability to attach a ParticleComponent to the location of one of our actor's bones, allowing the effect to move realistically as our actor is animated:

class SkeletonComponent : public ActorComponent {
public:
  AttachToBone(ActorComponent* Component, Bone* AttachmentPoint);
private:
  list<pair<ActorComponent*, Bone*>> AttachedComponents;
}

Similarly, we could have an ActorComponent that is designed to contain another Actor, thereby allowing complete actors, not just components, to be attached to other actors.

Combined with the SkeletonComponents ability to have its own components, and for those components' positions to be kept in sync with animations, this would allow our actor to carry another actor in their hand:

class ActorAttachment : public ActorComponent {
public:
  AttachActor(Actor* ActorToAttach);
private:
  Actor* AttachedActor;
}

Was this post useful?

3D art showing a progammer setting up a development environment

Intro to C++ Programming

Become a software engineer with C++. Starting from the basics, we guide you step by step along the way

Free, unlimited access

This course includes:

  • 56 Lessons
  • Over 200 Quiz Questions
  • 95% Positive Reviews
  • Regularly Updated
  • Help and FAQ

Not Ready Yet?

Subscribe to this course to return to it later, and get updates on future changes

Get Started Now

Intro to C++ Programming

Starting from the fundamentals, become a C++ software engineer, step by step.

Contact|Privacy Policy|Terms of Use
Copyright © 2024 - All Rights Reserved