A straightforward stateful input manager for the Bevy game engine.



A simple but robust input-action manager for Bevy: intended to be useful both as a plugin and a helpful library.

Inputs from various input sources (keyboard, mouse and gamepad) are collected into a common ActionState on your player entity, which can be conveniently used in your game logic.

The mapping between inputs and actions is many-to-many, and easily configured and extended with the InputMap components on your player entity. A single action can be triggered by multiple inputs (or set directly by UI elements or gameplay logic), and a single input can result in multiple actions being triggered, which can be handled contextually.

This library seamlessly supports both single-player and local multiplayer games! Simply add the InputManagerBundle to each controllable entity, and customize the InputMap and AssociatedGamepad values appropriately.


Getting started

  1. Add leafwing-input-manager to your Cargo.toml.
  2. Create an enum of the logical actions you want to represent, and implement the Actionlike trait for it.
  3. Add the InputManagerPlugin to your App.
  4. Add the InputManagerBundle to your player entity (or entities!).
  5. Configure a mapping between your inputs and your actions by modifying the InputMap component on your player entity.
  6. Read the ActionState component on your player entity to check the collected input state!

Running your game

Use cargo run. This repo is set up to always build with full optimizations, so there's no need for a --release flag in most cases. Dynamic linking is enabled to ensure build times stay snappy.

To run an example, use cargo run --example_name, where example_name is the file name of the example without the .rs extension.


This repository is open to community contributions! There are a few options if you'd like to help:

  1. File issues for bugs you find or new features you'd like.
  2. Read over and discuss issues, then make a PR that fixes them. Use "Fixes #X" in your PR description to automatically close the issue when the PR is merged.
  3. Review existing PRs, and leave thoughtful feedback. If you think a PR is ready to merge, hit "Approve" in your review!

Any contributions made are provided under the license(s) listed in this repo at the time of their contribution, and do not require separate attribution.


  1. Use doc tests aggressively to show how APIs should be used. You can use # to hide setup code from the doc tests, but use this sparingly.
  2. Unit test belong near the code they are testing. Use #[cfg(test)] on the test module to ignore it during builds, and #[test] on the test functions to ensure they are run.
  3. Integration tests should be stored in the top level tests folder, importing functions from lib.rs.

Use cargo test to run all tests.


The CI will:

  1. Ensure the code is formatted with cargo fmt.
  2. Ensure that the code compiles.
  3. Ensure that (almost) all clippy lints pass.
  4. Ensure all tests pass on Windows, MacOS and Ubuntu.

Check this locally with:

  1. cargo run -p ci
  2. cargo test --workspace

To manually rerun CI:

  1. Navigate to the Actions tab.
  2. Use the dropdown menu in the CI run of interest and select "View workflow file".
  3. In the top-right corner, select "Rerun workflow".


