diff --git a/crates/bevy_camera_controller/src/free_camera.rs b/crates/bevy_camera_controller/src/free_camera.rs index 8d18d0e673b6b..94c04eed668bf 100644 --- a/crates/bevy_camera_controller/src/free_camera.rs +++ b/crates/bevy_camera_controller/src/free_camera.rs @@ -20,7 +20,7 @@ use bevy_camera::Camera; use bevy_ecs::prelude::*; use bevy_input::keyboard::KeyCode; use bevy_input::mouse::{ - AccumulatedMouseMotion, AccumulatedMouseScroll, MouseButton, MouseScrollUnit, + AccumulatedMouseMotion, AccumulatedMouseScroll, MouseButton, MouseScrollPixelsPerLine, }; use bevy_input::touch::Touches; use bevy_input::ButtonInput; @@ -267,6 +267,7 @@ pub fn run_freecamera_controller( mut windows: Query<(&Window, &mut CursorOptions)>, accumulated_mouse_motion: Res, accumulated_mouse_scroll: Res, + mouse_scroll_conversion: Res, touch_input: Res, mouse_button_input: Res>, key_input: Res>, @@ -302,12 +303,10 @@ pub fn run_freecamera_controller( return; } - let scroll = match accumulated_mouse_scroll.unit { - MouseScrollUnit::Line => accumulated_mouse_scroll.delta.y, - MouseScrollUnit::Pixel => { - accumulated_mouse_scroll.delta.y / MouseScrollUnit::SCROLL_UNIT_CONVERSION_FACTOR - } - }; + let scroll = accumulated_mouse_scroll + .to_lines(&mouse_scroll_conversion) + .delta + .y; // By using exponentiation we ensure that this scales up and down smoothly // regardless of the amount of scrolling processed per frame state.speed_multiplier *= exp(config.scroll_factor * scroll); diff --git a/crates/bevy_camera_controller/src/pan_camera.rs b/crates/bevy_camera_controller/src/pan_camera.rs index 03338e90b5e83..209fcdad088ab 100644 --- a/crates/bevy_camera_controller/src/pan_camera.rs +++ b/crates/bevy_camera_controller/src/pan_camera.rs @@ -9,7 +9,7 @@ use bevy_app::{App, Plugin, RunFixedMainLoop, RunFixedMainLoopSystems}; use bevy_camera::{Camera, RenderTarget}; use bevy_ecs::prelude::*; use bevy_input::keyboard::KeyCode; -use bevy_input::mouse::{AccumulatedMouseScroll, MouseButton, MouseScrollUnit}; +use bevy_input::mouse::{AccumulatedMouseScroll, MouseButton, MouseScrollPixelsPerLine}; use bevy_input::ButtonInput; use bevy_math::{Vec2, Vec3}; use bevy_picking::events::{Drag, DragEnd, DragStart, Pointer}; @@ -167,6 +167,7 @@ fn run_pancamera_controller( time: Res>, key_input: Res>, accumulated_mouse_scroll: Res, + mouse_scroll_conversion: Res, mut query: Query<(&mut Transform, &mut PanCamera), With>, ) { let dt = time.delta_secs(); @@ -240,12 +241,10 @@ fn run_pancamera_controller( } // (with mouse wheel) - let mouse_scroll = match accumulated_mouse_scroll.unit { - MouseScrollUnit::Line => accumulated_mouse_scroll.delta.y, - MouseScrollUnit::Pixel => { - accumulated_mouse_scroll.delta.y / MouseScrollUnit::SCROLL_UNIT_CONVERSION_FACTOR - } - }; + let mouse_scroll = accumulated_mouse_scroll + .to_lines(&mouse_scroll_conversion) + .delta + .y; zoom_amount += mouse_scroll * controller.zoom_speed; controller.zoom_factor = diff --git a/crates/bevy_input/src/lib.rs b/crates/bevy_input/src/lib.rs index 66f4cdb250f32..e9e35eeab485c 100644 --- a/crates/bevy_input/src/lib.rs +++ b/crates/bevy_input/src/lib.rs @@ -80,7 +80,7 @@ use keyboard::{keyboard_input_system, Key, KeyCode, KeyboardFocusLost, KeyboardI use mouse::{ accumulate_mouse_motion_system, accumulate_mouse_scroll_system, mouse_button_input_system, AccumulatedMouseMotion, AccumulatedMouseScroll, MouseButton, MouseButtonInput, MouseMotion, - MouseWheel, + MouseScrollPixelsPerLine, MouseWheel, }; #[cfg(feature = "touch")] @@ -122,6 +122,7 @@ impl Plugin for InputPlugin { .add_message::() .init_resource::() .init_resource::() + .init_resource::() .init_resource::>() .add_systems( PreUpdate, diff --git a/crates/bevy_input/src/mouse.rs b/crates/bevy_input/src/mouse.rs index 902e0bf8a8f83..708075d8efeb7 100644 --- a/crates/bevy_input/src/mouse.rs +++ b/crates/bevy_input/src/mouse.rs @@ -1,5 +1,7 @@ //! The mouse input functionality. +use core::ops::{Div, Mul}; + use crate::{touch::TouchPhase, ButtonInput, ButtonState}; #[cfg(feature = "bevy_reflect")] use bevy_ecs::prelude::ReflectMessage; @@ -11,6 +13,8 @@ use bevy_ecs::{ system::ResMut, }; use bevy_math::Vec2; +use derive_more::{Deref, DerefMut}; + #[cfg(feature = "bevy_reflect")] use { bevy_ecs::reflect::ReflectResource, @@ -139,18 +143,53 @@ pub enum MouseScrollUnit { Pixel, } -impl MouseScrollUnit { - /// An approximate conversion factor to account for the difference between - /// [`MouseScrollUnit::Line`] and [`MouseScrollUnit::Pixel`]. - /// - /// Each line corresponds to many pixels; this must be corrected for in order to ensure that - /// mouse wheel controls are scaled properly regardless of the provided input events for the end user. - /// - /// This value is correct for Microsoft Edge, but its validity has not been broadly tested. - /// Please file an issue if you find that this differs on certain platforms or hardware! - pub const SCROLL_UNIT_CONVERSION_FACTOR: f32 = 100.; +/// Describes the quantity of [`MouseScrollUnit::Pixel`]s per [`MouseScrollUnit::Line`] +#[derive(Debug, Clone, Copy, PartialEq, Resource, Deref, DerefMut)] +#[cfg_attr( + feature = "bevy_reflect", + derive(Reflect), + reflect(Debug, PartialEq, Clone) +)] +#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr( + all(feature = "serialize", feature = "bevy_reflect"), + reflect(Serialize, Deserialize) +)] +pub struct MouseScrollPixelsPerLine(f32); + +impl Default for MouseScrollPixelsPerLine { + fn default() -> Self { + MouseScrollPixelsPerLine(100.0) + } } +impl Mul for f32 { + type Output = f32; + fn mul(self, rhs: MouseScrollPixelsPerLine) -> Self::Output { + self * rhs.0 + } +} + +impl Div for f32 { + type Output = f32; + fn div(self, rhs: MouseScrollPixelsPerLine) -> Self::Output { + self / rhs.0 + } +} + +impl Mul for Vec2 { + type Output = Vec2; + fn mul(self, rhs: MouseScrollPixelsPerLine) -> Self::Output { + self * rhs.0 + } +} + +impl Div for Vec2 { + type Output = Vec2; + fn div(self, rhs: MouseScrollPixelsPerLine) -> Self::Output { + self / rhs.0 + } +} /// A mouse wheel event. /// /// This event is the translated version of the `WindowEvent::MouseWheel` from the `winit` crate. @@ -254,6 +293,31 @@ impl Default for AccumulatedMouseScroll { } } +impl AccumulatedMouseScroll { + /// Converts the units to [`MouseScrollUnit::Line`] + pub fn to_lines(&self, conversion_ratio: &MouseScrollPixelsPerLine) -> Self { + if self.unit == MouseScrollUnit::Pixel { + AccumulatedMouseScroll { + unit: MouseScrollUnit::Line, + delta: self.delta / *conversion_ratio, + } + } else { + *self + } + } + /// Converts the units to [`MouseScrollUnit::Pixel`] + pub fn to_pixels(&self, conversion_ratio: &MouseScrollPixelsPerLine) -> Self { + if self.unit == MouseScrollUnit::Line { + AccumulatedMouseScroll { + unit: MouseScrollUnit::Pixel, + delta: self.delta * *conversion_ratio, + } + } else { + *self + } + } +} + /// Updates the [`AccumulatedMouseMotion`] resource using the [`MouseMotion`] event. /// The value of [`AccumulatedMouseMotion`] is reset to zero every frame pub fn accumulate_mouse_motion_system( diff --git a/examples/camera/projection_zoom.rs b/examples/camera/projection_zoom.rs index 0c9512a94d5de..3797249f33ca5 100644 --- a/examples/camera/projection_zoom.rs +++ b/examples/camera/projection_zoom.rs @@ -3,7 +3,8 @@ use std::{f32::consts::PI, ops::Range}; use bevy::{ - camera::ScalingMode, input::mouse::AccumulatedMouseScroll, input::mouse::MouseScrollUnit, + camera::ScalingMode, + input::mouse::{AccumulatedMouseScroll, MouseScrollPixelsPerLine}, prelude::*, }; @@ -138,17 +139,13 @@ fn zoom( camera: Single<&mut Projection, With>, camera_settings: Res, mouse_wheel_input: Res, + scroll_conversion: Res, ) { // Usually, you won't need to handle both types of projection, // but doing so makes for a more complete example. // Get a scroll amount proportional to the kind of input that generated it. - let scroll = match mouse_wheel_input.unit { - MouseScrollUnit::Line => mouse_wheel_input.delta.y, - MouseScrollUnit::Pixel => { - mouse_wheel_input.delta.y / MouseScrollUnit::SCROLL_UNIT_CONVERSION_FACTOR - } - }; + let scroll = mouse_wheel_input.to_lines(&scroll_conversion).delta.y; match *camera.into_inner() { Projection::Orthographic(ref mut orthographic) => {