States
States model mutually exclusive modes with transition callbacks. Declare them in your manifest and the framework wires the state machine in automatically.
What Are States?
States represent the current mode of a component — one active value from a named set. Unlike flags (which are independent booleans that can all be true at once), only one state can be active at a time, and transitions fire optional enter/exit callbacks.
Idle and Running at once.Declaring States in a Manifest
Add a State enum to your manifest. The framework's template machinery detects it at compile time and wires the state machine in automatically. The initial state is the zero-value of the enum (first declared value).
class EnemyManifest : public ml::Manifest
{
public:
// Only one of these is active at a time
enum class State { Idle, Patrolling, Chasing, Attacking, Dead };
enum class Flag { Visible, Invulnerable }; // flags can combine freely
};
State API Reference
| Method | Description |
|---|---|
setState(State::X) | Transition to X. Fires exit callback on old, enter callback on new. |
getState() | Return the current StateEnum value. |
isState(State::X) | Return true if currently in state X. |
onStateEnter(callback) | Register a callback invoked after each transition. Receives new state. |
onStateExit(callback) | Register a callback invoked before each transition. Receives old state. |
class Enemy : public ml::ComponentWith
{
public:
void initialize()
{
// Wire transition callbacks
onStateEnter([this](State s) {
switch (s) {
case State::Patrolling: startPatrolAnimation(); break;
case State::Chasing: startRunAnimation(); break;
case State::Dead: startDeathAnimation(); break;
default: break;
}
});
onStateExit([this](State s) {
if (s == State::Attacking)
cancelAttackHitbox();
});
setState(State::Patrolling); // fires enter(Patrolling)
}
void update()
{
// Use isState() and getState() to drive logic
if (isState(State::Chasing) && playerInAttackRange())
setState(State::Attacking);
}
void takeDamage(int amount)
{
_health -= amount;
if (_health <= 0 && !isState(State::Dead))
setState(State::Dead); // fires exit(current), enter(Dead)
}
};
Multiple States via Traits
When you mix traits into a component via ComponentWith, any trait that has its own State enum contributes a separate state machine. GatherStates aggregates all of them, and a single setState() overloaded on enum type handles all of them.
// DraggableManifest::State has: { FREE, LOCK_X, LOCK_Y }
// MyManifest::State has: { Idle, Active, Done }
class MyWidget : public ml::ComponentWith {};
MyWidget w;
// Both state machines are completely independent
w.setState(MyManifest::State::Active); // widget state machine
w.setState(ml::DraggableManifest::State::LOCK_Y); // drag axis state machine
// Each has its own callbacks
w.onStateEnter([](MyManifest::State s) {
// fires only for MyManifest::State transitions
});
MyManifest::State::Active has zero effect on DraggableManifest::State, and vice versa. Each state machine is entirely independent.