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,
|
system::Resource,
|
||||||
};
|
};
|
||||||
use bevy_ptr::{Ptr, UnsafeCellDeref};
|
use bevy_ptr::{Ptr, UnsafeCellDeref};
|
||||||
|
use std::mem;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
/// The (arbitrarily chosen) minimum number of world tick increments between `check_tick` scans.
|
/// 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
|
/// This is useful to ensure change detection is only triggered when the underlying value
|
||||||
/// changes, instead of every time it is mutably accessed.
|
/// 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
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
@ -167,6 +170,77 @@ pub trait DetectChangesMut: DetectChanges {
|
|||||||
self.set_changed();
|
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 {
|
macro_rules! change_detection_impl {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user