Bevy Integration
The elevator-bevy crate is a Bevy 0.18 binary that wraps the core simulation with 2D rendering, a HUD, AI passengers, and keyboard controls. It serves as both a visual debugger for testing dispatch strategies and a reference implementation for integrating elevator-core into a game engine.
Running the Bevy app
With the default config:
cargo run
With a custom config:
cargo run -- assets/config/space_elevator.ron
The app reads a RON config file, creates a Simulation, and renders the building in a 2D view with elevator cars, rider dots, and a metrics HUD.
Plugin architecture
The integration is built around a single Bevy plugin:
pub struct ElevatorSimPlugin;
When you add this plugin to a Bevy app, it:
- Loads config from a RON file (CLI argument or
assets/config/default.ron) - Creates a
Simulationand inserts it as theSimulationResresource - Inserts
SimSpeedresource for controlling simulation speed - Registers the
EventWrappermessage for bridging sim events to Bevy - Adds systems for ticking the sim, rendering, AI passengers, input, and HUD
Key resources
SimulationRes
The core simulation is wrapped in a Bevy resource:
#[derive(Resource)]
pub struct SimulationRes {
pub sim: Simulation,
}
Any Bevy system can access the simulation through Res<SimulationRes> (read) or ResMut<SimulationRes> (write).
SimSpeed
Controls how many simulation ticks run per Bevy frame:
#[derive(Resource)]
pub struct SimSpeed {
pub multiplier: u32,
}
multiplier: 0– simulation is pausedmultiplier: 1– one tick per frame (normal speed)multiplier: 10– ten ticks per frame (fast forward)
The built-in input system maps keyboard keys to speed changes.
EventWrapper
Core simulation events are bridged into Bevy’s message system:
#[derive(Message)]
pub struct EventWrapper(pub Event);
Bevy systems can read simulation events using MessageReader<EventWrapper>:
fn my_system(mut events: MessageReader<EventWrapper>) {
for EventWrapper(event) in events.read() {
match event {
Event::RiderExited { rider, stop, tick, .. } => {
// React to rider arrival in Bevy-land.
}
_ => {}
}
}
}
The tick system
The bridge between elevator-core and Bevy is a single system that runs each frame:
pub fn tick_simulation(
mut sim: ResMut<SimulationRes>,
speed: Res<SimSpeed>,
mut events: MessageWriter<EventWrapper>,
) {
for _ in 0..speed.multiplier {
sim.sim.step();
}
for event in sim.sim.drain_events() {
events.write(EventWrapper(event));
}
}
It steps the simulation multiplier times, then drains all events and re-emits them as Bevy messages. This is the only point where the core simulation and Bevy synchronize.
Writing custom Bevy systems
To add your own gameplay systems that interact with the simulation, access SimulationRes:
use bevy::prelude::*;
use elevator_bevy::sim_bridge::SimulationRes;
use elevator_core::prelude::*;
fn print_metrics(sim: Res<SimulationRes>) {
let m = sim.sim.metrics();
if sim.sim.current_tick() % 3600 == 0 {
println!(
"Minute {}: delivered={} avg_wait={:.0}",
sim.sim.current_tick() / 3600,
m.total_delivered(),
m.avg_wait_time(),
);
}
}
Register your system in the Bevy app after the plugin:
app.add_plugins(ElevatorSimPlugin)
.add_systems(Update, print_metrics);
Module layout
The elevator-bevy crate is organized into focused modules:
| Module | Responsibility |
|---|---|
plugin.rs | ElevatorSimPlugin – loads config, creates sim, registers everything |
sim_bridge.rs | SimulationRes, SimSpeed, EventWrapper, tick system |
rendering.rs | 2D visualization of the building, elevators, and riders |
ui.rs | HUD overlay showing metrics and simulation state |
camera.rs | Camera setup sized to the building |
input.rs | Keyboard controls for speed adjustment |
passenger_ai.rs | Timer-based passenger spawning |
When to use elevator-bevy vs. building your own
Use elevator-bevy if you want a quick visual test of your dispatch strategy or config. Run it, watch the elevators move, tweak parameters.
Build your own if you are making a game. The Bevy crate is intentionally simple – it is a reference, not a framework. Copy the patterns you need (the SimulationRes resource, the tick system, the event bridge) into your own Bevy app and build your game systems around them.
The core library does not depend on Bevy at all. You can use it with any Rust game engine, a TUI, a web frontend via WASM, or pure headless batch simulation.