bevy_rl

Library Listed

Build environments for reinforcement learning with bevy

Checked Β· Sources

Downloads
33
Dependents
1
Stars
104
Forks
3
Contributors
1
Commits
97
Latest release
0.17.0 Β·

About

From the project README Β·

image

Reinforcement Learning for Bevy Engine

Build Reinforcement Learning Gym environments with Bevy engine to train AI agents that can learn from screen pixels or defined obeservation state.

πŸ“Features

  • Set of APIs to implement OpenAI Gym interface, such as reset, step, render and associated simulator states
  • Multi-Agent support
  • Rendering screen pixels to RAM buffer β€” for training agents with raw pixels
  • REST API to control agents

πŸ‘©β€πŸ’» Usage

1. Define Action and Obeservation Space

Observation space needs to be Serializable for REST API to work.

// Action space
#[derive(Default)]
pub struct Actions {
    // actuator_signals: [f32; 3],
}

// Observation space
#[derive(Default, Serialize, Clone)]
pub struct Observations {
    // agent_coords: [(f32, f32); 16],
}
2. Enable AI Gym Plugin

Width and height should exceed 256, otherwise wgpu will panic.

Read the full README
// Setup bevy_rl
let ai_gym_state = AIGymState::<Actions, State>::new(AIGymSettings {
    width: u32,              // Width and height of the screen
    height: u32,             // ...
    num_agents: 1,           // Number of agents β€” each will get a camera handle
    render_to_buffer: false, // You can disable rendering to buffer
    pause_interval: 0.01,    // 100 Hz
    ..default()
});
app.insert_resource(ai_gym_state)
    .add_plugins(AIGymPlugin::<Actions, Observations>::default());
2.1 (Optional) Enable Rendering to Buffer

If your environment wants to export raw pixels, you will need to attach a render target to each camera you want to export them from. Render targets are copied each frame from GPU memory to RAM buffers so that they can be accessed with REST API.

pub(crate) fn spawn_cameras(
    ai_gym_state: Res<AIGymState<Actions, Observations>>,
) {
    let mut ai_gym_state = ai_gym_state.lock().unwrap();
    let ai_gym_settings = ai_gym_state.settings.clone();

    for i in 0..ai_gym_settings.num_agents {
        let render_image_handle = ai_gym_state.render_image_handles[i as usize].clone();
        let render_target = RenderTarget::Image(render_image_handle);
        let camera_bundle = Camera3dBundle {
            camera: Camera {
                target: render_target,  // Render target is baked in bevy_rl and used to export pixels
                priority: -1,           // set to -1 to render at the firstmost pass
                ..default()
            },
            ..default()
        };
        commands.spawn(camera_bundle);
    }
}
4. Handle bevy_rl events

bevy_rl will communicate with your environment through events. Those events initiate from REST API or bevy_rl.SimulationPauseTimer that pauses the simulation with given interval (AIGymSettings.pause_interval).

EventDescriptionUsage
EventResetReset environment to initial stateYou should rebuild your evnironment here
EventControlSwitch to control stateYou should recieve actions here and apply them to your environment (and resume simulation)
EventPausePause environment executionPause physics engine or game clock and take snapshot of your game state

Here's example of how to handle those events:

// EventPauseResume
fn bevy_rl_pause_request(
    mut pause_event_reader: EventReader<EventPauseResume>,
    ai_gym_state: Res<AIGymState<Actions, State>>,
) {
    for _ in pause_event_reader.iter() {
        // Pause simulation (physics engine)
        // ...
        // Collect state into serializable struct
        let env_state = Observations(...);
        // Set bevy_rl gym state
        let mut ai_gym_state = ai_gym_state.lock().unwrap();
        ai_gym_state.set_env_state(env_state);
    }
}

// EventControl
fn bevy_rl_control_request(
    mut pause_event_reader: EventReader<EventControl>,
    mut simulation_state: ResMut<State<SimulationState>>,
) {
    for control in pause_event_reader.iter() {
        let unparsed_actions = &control.0;
        for i in 0..unparsed_actions.len() {
            if let Some(unparsed_action) = unparsed_actions[i].clone() {
                let action: Vec<f64> = serde_json::from_str(&unparsed_action).unwrap();
                // Pass control inputs to your agents
                // ...
            }
        }
        // Resume simulation (physics engine)
        // ...
        // Return to running state; note that it uses pop/push to avoid
        // entering `SystemSet::on_enter(SimulationState::Running)` which initialized game world anew
        simulation_state.pop().unwrap();
    }
}

