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`.
        }
    }
}
```
This commit is contained in:
JoJoJet 2023-02-19 16:19:56 +00:00
parent 0af001edd4
commit 5a5125671b
5 changed files with 40 additions and 33 deletions

View file

@ -696,10 +696,9 @@ mod tests {
use crate::{
self as bevy_ecs,
change_detection::{
Mut, NonSendMut, ResMut, TicksMut, CHECK_TICK_THRESHOLD, MAX_CHANGE_AGE,
Mut, NonSendMut, Ref, ResMut, TicksMut, CHECK_TICK_THRESHOLD, MAX_CHANGE_AGE,
},
component::{Component, ComponentTicks, Tick},
query::ChangeTrackers,
system::{IntoSystem, Query, System},
world::World,
};
@ -718,11 +717,11 @@ mod tests {
#[test]
fn change_expiration() {
fn change_detected(query: Query<ChangeTrackers<C>>) -> bool {
fn change_detected(query: Query<Ref<C>>) -> bool {
query.single().is_changed()
}
fn change_expired(query: Query<ChangeTrackers<C>>) -> bool {
fn change_expired(query: Query<Ref<C>>) -> bool {
query.single().is_changed()
}
@ -753,7 +752,7 @@ mod tests {
#[test]
fn change_tick_wraparound() {
fn change_detected(query: Query<ChangeTrackers<C>>) -> bool {
fn change_detected(query: Query<Ref<C>>) -> bool {
query.single().is_changed()
}
@ -784,10 +783,10 @@ mod tests {
*world.change_tick.get_mut() += MAX_CHANGE_AGE + CHECK_TICK_THRESHOLD;
let change_tick = world.change_tick();
let mut query = world.query::<ChangeTrackers<C>>();
let mut query = world.query::<Ref<C>>();
for tracker in query.iter(&world) {
let ticks_since_insert = change_tick.wrapping_sub(tracker.component_ticks.added.tick);
let ticks_since_change = change_tick.wrapping_sub(tracker.component_ticks.changed.tick);
let ticks_since_insert = change_tick.wrapping_sub(tracker.ticks.added.tick);
let ticks_since_change = change_tick.wrapping_sub(tracker.ticks.changed.tick);
assert!(ticks_since_insert > MAX_CHANGE_AGE);
assert!(ticks_since_change > MAX_CHANGE_AGE);
}
@ -796,8 +795,8 @@ mod tests {
world.check_change_ticks();
for tracker in query.iter(&world) {
let ticks_since_insert = change_tick.wrapping_sub(tracker.component_ticks.added.tick);
let ticks_since_change = change_tick.wrapping_sub(tracker.component_ticks.changed.tick);
let ticks_since_insert = change_tick.wrapping_sub(tracker.ticks.added.tick);
let ticks_since_change = change_tick.wrapping_sub(tracker.ticks.changed.tick);
assert!(ticks_since_insert == MAX_CHANGE_AGE);
assert!(ticks_since_change == MAX_CHANGE_AGE);
}

View file

@ -25,6 +25,9 @@ pub use bevy_ptr as ptr;
/// Most commonly used re-exported types.
pub mod prelude {
#[doc(hidden)]
#[allow(deprecated)]
pub use crate::query::ChangeTrackers;
#[doc(hidden)]
#[cfg(feature = "bevy_reflect")]
pub use crate::reflect::{ReflectComponent, ReflectResource};
@ -35,7 +38,7 @@ pub mod prelude {
component::Component,
entity::Entity,
event::{Event, EventReader, EventWriter, Events},
query::{Added, AnyOf, ChangeTrackers, Changed, Or, QueryState, With, Without},
query::{Added, AnyOf, Changed, Or, QueryState, With, Without},
removal_detection::RemovedComponents,
schedule::{
apply_state_transition, apply_system_buffers, common_conditions::*, IntoSystemConfig,
@ -63,11 +66,10 @@ mod tests {
use crate::prelude::Or;
use crate::{
bundle::Bundle,
change_detection::Ref,
component::{Component, ComponentId},
entity::Entity,
query::{
Added, ChangeTrackers, Changed, FilteredAccess, ReadOnlyWorldQuery, With, Without,
},
query::{Added, Changed, FilteredAccess, ReadOnlyWorldQuery, With, Without},
system::Resource,
world::{Mut, World},
};
@ -1297,7 +1299,10 @@ mod tests {
}
#[test]
#[allow(deprecated)]
fn trackers_query() {
use crate::prelude::ChangeTrackers;
let mut world = World::default();
let e1 = world.spawn((A(0), B(0))).id();
world.spawn(B(0));
@ -1541,7 +1546,7 @@ mod tests {
assert_eq!(1, query_min_size![&B, (With<A>, With<C>)],);
assert_eq!(1, query_min_size![(&A, &B), With<C>],);
assert_eq!(4, query_min_size![&A, ()], "Simple Archetypal");
assert_eq!(4, query_min_size![ChangeTrackers<A>, ()],);
assert_eq!(4, query_min_size![Ref<A>, ()],);
// All the following should set minimum size to 0, as it's impossible to predict
// how many entities the filters will trim.
assert_eq!(0, query_min_size![(), Added<A>], "Simple Added");

View file

@ -1115,6 +1115,7 @@ unsafe impl<T: ReadOnlyWorldQuery> ReadOnlyWorldQuery for Option<T> {}
/// }
/// # bevy_ecs::system::assert_is_system(print_moving_objects_system);
/// ```
#[deprecated = "`ChangeTrackers<T>` will be removed in bevy 0.11. Use `bevy_ecs::prelude::Ref<T>` instead."]
pub struct ChangeTrackers<T: Component> {
pub(crate) component_ticks: ComponentTicks,
pub(crate) last_change_tick: u32,
@ -1122,18 +1123,17 @@ pub struct ChangeTrackers<T: Component> {
marker: PhantomData<T>,
}
#[allow(deprecated)]
impl<T: Component> Clone for ChangeTrackers<T> {
fn clone(&self) -> Self {
Self {
component_ticks: self.component_ticks,
last_change_tick: self.last_change_tick,
change_tick: self.change_tick,
marker: PhantomData,
}
*self
}
}
#[allow(deprecated)]
impl<T: Component> Copy for ChangeTrackers<T> {}
#[allow(deprecated)]
impl<T: Component> std::fmt::Debug for ChangeTrackers<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ChangeTrackers")
@ -1144,6 +1144,7 @@ impl<T: Component> std::fmt::Debug for ChangeTrackers<T> {
}
}
#[allow(deprecated)]
impl<T: Component> ChangeTrackers<T> {
/// Returns true if this component has been added since the last execution of this system.
pub fn is_added(&self) -> bool {
@ -1171,6 +1172,7 @@ pub struct ChangeTrackersFetch<'w, T> {
change_tick: u32,
}
#[allow(deprecated)]
// SAFETY: `ROQueryFetch<Self>` is the same as `QueryFetch<Self>`
unsafe impl<T: Component> WorldQuery for ChangeTrackers<T> {
type Fetch<'w> = ChangeTrackersFetch<'w, T>;
@ -1317,6 +1319,7 @@ unsafe impl<T: Component> WorldQuery for ChangeTrackers<T> {
}
}
#[allow(deprecated)]
/// SAFETY: access is read only
unsafe impl<T: Component> ReadOnlyWorldQuery for ChangeTrackers<T> {}

View file

@ -572,7 +572,7 @@ impl_tick_filter!(
/// A common use for this filter is one-time initialization.
///
/// To retain all results without filtering but still check whether they were added after the
/// system last ran, use [`ChangeTrackers<T>`](crate::query::ChangeTrackers).
/// system last ran, use [`Ref<T>`](crate::change_detection::Ref).
///
/// # Examples
///

View file

@ -1,6 +1,6 @@
//! This example illustrates how to react to component change.
use bevy::prelude::*;
use bevy::{ecs::world::Ref, prelude::*};
use rand::Rng;
fn main() {
@ -43,15 +43,15 @@ fn change_detection(query: Query<(Entity, &MyComponent), Changed<MyComponent>>)
}
}
// By looking at trackers, the query is not filtered but the information is available
fn tracker_monitoring(
query: Query<(
Entity,
Option<&MyComponent>,
Option<ChangeTrackers<MyComponent>>,
)>,
) {
for (entity, component, trackers) in &query {
info!("{:?}: {:?} -> {:?}", entity, component, trackers);
// By using `Ref`, the query is not filtered but the information is available
fn tracker_monitoring(query: Query<(Entity, Ref<MyComponent>)>) {
for (entity, component) in &query {
info!(
"{:?}: {:?} -> {{is_added: {}, is_changed: {}}}",
entity,
component,
component.is_added(),
component.is_changed()
);
}
}