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
MediumWrite the flytrap's transitions in each Tick of FlytrapStates.cs.
- Open: when
trap.PlayerInReach(), calltrap.machine.ChangeState(trap.snap);. - Snap: add
Time.deltaTimetotimer; at0.5, change totrap.chewif the player is still in reach, elsetrap.open. - Chew: count
timerdown from 3; at 0, change totrap.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
1Components, Not Hierarchies
Small ComponentsA Base Enemy ScriptAbstract ComponentsComponents Working TogetherRecap: Modular TurretRecap: Twin Cannon4Serialized Data
Serializable ClassesLists in the InspectorEnums in the InspectorRecap: Loot TableRecap: Roll the Loot7State Machines
States as ClassesThe Alert StateA State MachineTransitionsEnemy BrainsThe Knight AttacksRecap: Door States2Interfaces in Unity
IDamageableInterfaces and TryGetComponentGetComponentsRecap: Interact KeyRecap: Nearest in ReachPractice on your own: Unity playground