Design Patterns in Unity

Design Patterns in Unity (and when to use them)

In Advanced Scripting by John FrenchPublished Leave a Comment

Software design patterns are architectural approaches to designing code in a way that avoids or solves certain problems.

For professional software engineers, or for teams of people working on the same application, they can be essential for making sure that everyone is on the same page and that everyone is building things in the same way.

Which can be important for avoiding bugs, and making a project easier to work on, especially as it gets bigger and more complex.

But, do you need to use a programming pattern when building a game in Unity?

Choosing whether or not to use a design pattern, and how strictly you follow it, depends massively on what it is you’re making and how you’re making it.

Why? Because all design patterns are, essentially, solutions to known or anticipated problems, which means that a pattern that’s ideal in one use case, might be difficult to apply in another.

What’s more, if you’re an independent developer, working on your own game, alone, a design pattern is only as useful as you find it to be, as strictly following a set of programming design rules can sometimes make your game harder to build instead of easier.

Which kind of defeats the point of design patterns in the first place. After all, strictly following a design principle does not automatically make a game good or efficient.

It should, however, make your game easier to build and, chances are, if you’re anything like me, at least, you’ll probably spend just as much time working out how the relationship between the code in your game should work as you’ll spend actually writing it.

But, all of this can only work if you have some idea of the problems you’re trying to solve in the first place.

So what are some of the more commonly used design patterns in Unity, and how can they help you?

What you’ll find on this page:

The Observer Pattern

The Observer Pattern is an event-driven approach that allows you to execute logic when something else in your game happens.

It typically works by allowing observers, such as other scripts, to subscribe one or more of their functions to a subject’s event.

The purpose of this is to avoid tight coupling between scripts, as it allows a class to anonymously respond to events when they happen.

This works because the observer manages its own connection to the subject, which is usually the only class that’s able to call the event.

This means that, if either one is missing, so long as the observer pattern is being used correctly, nothing breaks, because neither of the scripts is dependent on the other.

In Unity, the observer pattern is implemented with Delegates, which are variables that can store one or more functions.

Try my article on Events and Delegates for more information on how to implement a version of the Observer Pattern for yourself.

The Singleton Pattern

One of the first problems you’re likely to run into when building your game is how to connect objects together.

For example, enemy objects in the game may need a reference to the player object in order to fire at it or move towards it.

A common solution to this problem is by using a Singleton, which is a technique that creates a static reference, which is a reference that any class can access without a link to a specific object, but to an instance of an object.

It generally works by having a script set the static reference, which is of its own type, to itself, so long as it’s not already set.

Like this:

public static Singleton Instance { get; private set; }

private void Awake() 
{ 
    // If there is an instance, and it's not me, delete myself.
    
    if (Instance != null && Instance != this) 
    { 
        Destroy(this); 
    } 
    else 
    { 
        Instance = this; 
    } 
}

However, you may have heard that singletons are bad, and that you shouldn’t use them.

While this can be true, it’s only an issue if you use singletons in the wrong way.

But what’s the wrong way to use a singleton?

Singletons are based on static variables and, as a general rule of thumb, you should only make something static if there will definitely only ever be one of those types of things.

Audio Managers, for example, are an ideal use case for singletons, because they need to exist in the scene, everything may need to use it and there will only ever be one in the game.

While a player singleton, despite being very convenient, could cause problems later if you decide to add multiplayer to your game.

Try my article on the right way to use Singletons for more information on how, and when, to use the Singleton Pattern in Unity.

Inheritance

Inheritance is a cornerstone of object-oriented programming, as it allows you to define the behaviour of something based on what it is.

In Unity, you’d typically use inheritance when you want to create different classes that are a type of something else.

For example, a Player, an Enemy, and an NPC would all behave in different ways, but they might still need to share the same functionality, such as moving, talking, or taking damage.

These behaviours are common to all three classes because of what they are, types of human.

The benefit of Inheritance is that it allows you to place those behaviours in one place, a human class, that each of the sub-classes will inherit from.

Like this:

public class Player : Human
{
    // I have health!
}

public class NPC : Human
{
    // I have health!
}

public class Enemy : Human
{
    // I have health!
}

public class Human : MonoBehaviour
{
    public float hp;

    public void TakeDamage(float dmg){...}

    public void Walk(){...}

    public void Talk(){...}
}

