Skip to content

Commit

Permalink
Mouse input accumulation (#14044)
Browse files Browse the repository at this point in the history
# 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>
  • Loading branch information
3 people authored Jul 1, 2024
1 parent f607be8 commit 6dcff2b
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 6 deletions.
22 changes: 19 additions & 3 deletions crates/bevy_input/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,11 @@ use bevy_ecs::prelude::*;
use bevy_reflect::Reflect;
use gestures::*;
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 gamepad::{
Expand Down Expand Up @@ -77,7 +81,15 @@ impl Plugin for InputPlugin {
.add_event::<MouseMotion>()
.add_event::<MouseWheel>()
.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::<RotationGesture>()
.add_event::<DoubleTapGesture>()
Expand All @@ -94,6 +106,8 @@ impl Plugin for InputPlugin {
.init_resource::<ButtonInput<GamepadButton>>()
.init_resource::<Axis<GamepadAxis>>()
.init_resource::<Axis<GamepadButton>>()
.init_resource::<AccumulatedMouseMotion>()
.init_resource::<AccumulatedMouseScroll>()
.add_systems(
PreUpdate,
(
Expand Down Expand Up @@ -124,7 +138,9 @@ impl Plugin for InputPlugin {
.register_type::<TouchInput>()
.register_type::<GamepadEvent>()
.register_type::<GamepadButtonInput>()
.register_type::<GamepadSettings>();
.register_type::<GamepadSettings>()
.register_type::<AccumulatedMouseMotion>()
.register_type::<AccumulatedMouseScroll>();
}
}

Expand Down
82 changes: 81 additions & 1 deletion crates/bevy_input/src/mouse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@

use crate::{ButtonInput, ButtonState};
use bevy_ecs::entity::Entity;
use bevy_ecs::reflect::ReflectResource;
use bevy_ecs::system::Resource;
use bevy_ecs::{
change_detection::DetectChangesMut,
event::{Event, EventReader},
system::ResMut,
};
use bevy_math::Vec2;
use bevy_reflect::Reflect;
use bevy_reflect::{std_traits::ReflectDefault, Reflect};

#[cfg(feature = "serialize")]
use bevy_reflect::{ReflectDeserialize, ReflectSerialize};
Expand Down Expand Up @@ -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;
}
22 changes: 20 additions & 2 deletions examples/input/mouse_input.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
//! Prints mouse button events.

use bevy::prelude::*;
use bevy::{
input::mouse::{AccumulatedMouseMotion, AccumulatedMouseScroll},
prelude::*,
};

fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Update, mouse_click_system)
.add_systems(Update, (mouse_click_system, mouse_move_system))
.run();
}

Expand All @@ -23,3 +26,18 @@ fn mouse_click_system(mouse_button_input: Res<ButtonInput<MouseButton>>) {
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);
}
}

0 comments on commit 6dcff2b

Please sign in to comment.