bevy_fsm
Observer-driven finite state machine framework for Bevy ECS.
Bevy Compatibility
| Bevy | bevy_fsm |
|---|---|
| 0.18 | 0.3 |
| 0.17 | 0.2 |
| 0.16 | 0.1 |
Features
- Enum-based states: Keep your states as simple enum variants
- Observer-driven: React to state changes via Bevy observers
- Variant-specific events: No runtime state checks needed in observers
- Flexible validation: Per-entity and per-type transition rules
- Clean API: FSMPlugin for automatic setup
- Initial state support: Automatic enter events when FSM components are added
- Organized hierarchy: Observers automatically organized in entity hierarchy
Quick Start
use *;
use ;
use EnumEvent;
;
Core Concepts
FSMTransition Trait
Implement this trait to define which state transitions are valid:
EnumEvent and FSMState Derives
Use these derive macros to generate variant-specific events:
#[derive(EnumEvent)]- Generates variant-specific event types#[derive(FSMState)]- Implements FSM-specific trigger methods
use *;
use ;
// FSMTransition derive provides "allow all" behavior
// For custom rules, skip the derive and implement manually
FSMPlugin - Automatic Setup
use FSMPlugin;
fsm_observer! Macro
Register variant-specific observers with automatic hierarchy organization:
use ;
Manual Observer Registration
use ;
app.world_mut.add_observer;
app.world_mut.add_observer;
app.world_mut.add_observer;
Generic Event Observers
Observe generic events for runtime state checking:
Advanced Features
Per-Entity Configuration with Priority Model
FSMOverride allows per-entity transition control with a priority-based system.
Priority: Config Wins, Rules Fill Gaps
- Whitelist: Transitions ON the list are immediately accepted
- Blacklist: Transitions ON the list are immediately denied
- Transitions NOT decided by config use
FSMTransitionrules (ifwith_rules())
use FSMOverride;
// Force allow specific transition
commands.entity.insert;
// Whitelist + fallback to FSMTransition
commands.entity.insert;
// Force deny specific transition
commands.entity.insert;
FSMOverride Modes
whitelist([...]): Only listed transitions pass immediatelyblacklist([...]): Listed transitions denied immediatelyallow_all(): All transitions pass (bypass FSMTransition unlesswith_rules())deny_all(): All transitions denied (immutable state)
Context-Aware Validation
Use world state in transition validation:
Event Types
All transition events implement EntityEvent and contain an entity field:
StateChangeRequest<S>: Request to change state (entity,next)Enter<S>: Enter event (entity,state)Exit<S>: Exit event (entity,state)Transition<S, S>: Transition event (entity,from,to)
Access the entity via trigger.entity (using Deref).
How It Works
When a state change is requested:
apply_state_requestobserver validates the transition- Exit events are triggered
- Transition event is triggered
- State component is updated
- Enter events are triggered
When an FSM component is first added:
on_fsm_addedobserver detects the new component- Enter events are triggered for the initial state
Important: Timing of Initial Enter Events
When an FSM component is added during entity spawn, the initial Enter event fires in the same frame, before the entity is fully initialized.
let entity = commands.spawn.id;
Consider using ignore_fsm_addition() if you don't need initial Enter events:
app.add_plugins;
Testing
use ;
License
Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT License (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Contribution
Contributions are welcome! This crate is part of the MolecularSadism game development libraries.