Monday, September 29, 2025

6 Design Patterns Every Unity Developer Needs to Know

There are many reasons why there are design patterns every Unity developer needs to know. To know, and to fully understand that is. If you are a beginning game developer, you’ve undoubtedly already applied some of these patterns in your own game design project without even knowing it. Which pattern is the best depends on the nature and the game design structure. I use a combination of design patterns in my project; predominantly the observer and the singleton pattern. Below, we're going over all those design patterns, along with their pros and cons. Find out what the most common game design patterns are in Unity 6, with examples, so you can easily copy and paste them in your own project!



1. Singleton Pattern

Let's start off with probably the most common game design pattern of all; the singleton pattern. The Singleton ensures there’s only one instance of a class and provides global access to it. In Unity, it’s commonly used for managers like GameManager, AudioManager, or UIManager.


public class AudioManager : MonoBehaviour
{
    public static AudioManager Instance { get; private set; }

    private void Awake()
    {
        if (Instance != null && Instance != this)
        {
            Destroy(gameObject);
            return;
        }
        Instance = this;
        DontDestroyOnLoad(gameObject);
    }
}  

From another script, the singleton can now be easily accessed by writing for example:


public class Player : MonoBehaviour
{
    private AudioClip _hurtSound;

    public void PlayHurtSound()
    {
        AudioManager.Instance.PlaySound(_hurtSound);
    }
}  

 

 

Pros:

  • Easy global access
  • Simple to understand 
  • Guarantees a single instance

Cons:

  • Can lead to tightly coupled code
  • Easily overused
  • There can only be one instance 


2. Observer Pattern

The Observer lets objects subscribe to and react to events. Instead of constant polling, scripts get notified when something changes, perfect for health systems, UI updates, or achievements. In fact, I wrote an article about EventBuses, a design pattern I use frequently in my own projects. It's easily expandable thanks to structs, and it's built on the observer pattern technique. Below is a more simplified example of an observer pattern:


public class PlayerHealth : MonoBehaviour
{
    public event Action<int> OnHealthChanged;
    private int health = 100;

    public void TakeDamage(int amount)
    {
        health -= amount;
        OnHealthChanged?.Invoke(health);
    }
}

public class HealthUI : MonoBehaviour
{
    [SerializeField] private PlayerHealth playerHealth;

    private void OnEnable()
    {
        playerHealth.OnHealthChanged += UpdateHealthUI;
    }

    private void OnDisable()
    {
        playerHealth.OnHealthChanged -= UpdateHealthUI;
    }

    private void UpdateHealthUI(int health)
    {
        Debug.Log("Health updated: " + health);
    }
}
  

Pros:

  • Decouples systems cleanly
  • Scales well with many listeners

Cons:

  • Event chains can be hard to debug
  • Unsubscribing mistakes may cause memory leaks


3. Factory Pattern

The Factory centralizes object creation. Instead of hardcoding prefabs or enemy types, you ask the 'factory' to spawn what you need, keeping spawning logic concise and flexible.


public abstract class Enemy : MonoBehaviour
{
    public abstract void Attack();
}

public class Goblin : Enemy
{
    public override void Attack() => Debug.Log("Goblin attacks!");
}

public class EnemyFactory
{
    public Enemy CreateEnemy(EnemyType type, Vector3 position)
    {
        switch (type)
        {
            case EnemyType.Goblin:
                return Object.Instantiate(
                    Resources.Load<Goblin>("GoblinPrefab"), 
                    position, 
                    Quaternion.identity
                );
            default:
                throw new ArgumentOutOfRangeException();
        }
    }
}
  

Pros:

  • Keeps creation logic centralized
  • Easy to extend with new types

Cons:

  • Factory can become bloated if mismanaged


4. State Pattern