/// Handle bevy_rl::EventReset
pub(crate) fn bevy_rl_reset_request(
    mut reset_event_reader: EventReader<EventReset>,
    mut commands: Commands,
    mut walls: Query<Entity, &Wall>,
    mut players: Query<(Entity, &Actor)>,
    mut simulation_state: ResMut<State<SimulationState>>,
    ai_gym_state: Res<AIGymState<Actions, Observations>>,
) {
    if reset_event_reader.iter().count() == 0 {
        return;
    }

    // Reset envrionment state here

    // Return simulation in Running state
    simulation_state.set(SimulationState::Running).unwrap();

    // tell bevy_rl that environment is reset and return response to REST API
    let ai_gym_state = ai_gym_state.lock().unwrap();
    ai_gym_state.send_reset_result(true);
}

Register systems to handle bevy_rl events.

app.add_systems(
    Update,
    (
        .with_system(bevy_rl_control_request)
        .with_system(bevy_rl_reset_request)
        .with_system(bevy_rl_pause_request),
    ).in_set(SimulationState::PausedForControl)
);

πŸ’» AIGymState API

Those methods are available on AIGymState resource. You should use them to alter bevy_rl internal state.

MethodDescriptionUsage
set_reward(agent_index: usize, score: f32)Set reward for an agentWhen a certain event happens, you can set reward for an agent.
set_terminated(agent_index: usize, result: bool)Set termination status for an agentOnce your agent is killed, you should set it's status to true. Useful for Multi-agent.
reset()Reset bevy_rl stateYou should call this method when you reset your environment to clear exported state history
set_env_state(state: State)Set current environment stateWhen you serialize your environment state, you should set it here.
send_reset_result(result: bool)Send reset result to REST APIYou should call this method when you have reset your environment to sychronize with REST API

🌐 REST API

Accessing bevy_rl-enabled environment is possible through REST API. Here's a list of available endpoints:

MethodVerbbevy_rl version
Camera PixelsGEThttp://localhost:7878/visual_observations
StateGEThttp://localhost:7878/state
Reset EnvironmentGEThttp://localhost:7878/reset
StepGEThttp://localhost:7878/step?payload=ACTION

bevy_rl_shooter implements an example Python wrapper.

✍️ Examples

Runs on
WebGPU
What shows it
  • WebGPU: bevy_rl dependency wgpu
Native code
None found
  • No native code found
Python bindings
No Python binding crate found
License
Apache-2.0 OR MIT

Compare with similar projects

Order: strongest tier first, then most downloads, then name, with unmeasured values last. A library counts downloads over 30 days. An app counts binary downloads per release. An app with none counts its crate downloads over 30 days. ML Rust runs this catalogue, so its closest project always gets a row.

bevy_rl beside the projects that share a topic with it
ProjectDownloadsDependentsStarsLatest releaseRuns on (GPU)Native codeLicense
bevy_rl This page Listed

Build environments for reinforcement learning with bevy

33 downloads over 30 days 1 104 0.17.0 WebGPU No native code found Apache-2.0 OR MIT
nexar Listed

Distributed runtime that replaces MPI between nodes: point-to-point messages, 11 collectives and RPC over QUIC, with no mpirun or MPI library.

1,191 downloads over 30 days 5 5 0.2.1 CUDA Binds native code: ring. Build uses a native toolchain: bindgen, pkg-config Apache-2.0
rurel Listed Dormant

Flexible, reusable reinforcement learning (Q learning) implementation

150 downloads over 30 days 0 165 0.6.0 GPU support unknown No native code found MPL-2.0
rsrl Listed Dormant

A fast, extensible reinforcement learning framework in Rust

80 downloads over 30 days 0 204 0.8.1 GPU support unknown Binds native code: lapack-sys MIT
gym-rs Listed Dormant

Open AI environments bindings for Rust

75 downloads over 30 days 2 70 2.2.1 GPU support unknown No native code found Apache-2.0 OR MIT
revonet Listed Dormant

Rust implementation of real-coded genetic algorithm for solving optimization problems and training of neural networks. The latter is also known as neuroevolution.

9 downloads over 30 days 0 20 0.2.1 GPU support unknown No native code found GPL-3.0
REnforce App Listed Dormant

Reinforcement learning library written in Rust

Not measured Not applicable 24 Not measured Not measured Not measured None stated
Why ListedNo archive, New or Featured rule places it, so it is Listed.

