Script Composition in Unity

How to use script composition in Unity

In Unity by John French8 Comments

As you begin building your game in Unity, you may find that deciding what each script should do can be surprisingly tricky.

For example, many objects in your game are likely to work in a similar way.

Both the player object and enemies are likely to use the same code to move around.

Different types of pickup, even if they offer different effects, will be collected by the player using the same mechanism.

And weapons, while each one will work in a different way, will all be equipped and used to cause damage using the same processes and functions.

Put simply, even in a simple project, parts of one script are likely to overlap with another.

So how can you share common functionality between scripts?

One option is to simply write out unique classes for every different type of object that you want to create.

However, using this approach will likely mean rewriting the same code over and over again, in different places, making it extremely difficult to manage.

One solution is to use inheritance, which allows an object to inherit core behavioural traits from a base class, making it easy to give different objects the same core behaviour based on their type.

However, while inheritance can be incredibly useful, not all objects will fit neatly into a hierarchy.

So what’s the answer?

How can you build up the functionality of the different objects in your game without writing the same code over and over and without forcing everything into a hierarchy?

One option is to use Script Composition, which is a design approach that involves building up an object’s behaviour using smaller, reusable parts, making your project easier to manage as a whole.

In this article, you’ll learn how composition works, how it differs from other methods, such as inheritance, and how to use the composition approach in your project.

Here’s what you’ll find on this page:

Let’s get started.

What is composition in Unity?

Script composition is a game architecture approach that involves splitting up the functionality of large, multi-purpose classes into smaller single-purpose scripts that are easier to add, remove or change.

Script Composition

Generally speaking, composition involves using multiple single-purpose scripts in place of a larger class.

It’s typically used as an alternative to inheritance and allows you to give objects functionality as if they’re abilities, such as the ability to be moved, to be collected, to be interacted with or the ability to take damage.

It’s also highly flexible, as it allows you to give any object a particular ability, simply by adding the right component to it.

Want to make a box breakable? Give it the ability to take damage.

Want to allow an object to be moved? Give it a moveable component.

Want to let the player drive a vehicle? Add an input component to it.

And, if you change your mind, simply take that component off again.

But how is this helpful?

How easy or difficult a project is to work with can often be measured by how easy it is to change.

Which is why the composition approach can be so useful, as it generally makes adding, removing or modifying part of an object’s behaviour much more simple.

Which makes your project easier to work with overall.

However…

Just like any tool, composition is good for some game architectures and less useful for others.

So, is composition right for your project, and how is it different to other design options, such as inheritance?

How is composition in Unity different to inheritance?

Inheritance works on the idea that a base class defines a core set of functionality, such as a human’s ability to walk around, while a class that inherits from that base includes all of the root functionality but then adds, or reinterprets, additional behaviour on top from their own class.

Visualisation of Inheritance in Unity

Inheritance allows subclasses to use the behaviour of their parents.

For example, an NPC and an enemy might both inherit from a Human class, giving both of them the ability to walk around, but their behaviour, such as how they respond to meeting you, will depend on their specific class. 

Their individual behaviour is unique, while their shared behaviour, in this case, their ability to move around like other human-based objects, is defined by the class that they both inherit from.

This can be useful as it allows you to build behaviour from the ground up and it can make a lot of sense when the objects that you create are a version of something else but share core behaviour that is always the same.

The composition method, however, allows you to create functionality by mixing and matching abilities.

For example, you can give an object the ability to move, to be damaged or to be destroyed simply by adding those components to it.

Which allows you to create behaviour in a very flexible way since, technically, you’ll be able to give any object any ability, without needing to organise it into a hierarchy first.

So is the composition method right for your project?

Should you use composition or inheritance in your project?

Generally speaking, inheritance works best for creating core behaviour that’s based on what an object is.

For example, the player is a human, an enemy is a human, and it makes sense for both the player and enemy classes to inherit the core functionality of being a human from their base class.

This works because it’s highly likely that all objects of the human type will share the ability to walk around.

However…

If you start to add more and more specific behaviour into a base class, inheritance can become tricky to work with.

A class can only inherit from one base class at a time. Which means that, if you try to put too much into it, you may start to run into a situation where not all of the objects that inherit from the base class can use its functionality.

For example, if you need different behaviour for different kinds of object, but you can’t easily divide your class into further subclasses to do it, you may find yourself in a situation where you need to check what an object is when inheriting.

