diff --git a/crates/bevy_input/src/input.rs b/crates/bevy_input/src/input.rs index 72876dcd2da09..31e0b9980c4b0 100644 --- a/crates/bevy_input/src/input.rs +++ b/crates/bevy_input/src/input.rs @@ -71,3 +71,80 @@ where self.just_released.iter() } } + +#[cfg(test)] +mod test { + + #[test] + fn input_test() { + use crate::Input; + + /// Used for testing `Input` functionality + #[derive(Copy, Clone, Eq, PartialEq, Hash)] + enum DummyInput { + Input1, + Input2, + } + + let mut input = Input::default(); + + // Test pressing + input.press(DummyInput::Input1); + input.press(DummyInput::Input2); + + // Check if they were "just pressed" (pressed on this update) + assert!(input.just_pressed(DummyInput::Input1)); + assert!(input.just_pressed(DummyInput::Input2)); + + // Check if they are also marked as pressed + assert!(input.pressed(DummyInput::Input1)); + assert!(input.pressed(DummyInput::Input2)); + + // Update the `Input` and check press state + input.update(); + + // Check if they're marked "just pressed" + assert!(!input.just_pressed(DummyInput::Input1)); + assert!(!input.just_pressed(DummyInput::Input2)); + + // Check if they're marked as pressed + assert!(input.pressed(DummyInput::Input1)); + assert!(input.pressed(DummyInput::Input2)); + + // Release the inputs and check state + + input.release(DummyInput::Input1); + input.release(DummyInput::Input2); + + // Check if they're marked as "just released" (released on this update) + assert!(input.just_released(DummyInput::Input1)); + assert!(input.just_released(DummyInput::Input2)); + + // Check that they're not incorrectly marked as pressed + assert!(!input.pressed(DummyInput::Input1)); + assert!(!input.pressed(DummyInput::Input2)); + + // Update the `Input` and check for removal from `just_released` + + input.update(); + + // Check that they're not incorrectly marked as just released + assert!(!input.just_released(DummyInput::Input1)); + assert!(!input.just_released(DummyInput::Input2)); + + // Set up an `Input` to test resetting. + let mut input = Input::default(); + + input.press(DummyInput::Input1); + input.release(DummyInput::Input2); + + // Reset the `Input` and test it was reset correctly. + input.reset(DummyInput::Input1); + input.reset(DummyInput::Input2); + + assert!(!input.just_pressed(DummyInput::Input1)); + assert!(!input.pressed(DummyInput::Input1)); + + assert!(!input.just_released(DummyInput::Input2)); + } +} diff --git a/crates/bevy_input/src/touch.rs b/crates/bevy_input/src/touch.rs index 9e78f7d05bedf..1a23e240796fc 100644 --- a/crates/bevy_input/src/touch.rs +++ b/crates/bevy_input/src/touch.rs @@ -2,7 +2,6 @@ use bevy_app::{EventReader, Events}; use bevy_ecs::{Local, Res, ResMut}; use bevy_math::Vec2; use bevy_utils::HashMap; -use core::ops::DerefMut; /// Represents a touch event /// @@ -139,10 +138,8 @@ impl Touches { self.just_pressed.contains_key(&id) } - pub fn iter_just_pressed(&self) -> impl Iterator + '_ { - self.just_pressed - .iter() - .map(move |(id, _)| self.pressed.get(id).unwrap()) + pub fn iter_just_pressed(&self) -> impl Iterator { + self.just_pressed.values() } pub fn get_released(&self, id: u64) -> Option<&Touch> { @@ -153,54 +150,234 @@ impl Touches { self.just_released.contains_key(&id) } - pub fn iter_just_released(&self) -> impl Iterator + '_ { - self.just_released - .iter() - .map(move |(id, _)| self.pressed.get(id).unwrap()) + pub fn iter_just_released(&self) -> impl Iterator { + self.just_released.values() } pub fn just_cancelled(&self, id: u64) -> bool { self.just_cancelled.contains_key(&id) } - pub fn iter_just_cancelled(&self) -> impl Iterator + '_ { - self.just_cancelled - .iter() - .map(move |(id, _)| self.pressed.get(id).unwrap()) + pub fn iter_just_cancelled(&self) -> impl Iterator { + self.just_cancelled.values() } -} -/// Updates the Touches resource with the latest TouchInput events -pub fn touch_screen_input_system( - mut state: Local, - mut touch_state: ResMut, - touch_input_events: Res>, -) { - let touch_state = touch_state.deref_mut(); - touch_state.just_pressed.clear(); - touch_state.just_released.clear(); - for event in state.touch_event_reader.iter(&touch_input_events) { + fn process_touch_event(&mut self, event: &TouchInput) { match event.phase { TouchPhase::Started => { - touch_state.pressed.insert(event.id, event.into()); - touch_state.just_pressed.insert(event.id, event.into()); + self.pressed.insert(event.id, event.into()); + self.just_pressed.insert(event.id, event.into()); } TouchPhase::Moved => { - let mut new_touch = touch_state.pressed.get(&event.id).cloned().unwrap(); + let mut new_touch = self.pressed.get(&event.id).cloned().unwrap(); new_touch.previous_position = new_touch.position; new_touch.previous_force = new_touch.force; new_touch.position = event.position; new_touch.force = event.force; - touch_state.pressed.insert(event.id, new_touch); + self.pressed.insert(event.id, new_touch); } TouchPhase::Ended => { - touch_state.just_released.insert(event.id, event.into()); - touch_state.pressed.remove_entry(&event.id); + self.just_released.insert(event.id, event.into()); + self.pressed.remove_entry(&event.id); } TouchPhase::Cancelled => { - touch_state.just_cancelled.insert(event.id, event.into()); - touch_state.pressed.remove_entry(&event.id); + self.just_cancelled.insert(event.id, event.into()); + self.pressed.remove_entry(&event.id); } }; } + + fn update(&mut self) { + self.just_pressed.clear(); + self.just_released.clear(); + self.just_cancelled.clear(); + } +} + +/// Updates the Touches resource with the latest TouchInput events +pub fn touch_screen_input_system( + mut state: Local, + mut touch_state: ResMut, + touch_input_events: Res>, +) { + touch_state.update(); + + for event in state.touch_event_reader.iter(&touch_input_events) { + touch_state.process_touch_event(event); + } +} + +#[cfg(test)] +mod test { + + #[test] + fn touch_update() { + use crate::{touch::Touch, Touches}; + use bevy_math::Vec2; + + let mut touches = Touches::default(); + + let touch_event = Touch { + id: 4, + start_position: Vec2::new(0.0, 0.0), + start_force: None, + previous_position: Vec2::new(0.0, 0.0), + previous_force: None, + position: Vec2::new(0.0, 0.0), + force: None, + }; + + // Add a touch to `just_pressed`, 'just_released', and 'just cancelled' + + touches.just_pressed.insert(4, touch_event); + touches.just_released.insert(4, touch_event); + touches.just_cancelled.insert(4, touch_event); + + touches.update(); + + // Verify that all the `just_x` maps are cleared + assert!(touches.just_pressed.is_empty()); + assert!(touches.just_released.is_empty()); + assert!(touches.just_cancelled.is_empty()); + } + + #[test] + fn touch_process() { + use crate::{touch::TouchPhase, TouchInput, Touches}; + use bevy_math::Vec2; + + let mut touches = Touches::default(); + + // Test adding a `TouchPhase::Started` + + let touch_event = TouchInput { + phase: TouchPhase::Started, + position: Vec2::new(4.0, 4.0), + force: None, + id: 4, + }; + + touches.update(); + touches.process_touch_event(&touch_event); + + assert!(touches.pressed.get(&touch_event.id).is_some()); + assert!(touches.just_pressed.get(&touch_event.id).is_some()); + + // Test adding a `TouchPhase::Moved` + + let moved_touch_event = TouchInput { + phase: TouchPhase::Moved, + position: Vec2::new(5.0, 5.0), + force: None, + id: touch_event.id, + }; + + touches.update(); + touches.process_touch_event(&moved_touch_event); + + assert_eq!( + touches + .pressed + .get(&moved_touch_event.id) + .expect("Missing from pressed after move.") + .previous_position, + touch_event.position + ); + + // Test cancelling an event + + let cancel_touch_event = TouchInput { + phase: TouchPhase::Cancelled, + position: Vec2::new(1.0, 1.0), + force: None, + id: touch_event.id, + }; + + touches.update(); + touches.process_touch_event(&cancel_touch_event); + + assert!(touches.just_cancelled.get(&cancel_touch_event.id).is_some()); + assert!(touches.pressed.get(&cancel_touch_event.id).is_none()); + + // Test ending an event + + let end_touch_event = TouchInput { + phase: TouchPhase::Ended, + position: Vec2::new(4.0, 4.0), + force: None, + id: 4, + }; + + touches.update(); + touches.process_touch_event(&touch_event); + touches.process_touch_event(&end_touch_event); + + assert!(touches.just_released.get(&touch_event.id).is_some()); + assert!(touches.pressed.get(&touch_event.id).is_none()); + } + + #[test] + fn touch_pressed() { + use crate::{touch::TouchPhase, TouchInput, Touches}; + use bevy_math::Vec2; + + let mut touches = Touches::default(); + + let touch_event = TouchInput { + phase: TouchPhase::Started, + position: Vec2::new(4.0, 4.0), + force: None, + id: 4, + }; + + // Register the touch and test that it was registered correctly + touches.process_touch_event(&touch_event); + + assert!(touches.get_pressed(touch_event.id).is_some()); + assert!(touches.just_pressed(touch_event.id)); + assert_eq!(touches.iter().count(), 1); + } + + #[test] + fn touch_released() { + use crate::{touch::TouchPhase, TouchInput, Touches}; + use bevy_math::Vec2; + + let mut touches = Touches::default(); + + let touch_event = TouchInput { + phase: TouchPhase::Ended, + position: Vec2::new(4.0, 4.0), + force: None, + id: 4, + }; + + // Register the touch and test that it was registered correctly + touches.process_touch_event(&touch_event); + + assert!(touches.get_released(touch_event.id).is_some()); + assert!(touches.just_released(touch_event.id)); + assert_eq!(touches.iter_just_released().count(), 1); + } + + #[test] + fn touch_cancelled() { + use crate::{touch::TouchPhase, TouchInput, Touches}; + use bevy_math::Vec2; + + let mut touches = Touches::default(); + + let touch_event = TouchInput { + phase: TouchPhase::Cancelled, + position: Vec2::new(4.0, 4.0), + force: None, + id: 4, + }; + + // Register the touch and test that it was registered correctly + touches.process_touch_event(&touch_event); + + assert!(touches.just_cancelled(touch_event.id)); + assert_eq!(touches.iter_just_cancelled().count(), 1); + } } diff --git a/crates/bevy_math/src/face_toward.rs b/crates/bevy_math/src/face_toward.rs index d090989fd4ccd..f873964b18afc 100644 --- a/crates/bevy_math/src/face_toward.rs +++ b/crates/bevy_math/src/face_toward.rs @@ -19,3 +19,23 @@ impl FaceToward for Mat4 { ) } } + +#[cfg(test)] +mod test { + #[test] + fn face_toward_mat4() { + use crate::{FaceToward, Mat4, Vec3, Vec4}; + + // Completely arbitrary arguments + let matrix = Mat4::face_toward( + Vec3::new(50.0, 60.0, 0.0), + Vec3::new(0.0, 0.0, 0.0), + Vec3::new(0.0, 1.0, 0.0), + ); + + assert_eq!(matrix.x_axis, Vec4::new(0.0, 0.0, -1.0, -0.0)); + assert_eq!(matrix.y_axis, Vec4::new(-0.7682213, 0.6401844, 0.0, 0.0)); + assert_eq!(matrix.z_axis, Vec4::new(0.6401844, 0.7682213, 0.0, 0.0)); + assert_eq!(matrix.w_axis, Vec4::new(50.0, 60.0, 0.0, 1.0)); + } +}