What it lacks for Featured

  • It has 0 active dependents and 0 new dependents in 365 days. Featured needs 11 active dependents or 9 new dependents in 365 days. The repository route reads 104 stars, 3 forks and 1 contributor. It needs 4,539 stars, 434 forks and 79 contributors together.

What else the check found

  • The last activity is on 22 Jun 2026, 140 days ago. The rules expect an update within 365 days. Dormancy rule

Exact rules: Listed, Featured library. The check on applied thresholds version 9.

Every rule outcome, as the check records it
  • expected-interval: version 0.17.0 base 140, raised to the 365-day floor: E = 365 days
  • dormancy: last activity 2026-06-22, 140 days ago, E = 365: active
  • featured: not featured: active_users 0 < 11 and new_adopters 0 < 9 and repository route stars 104 (needs 4539), forks 3 (needs 434), contributors 1 (needs 79)
  • listed: listed: no archive, new or featured rule applies
Crates2 crates

Measurements and sources

Each signal row shows the raw value, its source and the fetch date. The Sources list gathers every distinct source on this page. Movement needs more history.

Sources6 sources
Adoption0 active dependents, 0 new dependents in 365 days1 not measured
Adoption measurements
SignalValueSourceFetchedCounts
Active dependents0crates.io database dumpYes
New dependents (365 days)0crates.io database dumpYes
Peak active dependents0crates.io database dumpYes
Active dependents as a share of the peak1crates.io database dumpYes
Binary downloads per releaseNot measuredYes
MaintenanceLatest human commit: 4 months ago3 not measured
Maintenance measurements
SignalValueSourceFetchedCounts
Latest human commit2026-06-22GitHub repositoryYes
Latest release2026-06-22crates.io database dumpYes
First release2022-04-25crates.io database dumpYes
Latest version0.17.0crates.io database dumpYes
Days the oldest outside issue waits for a reply0GitHub repositoryYes
Maintainer replies over 365 days0GitHub repositoryYes
Repository archivedfalseGitHub repositoryYes
Repository gonefalseGitHub repositoryYes
Release commit or manifest in the repotruecrates.io packageYes
Published docstruecrates.io database dumpYes
Latest release fails cargo checkNot measuredYes
Build check dateNot measuredYes
Build check toolchainNot measuredYes
SecurityUnpatched vulnerabilities: none
Security measurements
SignalValueSourceFetchedCounts
Unpatched vulnerabilities past the grace windownoneRustSec advisory databaseYes
RustSec unmaintained advisorynoneRustSec advisory databaseYes
Deprecation noticenonecrates.io database dumpYes
Deprecated requirementnonecrates.io database dumpYes
Distribution36 releases, 33 downloads over 30 days5 not measured
Distribution measurements
SignalValueSourceFetchedCounts
Releases36crates.io database dumpYes
Downloads over 30 days33crates.io database dumpYes
Binary downloads over the last 3 releasesNot measuredYes
Homebrew installs on request over 365 daysNot measuredYes
Repology repository familiesNot measuredYes
Docker pull growth over 90 daysNot measuredYes
Docker pulls at the growth baselineNot measuredYes
Every version a prereleasefalsecrates.io database dumpYes
Every version 0.0.xfalsecrates.io database dumpYes
Every version yankedfalsecrates.io database dumpYes
Crate deleted from crates.iofalsecrates.io database dumpYes
Repository104 stars, 3 forks, 1 contributors, 97 commits. Stars alone never qualify a project.
Repository measurements
SignalValueSourceFetchedCounts
GitHub stars104GitHub repositoryYes β€” one part of the repository route
GitHub forks3GitHub repositoryYes β€” one part of the repository route
Contributors with a GitHub account1GitHub contributorsYes β€” one part of the repository route
Commits on the default branch97GitHub repositoryNo
Repository's main crate left out of the entrynonecrates.io database dumpYes
Facts that never count
Facts that never count measurements
SignalValueSourceFetchedCounts
LicenseApache-2.0 OR MITcrates.io database dumpNo
Runs on, from listed crates and their direct dependenciesWebGPUcrates.io database dumpNo
What shows each backendWebGPU: bevy_rl dependency wgpucrates.io database dumpNo
Native code, from listed crates and their dependenciesNo native code foundcrates.io database dumpNo
Python bindings, from direct dependencies of listed cratesNo Python binding crate foundcrates.io database dumpNo
Crates that depend on a listed crate directly1crates.io database dumpNo

Corrections and review history

Report a correction

No correction applies to this entry.

Judgments

A judgment enters only through an approved issue and a merged pull request.

No judgment applies to this entry.