In which case, inheritance may not be the best fit for that particular problem.

Alternatively, composition allows you to create reusable behaviour components that can be added to any object, regardless of what that object is.

Instead of being based on a particular type, composition describes what an object has or can do instead.

For example, an object that can take damage,

An object that can be selected,

An object that can be moved.

So, while composition and inheritance both solve a similar problem, how to share common functionality between different objects, they do it in different ways, and the option that’s most useful to you will depend on what it is you’re trying to make.

Generally speaking, inheritance is good for creating core behaviour that’s common to the type of object that inherits it.

However, composition is great for creating behaviour that doesn’t fit neatly into a hierarchy.

Or, if it works for your project, a mixture of inheritance and composition can be a powerful combination.

But, what does the composition method actually look like in practice?

How small should each script be?

And how will each of the components communicate with each other?

Here’s how it works…

How to use composition in Unity

The general idea of composition in Unity is to create small, individual scripts that each enable an object to do something or to have something done to it.

Which can help to avoid writing large multi-purpose classes, which can be difficult to maintain.

However, it’s not enough to simply take what would have been a large class and split it up into small pieces if each of those pieces are still dependent on each other to work.

Instead, it can be better to design each script as a self-contained component, much like Unity’s built-in components, that can be easily added or removed without affecting other systems.

But how do you do that?

First, you’ll need to decide how small each script should be…

How small should each script be?

When deciding how small a script should be, a good rule of thumb is that each script should only do one thing.

For example, a movement script shouldn’t also have to detect input.

A script that allows an object to take damage can be kept separate from the script that handles how much health it has.

And, a health script doesn’t need to concern itself with what happens when that object is destroyed.

Even though many of these systems are closely related, keeping them separate can make them much easier to work with.

For example, separating movement from input allows you to give the same movement functionality to an enemy object, except controlled by an AI.

Separating damage from health allows you to easily modify how much damage, if any, an object is able to take.

And keeping a death script separate from a health script allows you to easily change what happens when different types of object run out of health. For example, an enemy might be killed, dropping loot or experience, while an NPC might only be knocked out.

Limiting each script to a single task makes it much easier to combine functionality in a modular way.

But, it can sometimes be tricky to know where one script ends and another starts.

So, how can you decide what a script’s one task should be?

How to decide what each script should do

Exactly how small or large a script should be won’t always be clear.

However, when deciding if a particular kind of functionality needs to be one script or two, it can help to consider how easy it would be to change how that script works.

For example, imagine that you want to allow the player to click and drag to create a selection area in your game.

This might involve getting the mouse’s position in the world, detecting when the mouse button was held down, triggering a function for drawing the selection area and returning a reference to any objects that are inside it.

While you might be tempted to put all of this in one Object Selection class, realistically, this could be three different scripts.

  • One to listen for button clicks and translate them to world positions
  • One to measure the selection area and return anything that’s inside of it
  • And another to visually show the area that’s being selected

Visualisation of multiple scripts in Unity

So why use multiple different scripts to do what could be considered one thing?

The idea is to limit the impact of needing to change, remove or reuse a script.

For example,

The script that listens for mouse button clicks translates them into world positions for other scripts to use.

Which means that, if you wanted to change how the click and the position is detected, because you’re no longer using a mouse, for example, then you can do that easily, without breaking anything else.

Likewise, you can change how the selection area is calculated, without touching the system that triggers it.

Lastly, passing information about the selection to a display script, that’s kept separate from the selection itself, makes it easier to add visual effects, such as fading out the selection box, without affecting how the logic works.

So, while it can seem excessive, it can also be extremely useful to split up your game’s functionality into such small parts.

However,

The idea is to make working with your game easier, not harder.

If splitting up a class makes it more difficult to understand what a script does or how it’s supposed to work then simply don’t do it.

Instead, try to find a balance between convenience and modularity that you find easy to work with.

And, as a rule of thumb, if you can easily remove a script to disable a particular ability or you can reuse it on another object, without too much fuss, then you’re on the right track.

However, while splitting up a script into small parts can be easy enough, knowing how those parts will communicate with each other can be much more challenging.

So, once you’ve split up the behaviour of your game, how can you put it back together again?

How to connect scripts together (composition example)

One of the biggest challenges you’re likely to face when using composition in Unity is how to connect different scripts together on the same object in an appropriate way.