The State pattern organizes complex behaviors into modular states. Instead of giant if/else trees, you define states like Idle, Patrol, or Attack and switch between them cleanly. For small, and medium sized projects in Unity (the ones probably created by solo indie game developers), state pattern are unmissable. Not only can you setup a FSM (Finite State Machine) like this for enemy behavior, your entire game can run on a state machine like this. (In my game, I even have a state machine for different 'game states', like pause - menu - cut scene - gameplay etc). Many AAA games use state machines, although those are a little more in-dept as you can imagine. They use a Hierarchical Finite State Machines (HFSMs), it adds nested states. A concept we'll delve into at a later time. For now, just understand how the State Pattern at it's core works by looking at the example below:


public interface IEnemyState
{
    void Enter(Enemy enemy);
    void Execute(Enemy enemy);
    void Exit(Enemy enemy);
}

public class PatrolState : IEnemyState
{
    public void Enter(Enemy enemy) => Debug.Log("Start patrolling.");
    public void Execute(Enemy enemy) => Debug.Log("Patrolling...");
    public void Exit(Enemy enemy) => Debug.Log("Stop patrolling.");
}

public class Enemy : MonoBehaviour
{
    private IEnemyState currentState;

    public void ChangeState(IEnemyState newState)
    {
        currentState?.Exit(this);
        currentState = newState;
        currentState.Enter(this);
    }

    private void Update()
    {
        currentState?.Execute(this);
    }
}
  

Pros:

  • Organized, modular behavior
  • Easy to add new states

Cons:

  • More scripts and setup required


5. Object Pool Pattern

Object pooling reuses inactive objects instead of instantiating and destroying them repeatedly. It’s vital for bullets, enemies, and particles in high-performance games.If not applied, a game would performance struggle with constantly instantiating and destroying Game Objects.


public class ObjectPool<T> where T : MonoBehaviour
{
    private Queue<T> pool = new Queue<T>();
    private T prefab;

    public ObjectPool(T prefab, int initialSize)
    {
        this.prefab = prefab;
        for (int i = 0; i < initialSize; i++)
        {
            var obj = GameObject.Instantiate(prefab);
            obj.gameObject.SetActive(false);
            pool.Enqueue(obj);
        }
    }

    public T Get(Vector3 position)
    {
        if (pool.Count == 0)
            AddObject();

        var obj = pool.Dequeue();
        obj.transform.position = position;
        obj.gameObject.SetActive(true);
        return obj;
    }

    public void Return(T obj)
    {
        obj.gameObject.SetActive(false);
        pool.Enqueue(obj);
    }

    private void AddObject()
    {
        var obj = GameObject.Instantiate(prefab);
        obj.gameObject.SetActive(false);
        pool.Enqueue(obj);
    }
}
  

Pros:

  • Huge performance gains
  • Reduces garbage collection spikes

Cons:

  • Uses more memory upfront
  • Requires management logic


6. Component Pattern

If you have ever made the simplest game in Unity, you undoubtedly have used this one without even realizing it. That's because the component pattern is in Unity’s core design. Instead of deep inheritance chains, you attach small, reusable behaviors to GameObjects. Think of jump, sprint, and attack as separate modular scripts.


public class JumpAbility : MonoBehaviour
{
    [SerializeField] private float jumpForce = 5f;
    private Rigidbody rb;

    private void Awake() => rb = GetComponent<Rigidbody>();

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
            rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
    }
}
  

Pros:

  • Encourages modular design
  • Easy to mix and match behaviors

Cons:

  • Can lead to too many small scripts if unmanaged
  • Can lead to large, hard to maintain scripts if unmanaged 


Alright, those are the 6 design patterns every Unity developer should know. It's part of the architectural design of your game (a topic I'll elaborate on later with multiple articles, since it's too broad of a topic), and it's essential to understand all of them and be persistent with its implementation. Hopefully you've learned a thing or two (or more). How are you structuring your game projects? Do you use more than two game design patterns, or perhaps ALL of them?? Or do you know a pattern that's not on the list? Let me (and everyone else that visits the site!) know :)

No comments:

Post a Comment