The point of this is to avoid writing the same code in multiple different places and it usually works best when your objects and, importantly, their behaviour, falls into an easily identifiable hierarchy.

For more information, try my article explaining how Inheritance in Unity works, and when you should consider using it.

However, as useful as inheritance can be, sometimes it causes as many problems as it solves.

Especially when the objects in your game don’t fit into a neat hierarchical structure.

In which case, you might want to try Composition instead.

Script Composition

Script Composition isn’t technically a design pattern, it’s more of a design concept, but it’s one that lends itself well to Unity’s object-based game engine.

Generally speaking, composition is an alternative to inheritance. While inheritance defines an object’s logic based on what it is, composition is based on what an object is able to do.

The idea is that, instead of putting shared functionality into base classes, composition means that everything an object can do is added to it as separate script components.

Want to be able to break an object? Give it a Breakable script.

Want to select an object? Give it a Selectable script.

Want an object to have health? Write a script that does just that, and nothing else, and then add it to any of your objects.

But why is this useful?

The main limitation of inheritance is that a class can only inherit from one other class.

This means that, if you want to share the same functionality between different objects, you’ll need to structure your hierarchy in a way that makes sense.

For example, animals can take damage, humans can walk, and the player can be controlled.

But what if you want to make a box take damage?

And what if you want to control a car?

Inheritance works when the objects in your game fit into an organisable hierarchy. But when they don’t, that’s when composition can be useful instead, as it allows you to give completely different objects the same abilities, without having to write the same code over and over again.

To find out more about how it works, and how it compares to inheritance, try my article on Script Composition.

The State Pattern

Broadly speaking, the State Pattern involves creating modular logic, meaning different blocks of code that can be swapped out to produce different results, but with the same inputs.

In Unity, this typically involves making some kind of State Machine.

There are several ways to build a state machine but, in code, the general idea is they’re made by creating a kind of swappable data structure, meaning a type that contains different logic inside it but that can be treated as one kind of object, a State.

To do this in Unity, you might use Interfaces or Inheritance which makes sure that each state has the same functions.

These will typically involve functions for processing the logic contained inside the state, such as some kind of Update function, and functions for switching the state to something else.

The benefit of state machines is that they allow you to ignore logic that isn’t relevant to a particular state.

This can make code much easier to write, as it means you don’t need to worry about checking what else the object is doing and can instead just focus on what’s going on in the current state.

Try my article on State Machines for more information on how to make them in Unity and what they’re good for.

The Mediator Pattern

It’s entirely possible that the methods you use to organise your scripts and your code may actually be design patterns in disguise, whether you know it or not.

The Mediator Pattern is a great example of that.

Generally speaking, it’s a pattern that prevents scripts from being codependent on each other, even if they need to interact.

This works by having scripts in a related system go through a mediator instead of communicating directly with the script itself.

This concept is similar to a Service Locator, which is a type of singleton that allows scripts to access commonly used systems via a shared reference.

The mediator pattern is different, however, in that it doesn’t allow a script to communicate through it. Instead, the mediator manages how, and when, the events of one script will influence another.

Unknowingly, I created a version of the mediator pattern as a solution to scripts interacting with each other when they’re on the same object.

I even made an entire video about it, without knowing that the mediator pattern existed:

Which raises an interesting point about design patterns, and how they can help you, in that you don’t need to strictly follow one to gain the benefits of it.

You don’t even need to know that what you’re doing is a design pattern at all.

All that’s important is that you understand the problems you’re trying to solve, the pitfalls you’d like to avoid and that you’re aware of the solutions that are available to you to do exactly that.

Which might be a strict design pattern or it might be a looser design concept, such as some of the techniques here.

It might be something else entirely.

However, when you’re building your own game, it kind of doesn’t matter, so long as it helps make your project easier to manage.

Now it’s your turn

Now I want to hear from you.

How are you using design patterns in your project?

Are you using a set of strict rules to keep your game organised?

Or are you using a mix of techniques to solve problems as you go?

And what have you learned about design patterns in Unity that you know someone else would find useful?

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

John French profile picture

by John 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.

How this content was created

This article was written using first-hand research and experience, without the use of AI. For more information on how Game Dev Beginner articles are written, see my writing policy.

From the experts

For more information on pure design patterns, try Game Programming Patterns by Robert Nystrom.

Leave a Comment