After all, what’s the point of splitting up a script if you can’t easily change or remove one of the smaller parts without affecting other systems as a result.

For example, it would make sense to keep a Damageable script, a Health script and a Destructible Object script separate as they each do different things.

Screenshot - multiple components on one object

However, they will, of course, need to work together.

So how could you connect them, without creating inappropriate dependencies?

Here’s how…

The Health script

For the moment, the Health script is only responsible for managing a single health float value and provides a public function that allows the health value to be changed by another script.

Like this:

using UnityEngine;
public class Health : MonoBehaviour
{
    float hp=100;
    public void ChangeHealth(float amountToChange)
    {
        hp += amountToChange;
    }
}

That’s all it does, and it does it well.

The idea is that adding this script to an object gives it health, but doesn’t necessarily allow it to be damaged.

For that, I’m going to add a Damageable component.

The Damageable script

In this example, the Damageable script allows an object to take damage by providing a Deal Damage function, that takes a float value parameter for the amount of damage done.

Which looks like this:

public void DealDamage(float damageAmount)
{
    health.ChangeHealth(damageAmount);
}

However, in order for this to work, the Damageable script will need to have a reference to a Health script.

Because the Damageable script is highly likely to only ever be used in combination with a Health script, and because it’s unlikely that I’d want to use the Damageable script to affect any type of value other than a health value, it makes sense to give this script a direct reference to the Health component that’s on the same object.

And, to make sure it works as intended, I can force Unity to require a Health script on the same object when it’s added, using the Require Component attribute.

Like this:

using UnityEngine;
[RequireComponent(typeof(Health))]
public class Damageable : MonoBehaviour
{
    Health health;
    private void Awake()
    {
        health = GetComponent<Health>();
    }
    public void DealDamage(float damageAmount)
    {
        health.ChangeHealth(damageAmount);
    }
}

This will automatically add a Health script when the Damageable script is added to an object, if there isn’t one already. 

And, if you try to remove the Health component, you’ll be stopped, because the Damageable script needs it.

Require Component Warning

The Require Component attribute will prevent you from removing a component if another script needs it to work.

Which can be useful for making sure that different scripts work how they’re supposed to.

But why do it like this?

If the Damageable script requires a Health script, why keep it separate at all?

Why you should split scripts up

The reason it makes sense to keep the Damageable script separate from the Health script is, that, if I choose to change how an object takes damage later on (for example if I want to introduce armour, status effects or complex damage profiles) then I can, without affecting other systems.

Visualisation of script encapsulation in Unity

Limiting what goes in or out of a script makes it easier to change what happens inside it, without breaking anything else.

This works because the exposed elements of the script, which are the only points of contact that other scripts will have, remain the same. Meaning that, even if I changed everything that happens inside the Damageable script, so long as it still accepts a float value and still triggers the Health script’s Change Health function in the same way, nothing else would be affected.

Making it much easier to work with.

How to trigger the damage script

The object now has health and has the ability to take damage.

But…

How will an enemy actually trigger the Deal Damage function?

When an enemy attacks the player, such as by colliding with its hitbox using a weapon, you’ll be able to get a reference to the player object from that collision.

Like this:

private void OnCollisionEnter(Collision collision)
{
    GameObject hitObject = collision.gameObject;
}

For an object to be damageable and, as a result, take damage from being hit, it will need to have a Damageable component on it.

So, all you’d need to do to check if an object is damageable is to try to get that component from the object using Get Component or Try Get Component.

Like this:

public class CollisionDamage : MonoBehaviour
{
    public float dmgAmount=45;
    private void OnCollisionEnter(Collision collision)
    {
        GameObject hitObject = collision.gameObject;
        TryDamageObject(hitObject);
    }
    void TryDamageObject(GameObject objectToDamage)
    { 
        if(objectToDamage.TryGetComponent(out Damageable damageableObject))
        {
            damageableObject.DealDamage(-dmgAmount);
        }
        else
        {
            Debug.Log("That object cannot be damaged.");
        }
    }
}

Try Get Component, when used with the composition approach, can be extremely useful for checking if a particular object has a particular ability.

For example, if it is moveable, if it can be selected or, in this case, if it can be damaged.

If the object is damageable, it will take damage, if it’s not, it won’t.

Is Get Component bad for performance?

You may have heard that Get Component can sometimes be bad for performance.

