Avoid triggering change detection for inputs (#6847)

# Objective
Fix #5292.

## Solution
Avoid derefencing when clearing to ensure that change detection is not triggered when there is nothing to clear.
This commit is contained in:
James Liu 2022-12-11 18:22:09 +00:00
parent b37a6ca9a2
commit 0d67c32153
4 changed files with 18 additions and 7 deletions

View File

@ -1,6 +1,9 @@
use crate::{Axis, Input}; use crate::{Axis, Input};
use bevy_ecs::event::{EventReader, EventWriter}; use bevy_ecs::event::{EventReader, EventWriter};
use bevy_ecs::system::{Res, ResMut, Resource}; use bevy_ecs::{
change_detection::DetectChanges,
system::{Res, ResMut, Resource},
};
use bevy_reflect::{std_traits::ReflectDefault, FromReflect, Reflect}; use bevy_reflect::{std_traits::ReflectDefault, FromReflect, Reflect};
use bevy_utils::{tracing::info, HashMap}; use bevy_utils::{tracing::info, HashMap};
use thiserror::Error; use thiserror::Error;
@ -1160,7 +1163,7 @@ pub fn gamepad_event_system(
mut events: EventWriter<GamepadEvent>, mut events: EventWriter<GamepadEvent>,
settings: Res<GamepadSettings>, settings: Res<GamepadSettings>,
) { ) {
button_input.clear(); button_input.bypass_change_detection().clear();
for event in raw_events.iter() { for event in raw_events.iter() {
match &event.event_type { match &event.event_type {
GamepadEventType::Connected(_) => { GamepadEventType::Connected(_) => {

View File

@ -34,6 +34,13 @@ use bevy_ecs::schedule::State;
/// * Call the [`Input::press`] method for each press event. /// * Call the [`Input::press`] method for each press event.
/// * Call the [`Input::release`] method for each release event. /// * Call the [`Input::release`] method for each release event.
/// * Call the [`Input::clear`] method at each frame start, before processing events. /// * Call the [`Input::clear`] method at each frame start, before processing events.
///
/// Note: Calling `clear` from a [`ResMut`] will trigger change detection.
/// It may be preferable to use [`DetectChanges::bypass_change_detection`]
/// to avoid causing the resource to always be marked as changed.
///
///[`ResMut`]: bevy_ecs::system::ResMut
///[`DetectChanges::bypass_change_detection`]: bevy_ecs::change_detection::DetectChanges::bypass_change_detection
#[derive(Debug, Clone, Resource, Reflect)] #[derive(Debug, Clone, Resource, Reflect)]
#[reflect(Default)] #[reflect(Default)]
pub struct Input<T: Copy + Eq + Hash + Send + Sync + 'static> { pub struct Input<T: Copy + Eq + Hash + Send + Sync + 'static> {

View File

@ -1,5 +1,5 @@
use crate::{ButtonState, Input}; use crate::{ButtonState, Input};
use bevy_ecs::{event::EventReader, system::ResMut}; use bevy_ecs::{change_detection::DetectChanges, event::EventReader, system::ResMut};
use bevy_reflect::{FromReflect, Reflect}; use bevy_reflect::{FromReflect, Reflect};
#[cfg(feature = "serialize")] #[cfg(feature = "serialize")]
@ -41,8 +41,9 @@ pub fn keyboard_input_system(
mut key_input: ResMut<Input<KeyCode>>, mut key_input: ResMut<Input<KeyCode>>,
mut keyboard_input_events: EventReader<KeyboardInput>, mut keyboard_input_events: EventReader<KeyboardInput>,
) { ) {
scan_input.clear(); // Avoid clearing if it's not empty to ensure change detection is not triggered.
key_input.clear(); scan_input.bypass_change_detection().clear();
key_input.bypass_change_detection().clear();
for event in keyboard_input_events.iter() { for event in keyboard_input_events.iter() {
let KeyboardInput { let KeyboardInput {
scan_code, state, .. scan_code, state, ..

View File

@ -1,5 +1,5 @@
use crate::{ButtonState, Input}; use crate::{ButtonState, Input};
use bevy_ecs::{event::EventReader, system::ResMut}; use bevy_ecs::{change_detection::DetectChanges, event::EventReader, system::ResMut};
use bevy_math::Vec2; use bevy_math::Vec2;
use bevy_reflect::{FromReflect, Reflect}; use bevy_reflect::{FromReflect, Reflect};
@ -132,7 +132,7 @@ pub fn mouse_button_input_system(
mut mouse_button_input: ResMut<Input<MouseButton>>, mut mouse_button_input: ResMut<Input<MouseButton>>,
mut mouse_button_input_events: EventReader<MouseButtonInput>, mut mouse_button_input_events: EventReader<MouseButtonInput>,
) { ) {
mouse_button_input.clear(); mouse_button_input.bypass_change_detection().clear();
for event in mouse_button_input_events.iter() { for event in mouse_button_input_events.iter() {
match event.state { match event.state {
ButtonState::Pressed => mouse_button_input.press(event.button), ButtonState::Pressed => mouse_button_input.press(event.button),