Gearbox is a state machine/chart library for the Bevy game engine.
Why gearbox
State machines are useful everywhere in games - AI behavior, ability lifecycles, UI flows, animation controllers. But state machines in an ECS are a hard problem. Gearbox solves this by representing state machines as regular entity hierarchies. States are entities. Transitions are entities. Everything lives in the ECS and plays by its rules.
- Pure ECS. States and transitions are entities with components. Query for active states with
With<MyComponent>. No new paradigms. - Fully parallelizable. All transition resolution runs through a parallelized schedule. Thousands of machines per frame.
- Message-driven. Trigger transitions by writing Bevy messages. Attach side effects that automatically produce downstream messages when transitions fire.
- Data-driven. State machines are entity hierarchies. Spawn them from scenes, build them from assets, edit them at runtime.
- Visual Editor (optional). Build, edit, and monitor state machines while your game runs.
Getting started
use *;
use GearboxPlugin;
Building a state machine
use *;
use ;
use *;
Triggering transitions
Define a message with #[derive(GearboxMessage)], marking the entity it's
addressed to with #[gearbox(target)] (the message listener walks SubstateOf
from there to find the machine root):
use *;
use *;
// Write it from any system.
The derive also auto-registers the message type through inventory, and
GearboxPlugin installs every derived message's listener on build — so a
derived message just works with no extra wiring. (You can still call
app.register_transition::<Activate>() explicitly, e.g. for generic message
types, which inventory can't auto-register; it's deduped, so overlap is
harmless.)
To filter which messages match an edge, supply a custom validator (the default
is AcceptAll):
State components
Automatically insert/remove a component on the machine root based on which
state is active. StateComponent isn't Default, so insert it through a
template closure:
use *;
;
// ...
#Walking template
// The `Walking` component appears on the machine root while this state is active.
Reacting to state changes
Features
- Hierarchical states (nested state machines / statecharts)
- Parallel regions
- Shallow and deep history
- Guarded transitions with string-based guard sets
- Delayed transitions (timer-based)
- Always-edges (automatic transitions when conditions are met)
- Parameter-driven guards (float/int/bool ranges with hysteresis)
- Side effects (message-in, message-out on transition)
- State components (auto insert/remove on enter/exit)
- Reset edges (clear subtree state on transition)
- Internal vs external transitions
Migrating from the builder API
The imperative builder traits (spawn_substate, spawn_transition,
init_state_machine, build_transition_always, spawn_branch, …) are
deprecated and will be removed in the next major release. The
#[gearbox_message] / #[transition_message] attribute macros have been
removed - define messages with #[derive(GearboxMessage)]. Author machines
as bsn! scenes instead.
This is a restructuring, not a one-to-one swap. The complicated builder
collapses into a single bsn block.
// Before - imperative builders:
let ready = commands.spawn_substate.id;
let active = commands.spawn_substate.id;
commands.;
commands.spawn;
commands.entity.init_state_machine;
// After - one bsn! scene:
commands.spawn_scene;
Version Table
| Bevy | Gearbox |
|---|---|
| 0.19 | 0.8 |
| 0.19 | 0.7 |
| 0.18 | 0.6 |
| 0.18 | 0.5 |
| 0.17 | 0.4 |
Contributing
Feel free to open issues or create pull requests if you encounter any problems.
Ask us on the Bevy Discord server's Gearbox topic in #ecosystem-crates for larger changes or other things if you feel like so!
License
Dual-licensed under MIT (LICENSE-MIT) or Apache 2.0 (LICENSE-APACHE).