Add replace_if_neq to DetectChangesMut (#9418)
				
					
				
			# Objective Just like [`set_if_neq`](https://docs.rs/bevy_ecs/latest/bevy_ecs/change_detection/trait.DetectChangesMut.html#method.set_if_neq), being able to express the "I don't want to unnecessarily trigger the change detection" but with the ability to handle the previous value if change occurs. ## Solution Add `replace_if_neq` to `DetectChangesMut`. --- ## Changelog - Added `DetectChangesMut::replace_if_neq`: like `set_if_neq` change the value only if the new value if different from the current one, but return the previous value if the change occurs.
This commit is contained in:
		
							parent
							
								
									1abb6b0758
								
							
						
					
					
						commit
						cfb65c1eaf
					
				@ -6,6 +6,7 @@ use crate::{
 | 
			
		||||
    system::Resource,
 | 
			
		||||
};
 | 
			
		||||
use bevy_ptr::{Ptr, UnsafeCellDeref};
 | 
			
		||||
use std::mem;
 | 
			
		||||
use std::ops::{Deref, DerefMut};
 | 
			
		||||
 | 
			
		||||
/// The (arbitrarily chosen) minimum number of world tick increments between `check_tick` scans.
 | 
			
		||||
@ -129,6 +130,8 @@ pub trait DetectChangesMut: DetectChanges {
 | 
			
		||||
    /// This is useful to ensure change detection is only triggered when the underlying value
 | 
			
		||||
    /// changes, instead of every time it is mutably accessed.
 | 
			
		||||
    ///
 | 
			
		||||
    /// If you need to handle the previous value, use [`replace_if_neq`](DetectChangesMut::replace_if_neq).
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Examples
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
@ -167,6 +170,77 @@ pub trait DetectChangesMut: DetectChanges {
 | 
			
		||||
            self.set_changed();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Overwrites this smart pointer with the given value, if and only if `*self != value`
 | 
			
		||||
    /// returning the previous value if this occurs.
 | 
			
		||||
    ///
 | 
			
		||||
    /// This is useful to ensure change detection is only triggered when the underlying value
 | 
			
		||||
    /// changes, instead of every time it is mutably accessed.
 | 
			
		||||
    ///
 | 
			
		||||
    /// If you don't need to handle the previous value, use [`set_if_neq`](DetectChangesMut::set_if_neq).
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Examples
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// # use bevy_ecs::{prelude::*, schedule::common_conditions::{resource_changed, on_event}};
 | 
			
		||||
    /// #[derive(Resource, PartialEq, Eq)]
 | 
			
		||||
    /// pub struct Score(u32);
 | 
			
		||||
    ///
 | 
			
		||||
    /// #[derive(Event, PartialEq, Eq)]
 | 
			
		||||
    /// pub struct ScoreChanged {
 | 
			
		||||
    ///     current: u32,
 | 
			
		||||
    ///     previous: u32,
 | 
			
		||||
    /// }
 | 
			
		||||
    ///
 | 
			
		||||
    /// fn reset_score(mut score: ResMut<Score>, mut score_changed: EventWriter<ScoreChanged>) {
 | 
			
		||||
    ///     // Set the score to zero, unless it is already zero.
 | 
			
		||||
    ///     let new_score = 0;
 | 
			
		||||
    ///     if let Some(Score(previous_score)) = score.replace_if_neq(Score(new_score)) {
 | 
			
		||||
    ///         // If `score` change, emit a `ScoreChanged` event.
 | 
			
		||||
    ///         score_changed.send(ScoreChanged {
 | 
			
		||||
    ///             current: new_score,
 | 
			
		||||
    ///             previous: previous_score,
 | 
			
		||||
    ///         });
 | 
			
		||||
    ///     }
 | 
			
		||||
    /// }
 | 
			
		||||
    /// # let mut world = World::new();
 | 
			
		||||
    /// # world.insert_resource(Events::<ScoreChanged>::default());
 | 
			
		||||
    /// # world.insert_resource(Score(1));
 | 
			
		||||
    /// # let mut score_changed = IntoSystem::into_system(resource_changed::<Score>());
 | 
			
		||||
    /// # score_changed.initialize(&mut world);
 | 
			
		||||
    /// # score_changed.run((), &mut world);
 | 
			
		||||
    /// #
 | 
			
		||||
    /// # let mut score_changed_event = IntoSystem::into_system(on_event::<ScoreChanged>());
 | 
			
		||||
    /// # score_changed_event.initialize(&mut world);
 | 
			
		||||
    /// # score_changed_event.run((), &mut world);
 | 
			
		||||
    /// #
 | 
			
		||||
    /// # let mut schedule = Schedule::new();
 | 
			
		||||
    /// # schedule.add_systems(reset_score);
 | 
			
		||||
    /// #
 | 
			
		||||
    /// # // first time `reset_score` runs, the score is changed.
 | 
			
		||||
    /// # schedule.run(&mut world);
 | 
			
		||||
    /// # assert!(score_changed.run((), &mut world));
 | 
			
		||||
    /// # assert!(score_changed_event.run((), &mut world));
 | 
			
		||||
    /// # // second time `reset_score` runs, the score is not changed.
 | 
			
		||||
    /// # schedule.run(&mut world);
 | 
			
		||||
    /// # assert!(!score_changed.run((), &mut world));
 | 
			
		||||
    /// # assert!(!score_changed_event.run((), &mut world));
 | 
			
		||||
    /// ```
 | 
			
		||||
    #[inline]
 | 
			
		||||
    #[must_use = "If you don't need to handle the previous value, use `set_if_neq` instead."]
 | 
			
		||||
    fn replace_if_neq(&mut self, value: Self::Inner) -> Option<Self::Inner>
 | 
			
		||||
    where
 | 
			
		||||
        Self::Inner: Sized + PartialEq,
 | 
			
		||||
    {
 | 
			
		||||
        let old = self.bypass_change_detection();
 | 
			
		||||
        if *old != value {
 | 
			
		||||
            let previous = mem::replace(old, value);
 | 
			
		||||
            self.set_changed();
 | 
			
		||||
            Some(previous)
 | 
			
		||||
        } else {
 | 
			
		||||
            None
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macro_rules! change_detection_impl {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user