Hierarchical State Machines

Lesson 17 of 19 • 25 XP

Keep your place in this quest

Log in or sign up for free to subscribe, follow lesson progress, and access more learning content.

As your gameplay grows, you will eventually need objects that behave differently depending on what they are currently doing. An enemy can be idle, patrolling, chasing, attacking, stunned, or dead. A door can be closed, opening, open, or locked. A boss fight can move through several phases.

This is where State Machines become useful.

image.png

In the image above, you can see the default enemy that Crave creates for the new project, with its State Machine open to the right side. We are inside the "wander" state that do have inner states, such as the "random selector" (the initial state, represented by the green color), the "go to random" and the "idle". You can see the transitions between the states represented by the arrows, and that each state can have its own logic, either in Python, as shown in the image, or in logic bricks.

A state machine helps you organize behavior into clear states instead of writing one large script full of unrelated conditions.

In this lesson, you will learn:

  • What a State is.
  • What a Transition is.
  • Why hierarchical state machines are useful.
  • How Cave's State Machine Component fits into an entity.
  • How On Enter, On Update, and On Exit work.
  • How Python and Logic Bricks can be used inside states and transitions.

The goal is to understand the mental model first. Once that makes sense, the editor becomes much easier to use.

What Is a State?

A state describes what an object is currently doing.

image.png

For example, an enemy might have these states:

  • Idle
  • Patrol
  • Chase
  • Attack
  • Dead

Only one of those states should usually control the enemy at a time. If the enemy is in Patrol, it should not also be running the attack behavior. If it is in Dead, it should not keep chasing the player.

This makes gameplay easier to reason about because each state has a clear job.

State What It Might Do
Idle Stand still and wait.
Patrol Move between points.
Chase Move toward the player.
Attack Play an attack animation and deal damage.
Dead Disable movement and play a death animation.

Instead of asking "what should the enemy do this frame?" from one huge script, you can ask "which state is the enemy currently in?"

What Is a Transition?

A transition is the rule that moves the state machine from one state to another.

image.png

For example:

  • Idle to Patrol when the enemy should start moving, after a timer.
  • Patrol to Chase when the player is detected.
  • Chase to Attack when the enemy is close enough.
  • Attack to Chase when the attack finishes.
  • Any state to Dead when health reaches zero.

The transition is not the behavior itself. It is the decision that says, "now we should change state."

That separation is important. The Attack state can focus on attacking, while the transition decides when it is time to enter or leave that state.

Why Hierarchical State Machines Help

Cave uses a hierarchical state machine approach. This means states can be organized inside other states. That is useful because many behaviors share a common parent idea.

For example, an enemy could be organized like this:

Alive
    Idle
    Patrol
    Chase
    Attack
Dead

The Idle, Patrol, Chase, and Attack states all belong to the broader Alive state. This makes the structure easier to understand and can help you avoid repeating the same logic in several places.

For a beginner project, you can start with a simple flat state machine. But as your gameplay gets more complex, hierarchy helps keep the logic clean.

The State Machine Component

To use a state machine in a scene, an entity needs a State Machine Component.

The component connects the entity to a State Machine asset. The asset defines the states, transitions, and logic, while the component runs that logic on the entity during gameplay.

This is similar to other Cave systems:

Asset Component
Python Script Python Component runs it.
Animation Animation Component plays it.
State Machine State Machine Component runs it.

In practice, this means your enemy entity can have a State Machine Component, and that component can run an enemy AI state machine.

A state machine asset can have properties, as you can see by going to the properties tab, and those properties can be locally changed in the state machine component, allowing you to create modular state machines that have different settings across different types of entities.

State Events

Each state can react to three important moments:

Event When It Runs Common Use
On Enter When the state starts. Reset timers, initialize variables, play an animation, choose a target.
On Update While the state is active. Move, check distances, update timers.
On Exit When leaving the state. Stop effects, clear temporary values.

For example, an Attack state might work like this:

  • On Enter: play the attack animation.
  • On Update: wait for the hit moment or animation callback.
  • On Exit: clear the attack flag.

This makes each state feel like a small self-contained behavior.

Python Inside States

State machines can use Python for logic.

Inside each State's Python code, Cave provides useful variables such as entity and scene, so the state can control the entity that owns the State Machine Component.

For example, a state could play a sound when entered:

cave.playSound("Enemy Alert", volume=0.8)

Or it could read a property from the entity:

health = entity.properties.get("health", 100)

This keeps state logic close to the behavior it belongs to.

Python Inside Transitions

Transitions can also use Python. For transition Python logic, Cave uses a variable called result. If result becomes True, the transition can happen. For example, a transition to a Dead state could check health like this:

health = entity.properties.get("health", 100)
result = health <= 0

A transition from Chase to Attack could check distance if your enemy script or properties provide that value:

player = scene.get("Player")
playerPos = player.getTransform().getWorldPosition()
entityPos = entity.getTransform().getWorldPosition()

distanceToPlayer = (playerPos - entityPos).length()
result = distanceToPlayer < 2.0

The exact data depends on your game, but the idea is always the same: the transition decides whether it is time to move from one state to another.

TIP: During a transition, all the local variables defined in the state where the transition is coming from are available for you. So in the previous example, where we calculated the distance between the entity and the player, we could store the player transform and the entity transform on two variables at the on-enter state of the state this transition is coming from to save a little bit of resources, since it's then cached and we would no longer need to get this data every frame.

Logic Bricks Inside State Machines

State machines can also use Logic Bricks.

This is useful when you want to build behavior visually instead of writing Python code. For many gameplay tasks, Logic Bricks are a very approachable way to connect events, conditions, and actions.

They work in the exact same way as explained above for the Python code inside the state or for a transition. The State's Logic Bricks and the Transition's Logic Bricks are already initialized with the bricks you need to get started.

You can mix Python and Logic Bricks depending on what feels clearer for the behavior you are building. If a State or Transition has both, then both will be executed and the transition will occur if one OR the other happens.

When to Use a State Machine

Use a state machine when an object has clear modes of behavior.

Good examples include:

  • Enemy AI.
  • Boss phases.
  • Doors and interactable objects.
  • Mission or quest flow.
  • Cutscene progression.
  • Character ability states.

You may not need a state machine for tiny one-action objects. If a pickup only plays a sound and disappears, a small Python script or Logic Brick setup may be enough.

What You Should Remember

A state machine organizes gameplay into states and transitions.

States describe what is currently happening. Transitions decide when to switch to something else. On Enter, On Update, and On Exit help keep each state focused and readable.

For simple behavior, a script may be enough. For behavior with multiple modes, a state machine usually makes the logic easier to understand and easier to expand.