And it can be, because Get Component typically involves searching an object for a component of a certain type, it can be an expensive function if you’re using it excessively.

However, for some tasks it’s the right tool for the job and trying to avoid using it might cause you more problems than benefits.

So, for this reason, it’s generally fine to use Get Component if you need to but, when you do need to use it, it can help to try to do so sparingly.

In practice, this may mean avoiding putting it in Update, using it as little as is needed, such as to get and cache a reference once, so that you don’t have to repeatedly use it or, when using Get Component with physics or Raycast checks, making use of layer masks to avoid unnecessary checks on other colliders.

Destroying the object when its health runs out

The object can now be damaged by other objects and, when it is, it will lose health.

Then, when the object’s health is below zero the object should be destroyed.

Like this:

using UnityEngine;
public class DestructibleObject : MonoBehaviour
{
    public void DestroyObject()
    {
        Destroy(gameObject);
    }
}

At first, it might make sense to directly trigger the Destroy Object function from the Health script when the health falls below zero using a reference to the Destructible Object script.

However…

Unlike the Damageable script, which, in this example at least, is likely to only ever be used with a Health script, directly connecting the Health script to a component that destroys the object may not make sense.

Why?

Because different damageable objects may respond in different ways when their health is reduced to zero.

When an enemy is defeated, the player might get experience or points.

When a box is destroyed it might drop loot.

Whereas, when the player’s health runs out, enemies might stop attacking, the UI might display a message and the game will reload.

While some connections are unlikely to change, others may be completely different depending on which object the component is attached to.

So how can you trigger other functions from a script if you don’t know what those functions will be?

How to create modular connections in Unity

When building the functionality of your game, chances are you will run into a situation where you need to trigger different, interchangeable behaviours from the same event.

For example, different types of collectables might produce different effects but they are all picked up in the same way.

Interactions with buttons, levers and objects might trigger different events but may all be triggered from a single type of command.

Or, as is the case in this example, the process of running out of health is always the same, but may need to trigger different actions as a result, depending on the object.

So what’s the answer?

How can you use a single trigger to generate different results for different objects?

One option is to use a Unity Event.

How to use Unity Events to trigger functions

Unity Events, while commonly used with buttons and UI elements, can also be used in scripting to create modular function triggers.

Screenshot of a Unity Event in the Inspector

This works because Unity Events are, essentially, serialized function calls.

What does that mean?

Serialised means that it will appear in the Inspector, allowing you to decide what will happen when the Unity Event is invoked, without changing the script.

Which means that, while the trigger stays the same, it can be used to generate different actions on a per-object basis.

Here’s how it works.

Back in the Health script, add the Unity Events namespace and a new Unity Event, called On Health Zero.

Then, every time the health is changed, check to see if it’s below zero and, if it is, call the On Health Zero Event.

Like this:

using UnityEngine;
using UnityEngine.Events;
public class Health : MonoBehaviour
{
    public float hp=100;
    public UnityEvent onHealthZero;
    public void ChangeHealth(float amountToChange)
    {
        hp += amountToChange;
        if(hp <= 0 )
        {
            onHealthZero.Invoke();
        }
    }
}

Then, in the Inspector, you’ll be able to hook up the On Health Zero event to any other script, allowing you to trigger other functions without having to specifically write them out in code.

Screenshot - Select Unity Event Function

Unity Events allow you to connect different outcomes to common triggers, without changing the script.

This is useful, as it allows you to create specific behaviour, with a common trigger.

When should you use a Unity Event instead of a direct reference?

If a script’s only purpose is to work with another script or component, then it usually makes sense to keep a cached reference to it and to deal with it directly.

Such as in the Damegable script example earlier in this article.

However…

If you want to trigger an action that is beyond the responsibility of the script you’re writing, or that may change from object to object, consider using a Unity Event to trigger it instead, as doing so may make it easier to build your game using the composition method.

Secondary functions, such as particle triggers, sound effects or disabling other local systems all typically occur when something else happens and, as a result, it can be tricky to know where to put them in code, particularly if, when using the composition method, you’re trying to keep each script focussed on a single task.

Using Unity Events allows you to trigger other actions in a modular way, outside of a script, so that the script itself is small, reusable and self-contained, which can make your whole project easier to work with. 

Now it’s your turn

Now I want to hear from you.

How are you structuring your game’s scripts?

Are you using composition, inheritance, or some other method?

And what have you learned about organising logic in Unity that you know others will find helpful?

