Mouse input accumulation (#14044)

# Objective

- Add the `AccumulatedMouseMotion` and `AccumulatedMouseScroll`
resources to make it simpler to track mouse motion/scroll changes
- Closes #13915

## Solution

- Created two resources, `AccumulatedMouseMotion` and
`AccumulatedMouseScroll`, and a method that tracks the `MouseMotion` and
`MouseWheel` events and accumulates their deltas every frame.
- Also modified the mouse input example to show how to use the
resources.

## Testing

- Tested the changes by modifying an existing example to use the newly
added resources, and moving/scrolling my trackpad around a ton.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: Gino Valente <49806985+MrGVSV@users.noreply.github.com>
This commit is contained in:
Aztro 2024-07-01 09:27:21 -05:00 committed by GitHub
parent f607be8777
commit 6dcff2bfe8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 120 additions and 6 deletions

View file

@ -43,7 +43,11 @@ use bevy_ecs::prelude::*;
use bevy_reflect::Reflect; use bevy_reflect::Reflect;
use gestures::*; use gestures::*;
use keyboard::{keyboard_input_system, KeyCode, KeyboardFocusLost, KeyboardInput}; use keyboard::{keyboard_input_system, KeyCode, KeyboardFocusLost, KeyboardInput};
use mouse::{mouse_button_input_system, MouseButton, MouseButtonInput, MouseMotion, MouseWheel}; use mouse::{
accumulate_mouse_motion_system, accumulate_mouse_scroll_system, mouse_button_input_system,
AccumulatedMouseMotion, AccumulatedMouseScroll, MouseButton, MouseButtonInput, MouseMotion,
MouseWheel,
};
use touch::{touch_screen_input_system, TouchInput, Touches}; use touch::{touch_screen_input_system, TouchInput, Touches};
use gamepad::{ use gamepad::{
@ -77,7 +81,15 @@ impl Plugin for InputPlugin {
.add_event::<MouseMotion>() .add_event::<MouseMotion>()
.add_event::<MouseWheel>() .add_event::<MouseWheel>()
.init_resource::<ButtonInput<MouseButton>>() .init_resource::<ButtonInput<MouseButton>>()
.add_systems(PreUpdate, mouse_button_input_system.in_set(InputSystem)) .add_systems(
PreUpdate,
(
mouse_button_input_system,
accumulate_mouse_motion_system,
accumulate_mouse_scroll_system,
)
.in_set(InputSystem),
)
.add_event::<PinchGesture>() .add_event::<PinchGesture>()
.add_event::<RotationGesture>() .add_event::<RotationGesture>()
.add_event::<DoubleTapGesture>() .add_event::<DoubleTapGesture>()
@ -94,6 +106,8 @@ impl Plugin for InputPlugin {
.init_resource::<ButtonInput<GamepadButton>>() .init_resource::<ButtonInput<GamepadButton>>()
.init_resource::<Axis<GamepadAxis>>() .init_resource::<Axis<GamepadAxis>>()
.init_resource::<Axis<GamepadButton>>() .init_resource::<Axis<GamepadButton>>()
.init_resource::<AccumulatedMouseMotion>()
.init_resource::<AccumulatedMouseScroll>()
.add_systems( .add_systems(
PreUpdate, PreUpdate,
( (
@ -124,7 +138,9 @@ impl Plugin for InputPlugin {
.register_type::<TouchInput>() .register_type::<TouchInput>()
.register_type::<GamepadEvent>() .register_type::<GamepadEvent>()
.register_type::<GamepadButtonInput>() .register_type::<GamepadButtonInput>()
.register_type::<GamepadSettings>(); .register_type::<GamepadSettings>()
.register_type::<AccumulatedMouseMotion>()
.register_type::<AccumulatedMouseScroll>();
} }
} }

View file

@ -2,13 +2,15 @@
use crate::{ButtonInput, ButtonState}; use crate::{ButtonInput, ButtonState};
use bevy_ecs::entity::Entity; use bevy_ecs::entity::Entity;
use bevy_ecs::reflect::ReflectResource;
use bevy_ecs::system::Resource;
use bevy_ecs::{ use bevy_ecs::{
change_detection::DetectChangesMut, change_detection::DetectChangesMut,
event::{Event, EventReader}, event::{Event, EventReader},
system::ResMut, system::ResMut,
}; };
use bevy_math::Vec2; use bevy_math::Vec2;
use bevy_reflect::Reflect; use bevy_reflect::{std_traits::ReflectDefault, Reflect};
#[cfg(feature = "serialize")] #[cfg(feature = "serialize")]
use bevy_reflect::{ReflectDeserialize, ReflectSerialize}; use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
@ -155,3 +157,81 @@ pub fn mouse_button_input_system(
} }
} }
} }
/// Tracks how much the mouse has moved every frame.
///
/// This resource is reset to zero every frame.
///
/// This resource sums the total [`MouseMotion`] events received this frame.
#[derive(Resource, Debug, Clone, Copy, PartialEq, Reflect, Default)]
#[reflect(Debug, Default, Resource, PartialEq)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub struct AccumulatedMouseMotion {
/// The change in mouse position.
pub delta: Vec2,
}
/// Tracks how much the mouse has scrolled every frame.
///
/// This resource is reset to zero every frame.
///
/// This resource sums the total [`MouseWheel`] events received this frame.
#[derive(Resource, Debug, Clone, Copy, PartialEq, Reflect)]
#[reflect(Debug, Default, Resource, PartialEq)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub struct AccumulatedMouseScroll {
/// The mouse scroll unit.
/// If this value changes while scrolling, then the
/// result of the accumulation could be incorrect
pub unit: MouseScrollUnit,
/// The change in scroll position.
pub delta: Vec2,
}
impl Default for AccumulatedMouseScroll {
fn default() -> Self {
Self {
unit: MouseScrollUnit::Line,
delta: Vec2::ZERO,
}
}
}
/// Updates the [`AccumulatedMouseMotion`] resource using the [`MouseMotion`] event.
/// The value of [`AccumulatedMouseMotion`] is reset to zero every frame
pub fn accumulate_mouse_motion_system(
mut mouse_motion_event: EventReader<MouseMotion>,
mut accumulated_mouse_motion: ResMut<AccumulatedMouseMotion>,
) {
let mut delta = Vec2::ZERO;
for event in mouse_motion_event.read() {
delta += event.delta;
}
accumulated_mouse_motion.delta = delta;
}
/// Updates the [`AccumulatedMouseScroll`] resource using the [`MouseWheel`] event.
/// The value of [`AccumulatedMouseScroll`] is reset to zero every frame
pub fn accumulate_mouse_scroll_system(
mut mouse_scroll_event: EventReader<MouseWheel>,
mut accumulated_mouse_scroll: ResMut<AccumulatedMouseScroll>,
) {
let mut delta = Vec2::ZERO;
let mut unit = MouseScrollUnit::Line;
for event in mouse_scroll_event.read() {
if event.unit != unit {
unit = event.unit;
}
delta += Vec2::new(event.x, event.y);
}
accumulated_mouse_scroll.delta = delta;
accumulated_mouse_scroll.unit = unit;
}

