A state machine controls which behavior is active at any time. Only one state runs at a time. When conditions change, the machine switches to a different state.
- Player standing still - Idle state
- Player presses arrow - Walk state
- Player presses space - Jump state

Basic State Machine Structure
First, create an enum to list all possible states.
public enum PlayerState
{
Idle,
Walk,
Run,
Jump,
Attack
}
Then create a manager script that switches between states.
public class StateMachine : MonoBehaviour
{
public PlayerState currentState;
void Start()
{
currentState = PlayerState.Idle;
}
void Update()
{
switch (currentState)
{
case PlayerState.Idle:
Idle();
break;
case PlayerState.Walk:
Walk();
break;
case PlayerState.Run:
Run();
break;
case PlayerState.Jump:
Jump();
break;
case PlayerState.Attack:
Attack();
break;
}
}
void Idle()
{
// Play idle animation
// Check for input to switch state
if (Input.GetAxis("Horizontal") != 0)
currentState = PlayerState.Walk;
if (Input.GetButtonDown("Jump"))
currentState = PlayerState.Jump;
}
void Walk()
{
// Move player
// Play walk animation
if (Input.GetAxis("Horizontal") == 0)
currentState = PlayerState.Idle;
if (Input.GetButtonDown("Jump"))
currentState = PlayerState.Jump;
}
void Run()
{
// Run logic
}
void Jump()
{
// Jump logic
// After jump, return to Idle or Walk
}
void Attack()
{
// Attack logic
}
}
Output:

Complete Player State Machine Example
Here's a full working example for a player character
public class PlayerStateMachine : MonoBehaviour
{
public enum State
{
Idle,
Walking,
Jumping,
Attacking
}
public State currentState;
private Rigidbody rb;
private Animator anim;
private float moveSpeed = 5f;
private float jumpForce = 10f;
private bool isGrounded;
void Start()
{
rb = GetComponent<Rigidbody>();
anim = GetComponent<Animator>();
currentState = State.Idle;
}
void Update()
{
// Check ground (simplified)
isGrounded = Physics.Raycast(transform.position, Vector3.down, 1.1f);
switch (currentState)
{
case State.Idle:
UpdateIdle();
break;
case State.Walking:
UpdateWalking();
break;
case State.Jumping:
UpdateJumping();
break;
case State.Attacking:
UpdateAttacking();
break;
}
}
void UpdateIdle()
{
anim.Play("Idle");
float move = Input.GetAxis("Horizontal");
if (move != 0)
{
currentState = State.Walking;
}
if (Input.GetButtonDown("Jump") && isGrounded)
{
currentState = State.Jumping;
}
if (Input.GetButtonDown("Fire1"))
{
currentState = State.Attacking;
}
}
void UpdateWalking()
{
float move = Input.GetAxis("Horizontal");
if (move == 0)
{
currentState = State.Idle;
}
// Move player
transform.Translate(Vector3.right * move * moveSpeed * Time.deltaTime);
anim.Play("Walk");
if (Input.GetButtonDown("Jump") && isGrounded)
{
currentState = State.Jumping;
}
if (Input.GetButtonDown("Fire1"))
{
currentState = State.Attacking;
}
}
void UpdateJumping()
{
if (isGrounded && rb.velocity.y <= 0)
{
currentState = State.Idle;
}
rb.velocity = new Vector3(rb.velocity.x, jumpForce, rb.velocity.z);
}
void UpdateAttacking()
{
anim.Play("Attack");
StartCoroutine(ReturnToIdle());
}
IEnumerator ReturnToIdle()
{
yield return new WaitForSeconds(0.5f);
if (currentState == State.Attacking)
{
currentState = State.Idle;
}
}
}
State Machine with Separate Classes
For complex games, create separate classes for each state.
// Base class for all states
public abstract class State
{
protected PlayerController player;
public State(PlayerController player)
{
this.player = player;
}
public abstract void Enter();
public abstract void Update();
public abstract void Exit();
}
// Idle state
public class IdleState : State
{
public IdleState(PlayerController player) : base(player) { }
public override void Enter()
{
player.anim.Play("Idle");
}
public override void Update()
{
if (player.moveInput != 0)
{
player.stateMachine.ChangeState(new WalkState(player));
}
if (player.jumpPressed)
{
player.stateMachine.ChangeState(new JumpState(player));
}
}
public override void Exit() { }
}
// Walk state
public class WalkState : State
{
public WalkState(PlayerController player) : base(player) { }
public override void Enter()
{
player.anim.Play("Walk");
}
public override void Update()
{
player.Move();
if (player.moveInput == 0)
{
player.stateMachine.ChangeState(new IdleState(player));
}
}
public override void Exit() { }
}
And the state machine manager:
public class StateMachine
{
public State currentState;
public void ChangeState(State newState)
{
currentState?.Exit();
currentState = newState;
currentState.Enter();
}
}
public class PlayerController : MonoBehaviour
{
public Animator anim;
public float moveInput;
public bool jumpPressed;
public StateMachine stateMachine;
void Start()
{
stateMachine = new StateMachine();
stateMachine.ChangeState(new IdleState(this));
}
void Update()
{
moveInput = Input.GetAxis("Horizontal");
jumpPressed = Input.GetButtonDown("Jump");
stateMachine.currentState?.Update();
}
}
When to Use a State Machine
Good for
- Player behavior (idle, walk, jump, attack).
- Enemy AI (patrol, chase, attack, flee).
- Game states (menu, playing, paused, game over).
- Any system with multiple modes or transitions.
Not needed for
- Simple moving platforms.
- Static obstacles.
- Single-state objects (no behavior change).
- Very basic scripts with no state switching.