Bevy Input Patterns
Contents
- Player Movement
- FPS Camera Control
- Orbit Camera
- Drag and Drop with Picking
Common Patterns
Player Movement (WASD + Arrow Keys)
#![allow(unused)] fn main() { fn player_movement( keys: Res<ButtonInput<KeyCode>>, mut query: Query<&mut Transform, With<Player>>, time: Res<Time>, ) { let mut direction = Vec2::ZERO; if keys.pressed(KeyCode::KeyW) || keys.pressed(KeyCode::ArrowUp) { direction.y += 1.0; } if keys.pressed(KeyCode::KeyS) || keys.pressed(KeyCode::ArrowDown) { direction.y -= 1.0; } if keys.pressed(KeyCode::KeyA) || keys.pressed(KeyCode::ArrowLeft) { direction.x -= 1.0; } if keys.pressed(KeyCode::KeyD) || keys.pressed(KeyCode::ArrowRight) { direction.x += 1.0; } // Normalize to prevent diagonal speed boost let direction = direction.normalize_or_zero(); let speed = 200.0; for mut transform in &mut query { transform.translation.x += direction.x * speed * time.delta_secs(); transform.translation.y += direction.y * speed * time.delta_secs(); } } }
FPS Camera Control (Mouse Look)
#![allow(unused)] fn main() { #[derive(Component)] struct FpsCamera { sensitivity: f32, pitch: f32, yaw: f32, } fn fps_camera_look( mut motion: EventReader<MouseMotion>, mut camera: Query<(&mut Transform, &mut FpsCamera)>, ) { let (mut transform, mut fps) = camera.single_mut(); for event in motion.read() { fps.yaw -= event.delta.x * fps.sensitivity; fps.pitch -= event.delta.y * fps.sensitivity; fps.pitch = fps.pitch.clamp(-89.0_f32.to_radians(), 89.0_f32.to_radians()); } transform.rotation = Quat::from_rotation_y(fps.yaw) * Quat::from_rotation_x(fps.pitch); } }
Lock the cursor for FPS controls:
#![allow(unused)] fn main() { fn grab_cursor(mut windows: Query<&mut Window>) { let mut window = windows.single_mut(); window.cursor_options.grab_mode = CursorGrabMode::Locked; window.cursor_options.visible = false; } }
Orbit Camera
#![allow(unused)] fn main() { #[derive(Component)] struct OrbitCamera { focus: Vec3, radius: f32, pitch: f32, yaw: f32, } fn orbit_camera_system( mut scroll: EventReader<MouseWheel>, mut motion: EventReader<MouseMotion>, buttons: Res<ButtonInput<MouseButton>>, mut camera: Query<(&mut Transform, &mut OrbitCamera)>, ) { let (mut transform, mut orbit) = camera.single_mut(); // Zoom with scroll wheel for event in scroll.read() { orbit.radius -= event.y * 0.5; orbit.radius = orbit.radius.clamp(2.0, 50.0); } // Rotate with middle mouse button if buttons.pressed(MouseButton::Middle) { for event in motion.read() { orbit.yaw -= event.delta.x * 0.005; orbit.pitch -= event.delta.y * 0.005; orbit.pitch = orbit.pitch.clamp(-1.5, 1.5); } } // Update camera transform let rotation = Quat::from_rotation_y(orbit.yaw) * Quat::from_rotation_x(orbit.pitch); transform.translation = orbit.focus + rotation * Vec3::new(0.0, 0.0, orbit.radius); transform.look_at(orbit.focus, Vec3::Y); } }
Drag and Drop with Picking
#![allow(unused)] fn main() { #[derive(Component)] struct Draggable; #[derive(Component)] struct Dragging { offset: Vec2, } fn setup_draggable(mut commands: Commands) { commands.spawn(( Sprite { custom_size: Some(Vec2::new(64.0, 64.0)), ..default() }, Draggable, )) .observe(on_drag_start) .observe(on_drag) .observe(on_drag_end); } fn on_drag_start( trigger: Trigger<Pointer<DragStart>>, mut commands: Commands, transforms: Query<&Transform>, ) { let entity = trigger.target(); let pointer_pos = trigger.event().pointer_location.position; if let Ok(transform) = transforms.get(entity) { let offset = Vec2::new(transform.translation.x, transform.translation.y) - pointer_pos; commands.entity(entity).insert(Dragging { offset }); } } fn on_drag( trigger: Trigger<Pointer<Drag>>, mut transforms: Query<(&mut Transform, &Dragging)>, ) { let entity = trigger.target(); let pointer_pos = trigger.event().pointer_location.position; if let Ok((mut transform, dragging)) = transforms.get_mut(entity) { let new_pos = pointer_pos + dragging.offset; transform.translation.x = new_pos.x; transform.translation.y = new_pos.y; } } fn on_drag_end(trigger: Trigger<Pointer<DragEnd>>, mut commands: Commands) { commands.entity(trigger.target()).remove::<Dragging>(); } }