Menu
CoddyTech

Transitions

Part of the Game Architecture in Unity section of Coddy's Unity journey. Lesson 38 of 64.

Who decides when the state changes? Asking the owner puts every rule back in one Update, as the switch did. The usual answer is that each state knows its own way out: in its Tick it checks its condition and asks the machine for the next state. This bomb waits until the player comes close, then burns its fuse, and goes back to waiting if the player runs:

public class Bomb : MonoBehaviour
{
    public Transform player;
    public StateMachine machine = new StateMachine();
    public IState idle;
    public IState fuse;

    void Start()
    {
        idle = new IdleState(this);
        fuse = new FuseState(this);
        machine.ChangeState(idle);
    }

    void Update() { machine.Tick(); }

    public bool PlayerNear()
    {
        return Vector3.Distance(transform.position, player.position) < 3f;
    }
}

public class IdleState : IState
{
    private Bomb bomb;
    public IdleState(Bomb bomb) { this.bomb = bomb; }

    public void Enter() { }
    public void Exit() { }

    public void Tick()
    {
        if (bomb.PlayerNear()) bomb.machine.ChangeState(bomb.fuse);
    }
}

public class FuseState : IState
{
    private Bomb bomb;
    private float timer;
    public FuseState(Bomb bomb) { this.bomb = bomb; }

    public void Enter() { timer = 2f; }
    public void Exit() { }

    public void Tick()
    {
        if (!bomb.PlayerNear())
        {
            bomb.machine.ChangeState(bomb.idle);
            return;
        }
        timer -= Time.deltaTime;
        if (timer <= 0f)
        {
            Debug.Log("Boom!");
            Object.Destroy(bomb.gameObject);
        }
    }
}

Each state gets the bomb in its constructor and reaches the machine, the player test and the other states through it. The bomb makes every state once, in Start, so a state never makes another with new: two states that made each other in their constructors would loop until the stack overflows. The bomb's Update only ticks the machine, and the rules live in the states they belong to: the 2-second fuse is FuseState's business alone.

ChangeState runs FuseState's Exit and IdleState's Enter at once, but the rest of FuseState's Tick would still run after the call. The return stops it, otherwise the fuse would burn one more frame for a state that has already ended. Destroy is written Object.Destroy: a MonoBehaviour inherits it from Unity's Object class, and a plain class has to name the class.

challenge icon

Challenge

Medium

Write the flytrap's transitions in each Tick of FlytrapStates.cs.

  1. Open: when trap.PlayerInReach(), call trap.machine.ChangeState(trap.snap);.
  2. Snap: add Time.deltaTime to timer; at 0.5, change to trap.chew if the player is still in reach, else trap.open.
  3. Chew: count timer down from 3; at 0, change to trap.open.

Press Play, walk into the trap, then dodge it. Then Check.

Try it yourself

using UnityEngine;

public class OpenState : IState
{
    private Flytrap trap;

    public OpenState(Flytrap trap)
    {
        this.trap = trap;
    }

    public void Enter()
    {
        trap.Paint(new Color(0.3f, 0.8f, 0.2f));
    }

    public void Tick()
    {
        // Snap when the player is in reach
    }

    public void Exit()
    {
    }
}

public class SnapState : IState
{
    private Flytrap trap;
    private float timer;

    public SnapState(Flytrap trap)
    {
        this.trap = trap;
    }

    public void Enter()
    {
        timer = 0f;
        trap.Paint(new Color(1f, 0.55f, 0.1f));
    }

    public void Tick()
    {
        // After 0.5 seconds: chew if the player is still in reach, open again if not
    }

    public void Exit()
    {
    }
}

public class ChewState : IState
{
    private Flytrap trap;
    private float timer;

    public ChewState(Flytrap trap)
    {
        this.trap = trap;
    }

    public void Enter()
    {
        timer = 3f;
        trap.Paint(new Color(0.5f, 0.05f, 0.1f));
        Debug.Log("Chomp!");
    }

    public void Tick()
    {
        // After 3 seconds: open again
    }

    public void Exit()
    {
    }
}

All lessons in Game Architecture in Unity

Practice on your own: Unity playground