Composite
What is it?
The Composite Pattern is a structural design pattern that lets you treat single objects and groups of objects in the same way. Its main idea is to hide the difference between one object and many objects from the code that uses them.
For example, in Unity, you might call TakeDamage() on a single Enemy, or on an EnemyGroup that contains several enemies. If it is a single enemy, it takes the damage directly. If it is a group, the damage call is passed down to all enemies inside that group.
The structure usually has three parts: a shared interface or abstract class, leaf objects that do the actual work, and composite objects that hold other objects. This creates a tree-like structure where a command given at the top can travel down to the smallest objects.
When is it used?
Composite is useful when individual objects and groups of objects need to behave in a similar way. A common example in game development is an army system. Giving an attack command to one soldier and giving an attack command to a squad can be handled through the same method.
In Unity, this pattern can be used for enemy groups, UI panels, menu systems, buff or skill systems, behavior trees, and parent-child object structures.
It is a good choice when your system naturally has a hierarchy and the same action should work on both single and grouped objects.
However, if you only need to loop through a few objects, a simple List<T> with a foreach loop is usually enough.
Animation
Code
namespace Patterns.Structural.Composite
{
public interface IDamageable
{
void TakeDamage(float amount);
}
}Advantages and Disadvantages
• Advantages:
- Keeps the calling code simple.
- Lets groups contain other groups.
• Disadvantages:
- It can become overcomplicated if used everywhere.
- Deep hierarchies can create performance issues.
- The shared interface must stay clean.
Tips
When using Composite in Unity, it is important to make good use of Unity's existing hierarchy system. Unity's Transform structure already works with a parent-child relationship, so in many cases it is better to build on top of that instead of recreating the whole hierarchy manually.
A shared interface can be used for common behavior. For example, an IDamageable interface can be implemented by both Enemy and EnemyGroup.
Instead of manually managing child lists in every case, GetComponentsInChildren<T>() can sometimes be useful. In larger scenes, these calls should be cached instead of being used every frame.
Be careful with update logic. If Unity's normal Update() method is already running, and a Composite object also manually calls update-like methods on its children, the same objects may be updated more than once.
Finally, make sure a group cannot accidentally add itself or one of its parent groups as a child. This can cause infinite loops when a command is passed through the hierarchy.