⠀⠀⠀⠀⠀⠀⢸⡆⠀⣇⠀⣷⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⠀⠀⠀⠀⠀⠀⠀⢻⣧⣄⠀⠀⠀⠀⠀⠀⠀⢠⣼⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢻⣧⡀⠀⠀⠀⠀⠀⠀⠀⢰⡿⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⢠⣼⠀⢸⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⢸⣇⡀⣿⡆⠘⢻⣧⡄⠀⠀⠀⠀⠀⠀⠀⢸⡏⠀⠀⠀⠀⠀⠀⠀⠈⠹⢿⣧⡄⠀⠀⠀⠀⢠⣼⡏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢻⣧⡄⠀⠀⠀⠀⠀⢰⡿⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⢿⡇⠀⢠⣼⡏⠀⣸⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠘⢻⡇⢸⡇⠀⠘⢻⣷⡄⠀⠀⠀⠀⠀⠀⢸⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠻⣿⣦⡀⠀⢠⣾⡏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢻⣧⡄⠀⠀⠀⢸⡿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣧⢀⣼⡏⠁⢰⣿⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⢸⣧⣼⡇⠀⠀⠀⠹⢿⣆⡀⠀⠀⠀⢀⣼⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢻⣷⣤⣾⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢻⣧⡄⠀⣸⡿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢻⣸⡟⠃⠀⢸⡇⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⢹⣿⣧⠀⠀⠀⠀⠘⢻⣧⡄⠀⠀⢸⡟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣤⣤⣴⣿⣿⣿⣿⣿⣿⣿⣷⣶⣶⣶⣦⣤⣤⣤⣤⣤⣤⣤⣤⣈⣹⣧⣸⣟⣣⣤⣤⣤⣤⣤⣤⣴⣶⣶⣾⣿⣿⣿⣿⣿⡇⠀⠀⢸⡇⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢿⣿⠀⠀⠀⠀⠀⠈⢻⣷⡆⠀⢸⡇⠀⠀⠀⠀⢀⣠⣤⣴⣶⣿⣿⡿⠟⠛⠋⢹⣧⡈⠉⠉⠉⠙⠛⠛⠛⠛⠛⠛⠛⠻⠿⠿⠿⠿⣿⣿⣿⣿⣿⡿⠿⠿⠿⠟⠛⠛⠛⠛⠛⠋⠉⢩⣽⡟⠸⢿⡆⢠⣾⡇⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⠀⠀⠀⠀⠀⠀⠀⠸⢿⣧⣸⣇⣠⣤⣴⣾⣿⣿⠿⠛⠛⠋⠁⠀⠀⠀⠀⠈⢻⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⣼⡿⠋⢹⣇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣼⡟⠁⠀⠸⢿⣾⡟⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢻⣀⣠⣤⣤⣤⣤⣤⣤⣾⣿⣿⣿⣿⡟⠛⠋⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣧⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣰⣾⡟⠋⠁⠀⠘⢻⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⣾⠏⠀⠀⠀⠀⣸⣿⡇⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⡿⠿⠟⠛⠛⠛⠛⠛⢻⣿⢿⣿⣧⣤⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢻⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣾⡟⠋⠁⠀⠀⠀⠀⠸⣷⡄⠀⠀⠀⠀⠀⠀⢀⣸⡿⠃⠀⠀⢠⣴⣾⣿⠟⠃⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⠻⢿⣧⣄⠀⠀⠀⠀⠀⠘⢻⡇⠘⠛⠻⢿⣷⣦⣤⡄⠀⠀⠀⠀⠀⠀⠀⠀⢸⣧⡀⠀⠀⠀⠀⠀⠀⢠⣴⣾⡟⠋⠀⠀⠀⠀⠀⠀⠀⠀⢸⣧⡀⠀⠀⠀⠀⢠⣼⡟⠃⢀⣠⣼⣿⡿⠟⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠹⢿⣧⣄⡀⠀⠀⠀⢸⣷⡄⠀⠀⠀⠙⠛⠻⣿⣷⣦⣤⡀⠀⠀⠀⠀⠈⢻⡇⠀⠀⠀⠀⢠⣴⣾⡟⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢻⡇⠀⠀⠀⢠⣼⡟⢡⣴⣾⣿⡿⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⢻⣷⣄⡀⠀⠀⢸⣧⡀⠀⠀⠀⠀⠀⠈⠙⠛⢻⣿⣷⣦⣄⡀⠀⢸⣧⠀⠀⢠⣼⣿⡟⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢿⡆⠀⢰⣾⣯⣼⣿⣿⡿⠏⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⢻⣷⣦⡀⠘⢻⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠻⢿⣿⣷⣮⣿⣠⣼⣿⡟⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣧⣸⣿⣿⣿⣿⡿⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢻⣷⣦⡜⢿⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⣤⣬⣽⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡟⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢻⣿⣾⣧⠀⠀⢀⣠⣤⣤⣴⣾⣿⣿⡿⠿⠟⠛⠋⠉⠸⣷⡆⠈⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⢉⣩⣽⣿⣿⡟⣻⣿⣿⡟⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⠻⣿⣾⣿⣿⣿⣿⣟⡛⠛⠉⠁⠀⠀⠀⠀⠀⠀⠀⠸⢿⣆⠀⠀⠀⠀⠀⠀⠀⢀⣠⣴⣾⣿⣿⣿⣯⣥⣼⣿⡟⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⠛⠛⠻⠿⢿⣿⣿⣿⣷⣶⣦⣤⣤⣤⣄⡀⠀⠀⠘⢿⣇⡀⠀⢀⣠⣤⣼⣿⣿⣿⣿⣿⣿⡿⠿⠟⠛⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠙⠛⠛⠻⠿⢿⣿⣿⣿⣷⣶⣾⣿⣧⣴⣿⣿⣿⣿⡿⠟⠛⠛⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠙⠛⠛⠛⠿⠛⠛⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
Gamepad support for Bevy on macOS through Apple's Game Controller framework.
Use it if your controller shows up as connected in Bevy on macOS but nothing happens when you press buttons or move the sticks (typical with Xbox controllers on macOS 14+):
INFO bevy_input::gamepad: Gamepad 450v0 connected.
WARN gilrs::gamepad: No mapping found for UUID 03000000-5e04-0000-ea02-000014050000
LocationID
(Failed to get location id of device … Skipping).This crate reads controllers with the Game Controller framework (the API Apple recommends, and the
one browsers use, which is why the web gamepad viewer
works while the native one doesn't) and feeds them into Bevy's standard gamepad pipeline: it
writes the same raw events bevy_gilrs writes. So everything else works unchanged: the Gamepad
component, GamepadSettings dead zones, pressed / just_pressed, left_stick(), etc.
No app bundle and no system setting is needed.
[dependencies]
bevy = "0.20"
bevy_apple_gamepad = { path = "crates/bevy_apple_gamepad" } # or git / crates.io once published
use bevy::prelude::*;
use bevy_apple_gamepad::AppleGamepadPlugin;
fn main() {
let mut app = App::new();
// On macOS, replace gilrs (otherwise you get a second gamepad that never moves).
#[cfg(target_os = "macos")]
app.add_plugins(DefaultPlugins.build().disable::<bevy::gilrs::GilrsPlugin>());
#[cfg(not(target_os = "macos"))]
app.add_plugins(DefaultPlugins);
// No-op on other platforms, so it's safe to add unconditionally.
app.add_plugins(AppleGamepadPlugin::default());
app.run();
}
Then read gamepads the usual Bevy way:
fn read_gamepads(gamepads: Query<&Gamepad>) {
for gamepad in &gamepads {
let movement = gamepad.left_stick();
if gamepad.just_pressed(GamepadButton::South) {
// jump!
}
}
}
AppleGamepadPlugin {
// macOS only delivers controller input to the focused app; set this to keep reading it
// while the window is in the background.
monitor_background_events: true,
}
Bevy names buttons by position. With Xbox labels:
| Controller | Bevy GamepadButton / GamepadAxis |
|---|---|
| A / B / X / Y | South / East / West / North |
| LB / RB | LeftTrigger / RightTrigger |
| LT / RT (analog) | LeftTrigger2 / RightTrigger2 |
| View / Menu / Xbox | Select / Start / Mode |
| Stick clicks | LeftThumb / RightThumb |
| D-pad | DPadUp / DPadDown / DPadLeft / DPadRight |
| Sticks | LeftStickX/Y, RightStickX/Y (up = positive Y) |
Tested with an Xbox controller over USB on macOS 15 (Sequoia). Any controller the framework exposes with the standard "extended gamepad" layout (PlayStation, Switch Pro, MFi…) should work the same way, mapped by position, but hasn't been tested yet: reports welcome.
One system runs every frame in PreUpdate (before InputSystems, like bevy_gilrs) on the main
thread (NonSendMarker):
GCController.controllers().GamepadConnectionEvent::Connected.RawGamepadButtonChangedEvent /
RawGamepadAxisChangedEvent (also wrapped in RawGamepadEvent).GamepadConnectionEvent::Disconnected.Bevy's own gamepad_event_processing_system turns those into the Gamepad component.
vendor_id / product_id are None (the framework doesn't expose them).| bevy_apple_gamepad | Bevy |
|---|---|
| 0.1 | 0.20 |
Dual-licensed under MIT or Apache-2.0, at your option (same as Bevy).
⠀⠀⠀⠀⠀⠀⢸⡆⠀⣇⠀⣷⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⠀⠀⠀⠀⠀⠀⠀⢻⣧⣄⠀⠀⠀⠀⠀⠀⠀⢠⣼⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢻⣧⡀⠀⠀⠀⠀⠀⠀⠀⢰⡿⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⢠⣼⠀⢸⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⢸⣇⡀⣿⡆⠘⢻⣧⡄⠀⠀⠀⠀⠀⠀⠀⢸⡏⠀⠀⠀⠀⠀⠀⠀⠈⠹⢿⣧⡄⠀⠀⠀⠀⢠⣼⡏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢻⣧⡄⠀⠀⠀⠀⠀⢰⡿⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⢿⡇⠀⢠⣼⡏⠀⣸⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠘⢻⡇⢸⡇⠀⠘⢻⣷⡄⠀⠀⠀⠀⠀⠀⢸⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠻⣿⣦⡀⠀⢠⣾⡏⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢻⣧⡄⠀⠀⠀⢸⡿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣧⢀⣼⡏⠁⢰⣿⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⢸⣧⣼⡇⠀⠀⠀⠹⢿⣆⡀⠀⠀⠀⢀⣼⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢻⣷⣤⣾⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢻⣧⡄⠀⣸⡿⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢻⣸⡟⠃⠀⢸⡇⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⢹⣿⣧⠀⠀⠀⠀⠘⢻⣧⡄⠀⠀⢸⡟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣤⣤⣴⣿⣿⣿⣿⣿⣿⣿⣷⣶⣶⣶⣦⣤⣤⣤⣤⣤⣤⣤⣤⣈⣹⣧⣸⣟⣣⣤⣤⣤⣤⣤⣤⣴⣶⣶⣾⣿⣿⣿⣿⣿⡇⠀⠀⢸⡇⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢿⣿⠀⠀⠀⠀⠀⠈⢻⣷⡆⠀⢸⡇⠀⠀⠀⠀⢀⣠⣤⣴⣶⣿⣿⡿⠟⠛⠋⢹⣧⡈⠉⠉⠉⠙⠛⠛⠛⠛⠛⠛⠛⠻⠿⠿⠿⠿⣿⣿⣿⣿⣿⡿⠿⠿⠿⠟⠛⠛⠛⠛⠛⠋⠉⢩⣽⡟⠸⢿⡆⢠⣾⡇⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⠀⠀⠀⠀⠀⠀⠀⠸⢿⣧⣸⣇⣠⣤⣴⣾⣿⣿⠿⠛⠛⠋⠁⠀⠀⠀⠀⠈⢻⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⣼⡿⠋⢹⣇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣼⡟⠁⠀⠸⢿⣾⡟⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢻⣀⣠⣤⣤⣤⣤⣤⣤⣾⣿⣿⣿⣿⡟⠛⠋⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣧⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣰⣾⡟⠋⠁⠀⠘⢻⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⣾⠏⠀⠀⠀⠀⣸⣿⡇⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⣿⡿⠿⠟⠛⠛⠛⠛⠛⢻⣿⢿⣿⣧⣤⣄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢻⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣾⡟⠋⠁⠀⠀⠀⠀⠸⣷⡄⠀⠀⠀⠀⠀⠀⢀⣸⡿⠃⠀⠀⢠⣴⣾⣿⠟⠃⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⠻⢿⣧⣄⠀⠀⠀⠀⠀⠘⢻⡇⠘⠛⠻⢿⣷⣦⣤⡄⠀⠀⠀⠀⠀⠀⠀⠀⢸⣧⡀⠀⠀⠀⠀⠀⠀⢠⣴⣾⡟⠋⠀⠀⠀⠀⠀⠀⠀⠀⢸⣧⡀⠀⠀⠀⠀⢠⣼⡟⠃⢀⣠⣼⣿⡿⠟⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠹⢿⣧⣄⡀⠀⠀⠀⢸⣷⡄⠀⠀⠀⠙⠛⠻⣿⣷⣦⣤⡀⠀⠀⠀⠀⠈⢻⡇⠀⠀⠀⠀⢠⣴⣾⡟⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢻⡇⠀⠀⠀⢠⣼⡟⢡⣴⣾⣿⡿⠟⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⢻⣷⣄⡀⠀⠀⢸⣧⡀⠀⠀⠀⠀⠀⠈⠙⠛⢻⣿⣷⣦⣄⡀⠀⢸⣧⠀⠀⢠⣼⣿⡟⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢿⡆⠀⢰⣾⣯⣼⣿⣿⡿⠏⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⢻⣷⣦⡀⠘⢻⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠻⢿⣿⣷⣮⣿⣠⣼⣿⡟⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣧⣸⣿⣿⣿⣿⡿⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢻⣷⣦⡜⢿⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣠⣤⣬⣽⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡟⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⢻⣿⣾⣧⠀⠀⢀⣠⣤⣤⣴⣾⣿⣿⡿⠿⠟⠛⠋⠉⠸⣷⡆⠈⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⢉⣩⣽⣿⣿⡟⣻⣿⣿⡟⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⠻⣿⣾⣿⣿⣿⣿⣟⡛⠛⠉⠁⠀⠀⠀⠀⠀⠀⠀⠸⢿⣆⠀⠀⠀⠀⠀⠀⠀⢀⣠⣴⣾⣿⣿⣿⣯⣥⣼⣿⡟⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⠛⠛⠻⠿⢿⣿⣿⣿⣷⣶⣦⣤⣤⣤⣄⡀⠀⠀⠘⢿⣇⡀⠀⢀⣠⣤⣼⣿⣿⣿⣿⣿⣿⡿⠿⠟⠛⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠙⠛⠛⠻⠿⢿⣿⣿⣿⣷⣶⣾⣿⣧⣴⣿⣿⣿⣿⡿⠟⠛⠛⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠙⠛⠛⠛⠿⠛⠛⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
Gamepad support for Bevy on macOS through Apple's Game Controller framework.
Use it if your controller shows up as connected in Bevy on macOS but nothing happens when you press buttons or move the sticks (typical with Xbox controllers on macOS 14+):
INFO bevy_input::gamepad: Gamepad 450v0 connected.
WARN gilrs::gamepad: No mapping found for UUID 03000000-5e04-0000-ea02-000014050000
LocationID
(Failed to get location id of device … Skipping).This crate reads controllers with the Game Controller framework (the API Apple recommends, and the
one browsers use, which is why the web gamepad viewer
works while the native one doesn't) and feeds them into Bevy's standard gamepad pipeline: it
writes the same raw events bevy_gilrs writes. So everything else works unchanged: the Gamepad
component, GamepadSettings dead zones, pressed / just_pressed, left_stick(), etc.
No app bundle and no system setting is needed.
[dependencies]
bevy = "0.20"
bevy_apple_gamepad = { path = "crates/bevy_apple_gamepad" } # or git / crates.io once published
use bevy::prelude::*;
use bevy_apple_gamepad::AppleGamepadPlugin;
fn main() {
let mut app = App::new();
// On macOS, replace gilrs (otherwise you get a second gamepad that never moves).
#[cfg(target_os = "macos")]
app.add_plugins(DefaultPlugins.build().disable::<bevy::gilrs::GilrsPlugin>());
#[cfg(not(target_os = "macos"))]
app.add_plugins(DefaultPlugins);
// No-op on other platforms, so it's safe to add unconditionally.
app.add_plugins(AppleGamepadPlugin::default());
app.run();
}
Then read gamepads the usual Bevy way:
fn read_gamepads(gamepads: Query<&Gamepad>) {
for gamepad in &gamepads {
let movement = gamepad.left_stick();
if gamepad.just_pressed(GamepadButton::South) {
// jump!
}
}
}
AppleGamepadPlugin {
// macOS only delivers controller input to the focused app; set this to keep reading it
// while the window is in the background.
monitor_background_events: true,
}
Bevy names buttons by position. With Xbox labels:
| Controller | Bevy GamepadButton / GamepadAxis |
|---|---|
| A / B / X / Y | South / East / West / North |
| LB / RB | LeftTrigger / RightTrigger |
| LT / RT (analog) | LeftTrigger2 / RightTrigger2 |
| View / Menu / Xbox | Select / Start / Mode |
| Stick clicks | LeftThumb / RightThumb |
| D-pad | DPadUp / DPadDown / DPadLeft / DPadRight |
| Sticks | LeftStickX/Y, RightStickX/Y (up = positive Y) |
Tested with an Xbox controller over USB on macOS 15 (Sequoia). Any controller the framework exposes with the standard "extended gamepad" layout (PlayStation, Switch Pro, MFi…) should work the same way, mapped by position, but hasn't been tested yet: reports welcome.
One system runs every frame in PreUpdate (before InputSystems, like bevy_gilrs) on the main
thread (NonSendMarker):
GCController.controllers().GamepadConnectionEvent::Connected.RawGamepadButtonChangedEvent /
RawGamepadAxisChangedEvent (also wrapped in RawGamepadEvent).GamepadConnectionEvent::Disconnected.Bevy's own gamepad_event_processing_system turns those into the Gamepad component.
vendor_id / product_id are None (the framework doesn't expose them).| bevy_apple_gamepad | Bevy |
|---|---|
| 0.1 | 0.20 |
Dual-licensed under MIT or Apache-2.0, at your option (same as Bevy).