Skip to content

Commit 5a51256

Browse files
committed
Deprecate ChangeTrackers<T> in favor of Ref<T> (#7306)
# Objective `ChangeTrackers<>` is a `WorldQuery` type that lets you access the change ticks for a component. #7097 has added `Ref<>`, which gives access to a component's value in addition to its change ticks. Since bevy's access model does not separate a component's value from its change ticks, there is no benefit to using `ChangeTrackers<T>` over `Ref<T>`. ## Solution Deprecate `ChangeTrackers<>`. --- ## Changelog * `ChangeTrackers<T>` has been deprecated. It will be removed in Bevy 0.11. ## Migration Guide `ChangeTrackers<T>` has been deprecated, and will be removed in the next release. Any usage should be replaced with `Ref<T>`. ```rust // Before (0.9) fn my_system(q: Query<(&MyComponent, ChangeTrackers<MyComponent>)>) { for (value, trackers) in &q { if trackers.is_changed() { // Do something with `value`. } } } // After (0.10) fn my_system(q: Query<Ref<MyComponent>>) { for value in &q { if value.is_changed() { // Do something with `value`. } } } ```
1 parent 0af001e commit 5a51256

File tree

5 files changed

+40
-33
lines changed

5 files changed

+40
-33
lines changed

crates/bevy_ecs/src/change_detection.rs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -696,10 +696,9 @@ mod tests {
696696
use crate::{
697697
self as bevy_ecs,
698698
change_detection::{
699-
Mut, NonSendMut, ResMut, TicksMut, CHECK_TICK_THRESHOLD, MAX_CHANGE_AGE,
699+
Mut, NonSendMut, Ref, ResMut, TicksMut, CHECK_TICK_THRESHOLD, MAX_CHANGE_AGE,
700700
},
701701
component::{Component, ComponentTicks, Tick},
702-
query::ChangeTrackers,
703702
system::{IntoSystem, Query, System},
704703
world::World,
705704
};
@@ -718,11 +717,11 @@ mod tests {
718717

719718
#[test]
720719
fn change_expiration() {
721-
fn change_detected(query: Query<ChangeTrackers<C>>) -> bool {
720+
fn change_detected(query: Query<Ref<C>>) -> bool {
722721
query.single().is_changed()
723722
}
724723

725-
fn change_expired(query: Query<ChangeTrackers<C>>) -> bool {
724+
fn change_expired(query: Query<Ref<C>>) -> bool {
726725
query.single().is_changed()
727726
}
728727

@@ -753,7 +752,7 @@ mod tests {
753752

754753
#[test]
755754
fn change_tick_wraparound() {
756-
fn change_detected(query: Query<ChangeTrackers<C>>) -> bool {
755+
fn change_detected(query: Query<Ref<C>>) -> bool {
757756
query.single().is_changed()
758757
}
759758

@@ -784,10 +783,10 @@ mod tests {
784783
*world.change_tick.get_mut() += MAX_CHANGE_AGE + CHECK_TICK_THRESHOLD;
785784
let change_tick = world.change_tick();
786785

787-
let mut query = world.query::<ChangeTrackers<C>>();
786+
let mut query = world.query::<Ref<C>>();
788787
for tracker in query.iter(&world) {
789-
let ticks_since_insert = change_tick.wrapping_sub(tracker.component_ticks.added.tick);
790-
let ticks_since_change = change_tick.wrapping_sub(tracker.component_ticks.changed.tick);
788+
let ticks_since_insert = change_tick.wrapping_sub(tracker.ticks.added.tick);
789+
let ticks_since_change = change_tick.wrapping_sub(tracker.ticks.changed.tick);
791790
assert!(ticks_since_insert > MAX_CHANGE_AGE);
792791
assert!(ticks_since_change > MAX_CHANGE_AGE);
793792
}
@@ -796,8 +795,8 @@ mod tests {
796795
world.check_change_ticks();
797796

798797
for tracker in query.iter(&world) {
799-
let ticks_since_insert = change_tick.wrapping_sub(tracker.component_ticks.added.tick);
800-
let ticks_since_change = change_tick.wrapping_sub(tracker.component_ticks.changed.tick);
798+
let ticks_since_insert = change_tick.wrapping_sub(tracker.ticks.added.tick);
799+
let ticks_since_change = change_tick.wrapping_sub(tracker.ticks.changed.tick);
801800
assert!(ticks_since_insert == MAX_CHANGE_AGE);
802801
assert!(ticks_since_change == MAX_CHANGE_AGE);
803802
}

crates/bevy_ecs/src/lib.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ pub use bevy_ptr as ptr;
2525

2626
/// Most commonly used re-exported types.
2727
pub mod prelude {
28+
#[doc(hidden)]
29+
#[allow(deprecated)]
30+
pub use crate::query::ChangeTrackers;
2831
#[doc(hidden)]
2932
#[cfg(feature = "bevy_reflect")]
3033
pub use crate::reflect::{ReflectComponent, ReflectResource};
@@ -35,7 +38,7 @@ pub mod prelude {
3538
component::Component,
3639
entity::Entity,
3740
event::{Event, EventReader, EventWriter, Events},
38-
query::{Added, AnyOf, ChangeTrackers, Changed, Or, QueryState, With, Without},
41+
query::{Added, AnyOf, Changed, Or, QueryState, With, Without},
3942
removal_detection::RemovedComponents,
4043
schedule::{
4144
apply_state_transition, apply_system_buffers, common_conditions::*, IntoSystemConfig,
@@ -63,11 +66,10 @@ mod tests {
6366
use crate::prelude::Or;
6467
use crate::{
6568
bundle::Bundle,
69+
change_detection::Ref,
6670
component::{Component, ComponentId},
6771
entity::Entity,
68-
query::{
69-
Added, ChangeTrackers, Changed, FilteredAccess, ReadOnlyWorldQuery, With, Without,
70-
},
72+
query::{Added, Changed, FilteredAccess, ReadOnlyWorldQuery, With, Without},
7173
system::Resource,
7274
world::{Mut, World},
7375
};
@@ -1297,7 +1299,10 @@ mod tests {
12971299
}
12981300

12991301
#[test]
1302+
#[allow(deprecated)]
13001303
fn trackers_query() {
1304+
use crate::prelude::ChangeTrackers;
1305+
13011306
let mut world = World::default();
13021307
let e1 = world.spawn((A(0), B(0))).id();
13031308
world.spawn(B(0));
@@ -1541,7 +1546,7 @@ mod tests {
15411546
assert_eq!(1, query_min_size![&B, (With<A>, With<C>)],);
15421547
assert_eq!(1, query_min_size![(&A, &B), With<C>],);
15431548
assert_eq!(4, query_min_size![&A, ()], "Simple Archetypal");
1544-
assert_eq!(4, query_min_size![ChangeTrackers<A>, ()],);
1549+
assert_eq!(4, query_min_size![Ref<A>, ()],);
15451550
// All the following should set minimum size to 0, as it's impossible to predict
15461551
// how many entities the filters will trim.
15471552
assert_eq!(0, query_min_size![(), Added<A>], "Simple Added");

crates/bevy_ecs/src/query/fetch.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1115,25 +1115,25 @@ unsafe impl<T: ReadOnlyWorldQuery> ReadOnlyWorldQuery for Option<T> {}
11151115
/// }
11161116
/// # bevy_ecs::system::assert_is_system(print_moving_objects_system);
11171117
/// ```
1118+
#[deprecated = "`ChangeTrackers<T>` will be removed in bevy 0.11. Use `bevy_ecs::prelude::Ref<T>` instead."]
11181119
pub struct ChangeTrackers<T: Component> {
11191120
pub(crate) component_ticks: ComponentTicks,
11201121
pub(crate) last_change_tick: u32,
11211122
pub(crate) change_tick: u32,
11221123
marker: PhantomData<T>,
11231124
}
11241125

1126+
#[allow(deprecated)]
11251127
impl<T: Component> Clone for ChangeTrackers<T> {
11261128
fn clone(&self) -> Self {
1127-
Self {
1128-
component_ticks: self.component_ticks,
1129-
last_change_tick: self.last_change_tick,
1130-
change_tick: self.change_tick,
1131-
marker: PhantomData,
1132-
}
1129+
*self
11331130
}
11341131
}
1132+
1133+
#[allow(deprecated)]
11351134
impl<T: Component> Copy for ChangeTrackers<T> {}
11361135

1136+
#[allow(deprecated)]
11371137
impl<T: Component> std::fmt::Debug for ChangeTrackers<T> {
11381138
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
11391139
f.debug_struct("ChangeTrackers")
@@ -1144,6 +1144,7 @@ impl<T: Component> std::fmt::Debug for ChangeTrackers<T> {
11441144
}
11451145
}
11461146

1147+
#[allow(deprecated)]
11471148
impl<T: Component> ChangeTrackers<T> {
11481149
/// Returns true if this component has been added since the last execution of this system.
11491150
pub fn is_added(&self) -> bool {
@@ -1171,6 +1172,7 @@ pub struct ChangeTrackersFetch<'w, T> {
11711172
change_tick: u32,
11721173
}
11731174

1175+
#[allow(deprecated)]
11741176
// SAFETY: `ROQueryFetch<Self>` is the same as `QueryFetch<Self>`
11751177
unsafe impl<T: Component> WorldQuery for ChangeTrackers<T> {
11761178
type Fetch<'w> = ChangeTrackersFetch<'w, T>;
@@ -1317,6 +1319,7 @@ unsafe impl<T: Component> WorldQuery for ChangeTrackers<T> {
13171319
}
13181320
}
13191321

1322+
#[allow(deprecated)]
13201323
/// SAFETY: access is read only
13211324
unsafe impl<T: Component> ReadOnlyWorldQuery for ChangeTrackers<T> {}
13221325

crates/bevy_ecs/src/query/filter.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -572,7 +572,7 @@ impl_tick_filter!(
572572
/// A common use for this filter is one-time initialization.
573573
///
574574
/// To retain all results without filtering but still check whether they were added after the
575-
/// system last ran, use [`ChangeTrackers<T>`](crate::query::ChangeTrackers).
575+
/// system last ran, use [`Ref<T>`](crate::change_detection::Ref).
576576
///
577577
/// # Examples
578578
///

examples/ecs/component_change_detection.rs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! This example illustrates how to react to component change.
22
3-
use bevy::prelude::*;
3+
use bevy::{ecs::world::Ref, prelude::*};
44
use rand::Rng;
55

66
fn main() {
@@ -43,15 +43,15 @@ fn change_detection(query: Query<(Entity, &MyComponent), Changed<MyComponent>>)
4343
}
4444
}
4545

46-
// By looking at trackers, the query is not filtered but the information is available
47-
fn tracker_monitoring(
48-
query: Query<(
49-
Entity,
50-
Option<&MyComponent>,
51-
Option<ChangeTrackers<MyComponent>>,
52-
)>,
53-
) {
54-
for (entity, component, trackers) in &query {
55-
info!("{:?}: {:?} -> {:?}", entity, component, trackers);
46+
// By using `Ref`, the query is not filtered but the information is available
47+
fn tracker_monitoring(query: Query<(Entity, Ref<MyComponent>)>) {
48+
for (entity, component) in &query {
49+
info!(
50+
"{:?}: {:?} -> {{is_added: {}, is_changed: {}}}",
51+
entity,
52+
component,
53+
component.is_added(),
54+
component.is_changed()
55+
);
5656
}
5757
}

0 commit comments

Comments
 (0)