Course contentsShow
← Unity Game Development
Lesson 465 of 8287. Advanced Game ArchitectureFree lesson

Decoupling States with the State Pattern

Refactor conditional logic into individual state classes to improve code maintainability and adhere to the Open-Closed Principle.

In this lesson, you will learn how to replace messy, error-prone switch or if-else blocks with individual state classes to make your game logic scalable and easy to maintain.

The Problem: The "Mega-Switch" Statement

As your game grows, you’ve likely encountered a character script with a massive Update() method containing a switch statement that checks if (isJumping), if (isRunning), or if (isIdle). Adding a new state, like Dashing, forces you to modify this giant block, increasing the risk of breaking existing behavior. This violates the Open-Closed Principle, which states that your code should be open for extension but closed for modification.

The Solution: The State Pattern

Instead of one class handling every state, we encapsulate each state into its own class. You create a common IState interface and a StateMachine controller. The controller simply holds a reference to the "current" state object and calls its Update() method.

Here is how you structure this:

public interface IState {
    void Enter();
    void Update();
    void Exit();
}

public class IdleState : IState {
    public void Enter() { /* Logic for starting idle animation */ }
    public void Update() { /* Check for input to move */ }
    public void Exit() { /* Clean up */ }
}

Why This Works

When your character moves from Idle to Moving, the StateMachine calls Exit() on the IdleState, updates the reference to a new MovingState object, and calls Enter() on that new state.

Your PlayerController no longer needs to know how jumping or idling works; it only needs to know how to transition between states. If you want to add a Swimming state, you don't touch your existing Idle or Jump code at all—you simply create a new SwimmingState class. This clean separation of concerns makes debugging much simpler because you know exactly which class contains the logic for a specific behavior.

Key Takeaway: By encapsulating behavior into dedicated state classes, you transform monolithic conditional logic into a flexible system where states can be added or modified without risking regressions in unrelated game mechanics.