View file

@ -1,11 +1,14 @@
//! Prints mouse button events. //! Prints mouse button events.
use bevy::prelude::*; use bevy::{
input::mouse::{AccumulatedMouseMotion, AccumulatedMouseScroll},
prelude::*,
};
fn main() { fn main() {
App::new() App::new()
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
.add_systems(Update, mouse_click_system) .add_systems(Update, (mouse_click_system, mouse_move_system))
.run(); .run();
} }
@ -23,3 +26,18 @@ fn mouse_click_system(mouse_button_input: Res<ButtonInput<MouseButton>>) {
info!("left mouse just released"); info!("left mouse just released");
} }
} }
// This system prints messages when you finish dragging or scrolling with your mouse
fn mouse_move_system(
accumulated_mouse_motion: Res<AccumulatedMouseMotion>,
accumulated_mouse_scroll: Res<AccumulatedMouseScroll>,
) {
if accumulated_mouse_motion.delta != Vec2::ZERO {
let delta = accumulated_mouse_motion.delta;
info!("mouse moved ({}, {})", delta.x, delta.y);
}
if accumulated_mouse_scroll.delta != Vec2::ZERO {
let delta = accumulated_mouse_scroll.delta;
info!("mouse scrolled ({}, {})", delta.x, delta.y);
}
}