Whatever it is, let me know by leaving a comment.

by John Leonard French

Game audio professional and a keen amateur developer.

Get Game Development Tips, Straight to Your inbox

Get helpful tips & tricks and master game development basics the easy way, with deep-dive tutorials and guides.

My favourite time-saving Unity assets

Rewired (the best input management system)

Rewired is an input management asset that extends Unity's default input system, the Input Manager, adding much needed improvements and support for modern devices. Put simply, it's much more advanced than the default Input Manager and more reliable than Unity's new Input System. When I tested both systems, I found Rewired to be surprisingly easy to use and fully featured, so I can understand why everyone loves it.

DOTween Pro (should be built into Unity)

An asset so useful, it should already be built into Unity. Except it's not. DOTween Pro is an animation and timing tool that allows you to animate anything in Unity. You can move, fade, scale, rotate without writing Coroutines or Lerp functions.

Easy Save (there's no reason not to use it)

Easy Save makes managing game saves and file serialization extremely easy in Unity. So much so that, for the time it would take to build a save system, vs the cost of buying Easy Save, I don't recommend making your own save system since Easy Save already exists.

Comments

  1. I think these words you wrote are key: “Instead, try to find a balance between convenience and modularity that you find easy to work with.”

    I think that’s true of any approach, as there unfortunately isn’t a one-size-fits-all solution.

    I found that Actor Oriented Design worked well on our latest project as it enabled us to treat our GameObjects as Actors in a movie (which isn’t far from the truth given how our scenes are setup). These Actors take “cues” from other Actors in the scene and are responsible for performing their own complex actions. Those actions are carried out in a modular manner, with either composition or “sub-actors” which manage the larger responsibilities for more granular compositions. It made such a difference in script management it made our blog!

  2. This is for me one of the most challenging subjects in GameDev using Unity. When I feel that I have found an architecture of scripts that makes me feel on control and I feel I can do changes and updates easily, it gives me more energy in working on the project. In the opposite, when I see the architecture is convoluted and every time I want to add a new functionality the architecture plays against me instead of helping me, the chances are high that I will abandon the project.

    1. Author

      Absolutely! As someone who has tried to put a game together before, but gave up because I couldn’t work out why it was so confusing to make things work together, starting to understand the importance of this was an eye-opener for me. Thanks for your perspective on this.

  3. Awesome, thanks for the article. Will try to apply it now that I’m restarting my project from zero.

  4. Thanks for great article! I had a questions about composition and they answered me well, especially I was afraid of getting reference of Health in Damageable class, because sometimes i found myself Health also needs Damageable which i think something went wrong in my code. Well I had this inheritance problem in one of my game where I spawn resources when i cut some tree, dig some area etc. But when extending game, enemies also needed to spawn resources when killed. So I just couldn’t inherit from Harvestable and use SpawnResource method there. Well, Instead, I created a ResourceSpawner script and added it to all that spawns resource in proper places and referenced to it. For example, Harvestable had ResourceSpawner component in it. And enemy had the script but only used in Unity event since it will spawn when they died. What would be your apporach in that situation?
    I really want to use Interfaces and composition together and tried to solve this problem with interfaces by adding ISpawner interface to all components instead of adding directly ResourceSpawner component.
    So i created a ISpawner, and a method inside SpawnResource(); and implemented to those who need it.
    But since we need to create abstact method in Interface before 8.0 c#, I found myself repeating code while adding codes inside the SpawnResource(); script. For example, Enemy is implementing ISpawner, and need to fill the SpawnResource method. And for example, the tree implents ISpawner, and needs to fill the method again. How would you solve that in that case? I really think using composition & interfaces are cool together (well i might be wrong) but i couldnt make it modular.

    Thanks for the great article again.

    1. Author

      Thanks for sharing, so to answer your question, I think that your approach of giving both the harvestable objects and enemies a component that allows them to spawn resources is the simplest option.

      I’m not an expert, but I feel like, very generally speaking, interfaces are good for creating anonymous interactions. For example, where you want to be able to trigger two different classes in the same way, an interface allows you to do that. Which might make sense if your harvestable and your enemy both spawn resources differently.

      But, because I don’t think that’s what you’re doing, composition, alone, might be a better fit, since you don’t have a need to interpret the same function call differently, which is my understanding of why you might consider using an interface.

      Hope that helps!

Leave a Comment