Documentation is handled with standard Rust doc strings. Use cargo doc --open to build and then open the docs locally.

  • Add Support For Gamepad Axes

    Add Support For Gamepad Axes

    This should be functionally ready, but I haven't necessarily done my due diligence with the doc strings or self-review yet. I also need to add a new example demonstrating the use of the controller axes. I'll get to that tomorrow probably.

    The PR ended up somewhat large, but the changes are pretty straight-forward.

    • Now every event kind has a "value". This will be between -1.0 and 1.0.
    • If you wanted to implement simple character bindings with an analog stick you could do this:
    let input_map = InputMap::<PlayerAction>::new(vec![
            GamepadAxisThreshold {
                axis: GamepadAxisType::LeftStickX,
                comparison: GamepadAxisComparison::Greater,
                threshold: 0.1,
            GamepadAxisThreshold {
                axis: GamepadAxisType::LeftStickX,
                comparison: GamepadAxisComparison::Less,
                threshold: -0.1,
            GamepadAxisThreshold {
                axis: GamepadAxisType::LeftStickY,
                comparison: GamepadAxisComparison::Greater,
                threshold: 0.1,
            GamepadAxisThreshold {
                axis: GamepadAxisType::LeftStickX,
                comparison: GamepadAxisComparison::Less,
                threshold: -0.1,
    • In order to get the f32 threshold to fit inside of the Eq + Hash UserInput struct, I made use of the decorum crate, which has suitable Eq and Hash implementations for floats. Technically we can get rid of the dependency if you want to copy the to_canonical_bits function from the decorum crate.

    We really just need a way to hash and compare floats and that would give us what we need.

    opened by zicklag 33
  • Refactor rotation

    Refactor rotation

    • [x] I based this off of #151, so I'm going to wait for that to get merged, resolve any conflicts, and then take this out of "draft" status. At that point the diff will become much, much smaller.
    • [x] Also, I still need to go over this again and see if I missed any documentation or examples that need to be updated.
    • [x] @alice-i-cecile QUESTION: #151 itself seems to be branched off of the dev branch. Should I change the merge target to the dev branch instead of the main branch? (Yes, done)

    Resolves #155

    usability breaking-change 
    opened by CleanCut 11
  • macros crate name v macros dependency

    macros crate name v macros dependency



    Operating system & version

    macOs 12.4

    What you did

    cargo build

    (I was actually trying to run examples but it's easy to reproduce with just cargo build)

    What you expected to happen

    a successful build 😄

    What you think should've happened if everything was working properly.

    a successful build 😄

    What actually happened

    > $ cargo build                                                                                   ⬡ system [±main ✓]
    error: no matching package found
    searched package name: `leafwing_input_manager_macros`
    perhaps you meant:      leafwing-input-manager-macros
    location searched: /Users/user/workspace/leafwing-input-manager/macros
    required by package `leafwing-input-manager v0.4.0 (/Users/user/leafwing-input-manager)`

    Additional information

    Hello! 👋

    I could definitely be doing something wrong this morning 😅 But it looks like this was missed in the last commit on master. I'm raising this as an issue instead of a PR because it looks like dev is using _ and I'm not exactly sure what the flow is for bugs on main vs things that should be in PR'd to dev.


    opened by shnewto 7
  • Support Single Axis Inputs in Virtual DPads

    Support Single Axis Inputs in Virtual DPads

    This makes sure that SingleAxis inputs will report a value of 0.0 if the axis value is not withing the triggering zone. This makes it possible to create a virtual gamepad made up of multiple single-axis inputs on the same stick.

    I ran into a scenario where in my game input mapping screen I want to map inputs to player actions such as move up, move down, etc., in a table like most game input screens are laid out. This scenario meant that it's easier to structure the input binding as a virtual DPad, even when the player is inputting analog stick directions, and they could technically put in different axes for different directions.

    This commit makes the Virtual DPad output a proper axis pair data even when it contains single axis inputs for some or all of the directions. Before this commit you would end up getting a value of -2.0 when you would move left or down on the stick, and a value of 0.0 when moving the stick right or up, because the input_value() function was returning a value for both left and right stick movement, even though only one of them should be triggering at a time.

    opened by zicklag 6
  • Press duration example not working - Previous Duration and Current Duration methods always return zero

    Press duration example not working - Previous Duration and Current Duration methods always return zero


    0.3.0 Bevy 0.7.0

    Operating system & version

    Windows 10

    What you did

    I tried running the press_duration example.

    What you expected to happen

    From my understating, the player object should "leap" when the left/right key is released, with the leap distance dependent on the length of the keypress.

    What actually happened

    The player object didn't move at all.

    Additional information

    I've narrowed down the problem to the previous_duration and the current_duration functions always returning zero.

    A minimal example of the issue (edited minimal example)

    use bevy::prelude::*;
    use leafwing_input_manager::prelude::*;
    fn main() {
    #[derive(Actionlike, PartialEq, Eq, Clone, Copy, Hash, Debug)]
    enum Action {
    struct Player;
    fn spawn_player(mut commands: Commands) {
            .insert_bundle(InputManagerBundle::<Action> {
                action_state: ActionState::default(),
                input_map: InputMap::new([(Action::Jump, KeyCode::Space)]),
    fn jump(query: Query<&ActionState<Action>, With<Player>>) {
        let action_state = query.single();
        if action_state.just_released(Action::Jump) {
            info!("End jump time: {:?}", action_state.previous_duration(Action::Jump));
        if action_state.pressed(Action::Jump) {
            info!("Current jump time: {:?}", action_state.current_duration(Action::Jump));
        if action_state.just_pressed(Action::Jump) {
            info!("Started jump!");
    opened by Redwoodstudio 6
  • Multiple Gamepads Bound to One InputMap

    Multiple Gamepads Bound to One InputMap

    What problem does this solve?

    In my game I have a camera or a menu, and I want any gamepad to be able to control the menu/camera, but I can only specify one gamepad to be bound to the input map.

    What solution would you like?

    I would like to be able to specify multiple gamepads that are allowed to send inputs to the InputMap. Something like InputMap::new().set_gamepads(&[GamePad(0), Gamepad(1)]).

    [Optional] How could this be implemented?

    We could probably switch the associated gamepad on InputMap to be a PetitSet of gamepads instead of an Option and then we would just need to loop through all gamepads when testing inputs.

    [Optional] What alternatives have you considered?

    ~~I could get around this~~ I tried to get around this by adding input maps to a different entity than my camera, adding an input map for each of the gamepads, and then looping through those entities to collect camera movement input, instead of reading from a component on my camera itself.

    This issue with this approach is that inputs are detected multiple times if they come from the keyboard, because each ActionState will register the keyboard button as pressed independently, making it impractical to just loop over multiple entities with event handlers for each remote.

    Related work

    None yet!

    good first issue usability 
    opened by zicklag 5
  • Updated to bevy 0.8 dev

    Updated to bevy 0.8 dev

    I didn't know if the dev branch was scheduled to be merged with the main branch when bevy updates to 0.8. So I forked from main. Let if know if it should be otherwise.

    Dependencies should of course be updated when bevy gets updated.

    Currently using this pr branch to update bevy_dolly with main and cart:camera-viewports

    opened by BlackPhlox 5
  • Handle 2-dimensional analogue input

    Handle 2-dimensional analogue input

    There are three main use patterns for this:

    1. Partition the analog space into digital buttons
      • Handle with a mapping API that sets thresholds/boundaries for the values
    2. Map analog values into actions, but still use the raw analog value for something (like smoothly changing walk/run on an analog stick)
      • Actually a special case of 1: You map onto an action, but then get back the analog value that generated that action
        • This can be done with reverse mapping, or with forward propagation
      • Destructuring this value ain't gonna happen
      • But we can return out the values from the virtual button if we store it
      • We actually don't need to return the value itself, just the user input that triggered it (since then the caller can look up the correct value themselves)
      • Requires #76
    3. Mapping analog values to an action simply does not make sense (flight stick tilt)
      • Not our problem- can add helper features to bevy_input if necessary
        • In particular, "get a Vec2 from a particular axis" is an extremely common operation that is unnecessarily painful

    Eventually this should support:

    • all gamepad axes
    • touch emulated joysticks
    • mouse emulated joysticks
    enhancement controversial 
    opened by alice-i-cecile 5
  • Run clippy on examples in CI

    Run clippy on examples in CI

    Closes #201.

    Note that this doesn't run the examples, but I think running clippy on them gives the best ROI.

    Actually running may have several disadvantages:

    • It's hard to set up, you'd probably need to get a list of all examples somehow.
    • It could reduce CI times substantially, depending on the timeout.
    • It is flaky, sometimes it might catch an error and sometimes not.

    Also note that this way of setting up CI has the disadvantage that it's not run in parallel. This might not scale well, especially with the examples now.

    build system 
    opened by TimJentzsch 4
  • Optional Non-Entity Resource for handling inputs

    Optional Non-Entity Resource for handling inputs

    This incorporates a monomorphizable resource that operates the same as the input component. This allows for input handling not attached to an entity.


    #[derive(Actionlike, Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
    pub enum DebugAction {
    impl DebugAction {
        pub fn input_resource() -> InputResource<Self> {
            let mut res = InputResource::default();
                .insert(DebugAction::ToggleConsole, KeyCode::F1);
                .insert(DebugAction::ToggleOverlay, KeyCode::F2);
                .insert(DebugAction::TogglePathing, KeyCode::F3);
    #[derive(Debug, Default)]
    pub struct DebugState {
        text_overlay: Option<TextOverlay>,
    struct TextOverlay {
        position: Entity,
        clock: Entity,
    /// Should be called once to setup Gameplay Mode
    pub fn handle_input(
        mut commands: Commands,
        mut debug_state: ResMut<DebugState>,
        mut asset_server: ResMut<AssetServer>,
        mut input_resource: Res<InputResource<DebugAction>>,
    ) {
        if input_resource
            if let Some(text_overlay) = &mut debug_state.text_overlay {
                trace!("Disable Debug Overlay");
                debug_state.text_overlay = None;
            } else {
                trace!("Enable Debug Overlay");
                debug_state.text_overlay = Some(TextOverlay {
                    position: commands
                        .spawn_bundle(TextBundle {
                            text: Text::with_section(
                                TextStyle {
                                    font: asset_server.load("fonts/spacemono.ttf"),
                                    font_size: 30.0,
                                    color: Color::WHITE,
                                TextAlignment {
                                    vertical: VerticalAlign::Top,
                                    horizontal: HorizontalAlign::Left,
                    clock: commands
                        .spawn_bundle(TextBundle {
                            text: Text::with_section(
                                TextStyle {
                                    font: asset_server.load("fonts/spacemono.ttf"),
                                    font_size: 30.0,
                                    color: Color::WHITE,
                                TextAlignment {
                                    vertical: VerticalAlign::Bottom,
                                    horizontal: HorizontalAlign::Right,
    opened by huhlig 4
  • Release 0.3

    Release 0.3

    This release is planned to center around:

    • ~~handling game stick inputs~~
    • assorted usability improvements
    • clash handling


    • Closes #73

    Critical questions

    • [x] if we gather axis data into an AxisPair struct, where is it stored?
      • Store as Res<AxisPairs<Gamepad>>, with the ability to extend to other simulated axes
    • [x] how are axis-like inputs used to set ActionState?
      • new UserInput variant based on direction partition clamping
      • set value of variant based on AxisPair direction / vec2 / rotation
    • [x] if we want to store data in actionlike variants, how do we generate a variant-only Hash impl?
      • This isn't feasible: see #60. We also no longer need Hash 😄
    • [x] do we care, at all, about non-enum Actionlike types? If yes, how do we refactor to support this?
      • Nope! See below.
    bug documentation enhancement code-quality usability example 
    opened by alice-i-cecile 4
  • Constant spam about missed events

    Constant spam about missed events



    What you did

    Run the mouse_motion example.

    What actually happened

    The console is flooded with messages:

    2022-11-13T19:35:54.363800Z WARN bevy_ecs::event: Missed 345 bevy_input::mouse::MouseMotion events. Consider reading from the EventReader more often (generally the best solution) or calling Events::update() less frequently (normally this is called once per frame). This problem is most likely due to run criteria/fixed timesteps or consuming events conditionally. See the Events documentation for more information.


    Silence these notifications by replacing your DefaultPlugins with:

    DefaultPlugins.build().set(LogPlugin {
      filter: "wgpu=error,bevy_ecs::event=error".to_string(),

    Huge thanks to Martin for nailing this down: https://discordapp.com/channels/691052431525675048/1034547742262951966/1043564180306919514.

    Additional information

    This is due to https://github.com/bevyengine/bevy/pull/5730, as discussed in https://github.com/bevyengine/bevy/issues/6589.

    I suspect we're also double-counting events.

    opened by alice-i-cecile 1
  • Handling of enum with sub-variants

    Handling of enum with sub-variants


    I am not sure if this is a bug/limitation of the current implementation but I encounter some issues with the Actionlike macro being used with "complex" enums: it seems to construct them by default, thus loosing the information.

    #[derive(PartialEq, Default, Eq, Clone, Debug, Hash, Copy)]
    enum ActionBars { #[default] AB0, AB1 }
    #[derive(Actionlike, PartialEq, Eq, Clone, Debug, Copy)]
    enum GameAction { Jump, ActionBar(ActionBars), Test(i32) }

    First I was "forced" to add the Default trait to my ActionBars enum, but when running, it seems that everytime an ActionBar or the Test variant of the enum was triggered, it was reset to default when passed to the ActionState.

    Code that spawns the bundle

            player: Player,
            input_map: InputMap::new([
                (KeyCode::Key1, GameAction::ActionBar(ActionBars::AB0)),
                (KeyCode::Key2, GameAction::ActionBar(ActionBars::AB1)),
                (KeyCode::E, GameAction::Test(42)),
                (KeyCode::Space, GameAction::Jump)
            ability_action_state: ActionState::default(),
            actionbar_map: actionbars_mapping

    Actually reading the action:

    fn print_abilities(query: Query<(&ActionState<GameAction>, &ActionBarsMap)>) {
        for (ability_state, mapping) in query.iter() {
            for ability in ability_state.get_just_pressed() {
                if let GameAction::ActionBar(ab) = ability {
                    let spell_id = mapping.get(&ab);
                } else {

    And in the case of subtypes, it always prints either ActionBars(AB0) or Test(0), even when it should have printed ActionBars(AB1) or Test(42).

    Is this a limitation or did I miss something to make this work?

    If it is a limitation, I would like to have this as an enhancement if possible.

    Thank you

    opened by Mazrog 3
  • Chords don't work with `DualAxis`

    Chords don't work with `DualAxis`


    0.5.2 and HEAD

    Operating system & version


    What you did

    Change examples/mouse_motion.rs:

    - .insert(DualAxis::mouse_motion(), CameraMovement::Pan)
    + .insert_chord(
    +     [
    +         InputKind::Mouse(MouseButton::Left),
    +         InputKind::DualAxis(DualAxis::mouse_motion()),
    +     ],
    +     CameraMovement::Pan,
    + )

    and to prevent unwrap() from panicking:

    - let camera_pan_vector = action_state.axis_pair(CameraMovement::Pan).unwrap();
    - // Because we're moving the camera, not the object, we want to pan in the opposite direction
    - camera_transform.translation.x -= CAMERA_PAN_RATE * camera_pan_vector.x();
    - camera_transform.translation.y -= CAMERA_PAN_RATE * camera_pan_vector.y();
    + if let Some(camera_pan_vector) = action_state.axis_pair(CameraMovement::Pan) {
    +     // Because we're moving the camera, not the object, we want to pan in the opposite direction
    +     camera_transform.translation.x -= CAMERA_PAN_RATE * camera_pan_vector.x();
    +     camera_transform.translation.y -= CAMERA_PAN_RATE * camera_pan_vector.y();
    + }

    then cargo run --example mouse_motion.

    What you expected to happen

    Can pan with Left mouse button.

    What actually happened


    opened by djeedai 0
  • Ensure API parity between `InputStreams` and `MutableInputStreams`

    Ensure API parity between `InputStreams` and `MutableInputStreams`

    Which code could be improved?

    This API is subtly different: for example, InputStreams::pressed does not exist, while MutableInputStreams::pressed does.

    How should this be changed?

    We could either verify this manually or introduce a common trait.

    opened by alice-i-cecile 0
A tiling window manager for Windows

komorebi Tiling Window Management for Windows. About komorebi is a tiling window manager that works as an extension to Microsoft's Desktop Window Mana

Jade (جاد) 2.4k Dec 1, 2022
A floating, tag-based window manager written in Rust

worm worm is a floating, tag-based window manager for X11. It is written in the Rust programming language, using the X11RB library. Install cargo buil

null 608 Nov 26, 2022
Generic tiling window manager library in Rust

Pop Tiler Generic tiling window manager library for Rust, using an architecture based on GhostCell. License Licensed under the GNU Lesser General Publ

Pop!_OS 64 Jul 27, 2022
A window manager coded in rust

Tailwin A window manager coded in rust Thanks Victoruler for making the logo under a cc-by licence.

Arthur Melton 3 Jul 27, 2022
A cross-platform Mod Manager for RimWorld intended to work with macOS, linux and Windows

TODOs are available here. Discussions, PRs and Issues are open for anyone who is willing to contribute. rrm Inspired by Spoons rmm. This is a cross-pl

Alejandro Osornio 7 Sep 5, 2022
Experimental package manager/system configurator for system hoppers

mascara An experimental package manager/config initializer tool for system hoppers. mascara.toml [mascara] feature = "Debian" logs = { stdout = "blue"

Ethan Gallucci 1 Apr 15, 2022
skyWM is an extensible tiling window manager written in Rust

skyWM is an extensible tiling window manager written in Rust. skyWM has a clear and distinct focus adhering to the KISS and Unix philosophy.

Chadano 62 Sep 19, 2022
A Modern, Open Source GTK4 ebook manager powered by Rust.

Bookx An MVP in progress: An ebook reader with .epub support Context menu for each book (delete, rename book, info) On click switch the carousal to th

Anurag Dhadse 11 Nov 18, 2022
An easy to use command line project manager for projects using the ReCT programming language

☢️ A powerful project manager for the ReCT programming language! ☢️ ReCTx makes your projects easier to manage by allowing you to configure everything

Remy 2 Nov 20, 2022
♾️ Fast & Simple AppImage manager

⚠️ Heavily in development (Not working) Leap Fast & Simple AppImage manager What's working Installation (github only, info about app not stored yet) R

lynx 5 Nov 14, 2022
A chess engine written from scratch in Rust ♞

Walleye Walleye is a chess engine written completely in rust. Walleye is a UCI-compatible engine written using the classical alpha-beta style AI. It s

Mitchel Paulin 95 Nov 19, 2022
Honkers Launcher variant written on Rust, GTK4 and libadwaita, using Anime Game Core library

You could also try the main branch Development Folder Description ui Blueprint UI files ui/.dist UI files compiled by the blueprint src Rust source co

An Anime Team 9 Nov 2, 2022
Easy c̵̰͠r̵̛̠ö̴̪s̶̩̒s̵̭̀-t̶̲͝h̶̯̚r̵̺͐e̷̖̽ḁ̴̍d̶̖̔ ȓ̵͙ė̶͎ḟ̴͙e̸̖͛r̶̖͗ë̶̱́ṉ̵̒ĉ̷̥e̷͚̍ s̷̹͌h̷̲̉a̵̭͋r̷̫̊ḭ̵̊n̷̬͂g̵̦̃ f̶̻̊ơ̵̜ṟ̸̈́ R̵̞̋ù̵̺s̷̖̅ţ̸͗!̸̼͋

Rust S̵̓i̸̓n̵̉ I̴n̴f̶e̸r̵n̷a̴l mutability! Howdy, friendly Rust developer! Ever had a value get m̵̯̅ð̶͊v̴̮̾ê̴̼͘d away right under your nose just when

null 293 Sep 22, 2022
Minecraft-esque voxel engine prototype made with the bevy game engine. Pending bevy 0.6 release to undergo a full rewrite.

vx_bevy A voxel engine prototype made using the Bevy game engine. Goals and features Very basic worldgen Animated chunk loading (ala cube world) Optim

Lucas Arriesse 108 Nov 22, 2022
dlfile is a very simple, straightforward CLI to download a file

dlfile dlfile is a very simple, straightforward CLI to download a file, except it... Only downloads over HTTPS Requires TLS 1.2 or higher Executes in

Colin 3 Oct 21, 2022
Fish Game for Macroquad is an online multiplayer game, created as a demonstration of Nakama, an open-source scalable game server, using Rust-lang and the Macroquad game engine.

Fish Game for Macroquad is an online multiplayer game, created as a demonstration of Nakama, an open-source scalable game server, using Rust-lang and the Macroquad game engine.

Heroic Labs 128 Nov 28, 2022
2d Endless Runner Game made with Bevy Game Engine

Cute-runner A 2d Endless Runner Game made with Bevy Game Engine. Table of contents Project Infos Usage Screenshots Disclaimer Project Infos Date: Sept

JoaoMarinho 2 Jul 15, 2022
A game of snake written in Rust using the Bevy game engine, targeting WebGL2

Snake using the Bevy Game Engine Prerequisites cargo install cargo-make Build and serve WASM version Set your local ip address in Makefile.toml (loca

Michael Dorst 0 Dec 26, 2021
A game made in one week for the Bevy engine's first game jam

¿Quien es el MechaBurro? An entry for the first Bevy game jam following the theme of "Unfair Advantage." It was made in one week using the wonderful B

mike 19 Nov 28, 2022
Small and simple stateful applications, designed to facilitate the monitoring of unwanted behaviors of the same.

Violet Violet é um pequeno e simples monitorador de aplicação, voltado para receber eventos de erro e estado. Instalação simples: Dependencias: Docker

Lucas Mendes Campos 3 Jun 4, 2022