change_detection/change_detection.rs
1//! This example illustrates how to react to component and resource changes.
2
3use bevy::prelude::*;
4use rand::Rng;
5
6fn main() {
7 App::new()
8 .add_plugins(DefaultPlugins)
9 .add_systems(Startup, setup)
10 .add_systems(
11 Update,
12 (
13 change_component,
14 change_component_2,
15 change_resource,
16 change_detection,
17 ),
18 )
19 .run();
20}
21
22#[derive(Component, PartialEq, Debug)]
23struct MyComponent(f32);
24
25#[derive(Resource, PartialEq, Debug)]
26struct MyResource(f32);
27
28fn setup(mut commands: Commands) {
29 // Note the first change detection log correctly points to this line because the component is
30 // added. Although commands are deferred, they are able to track the original calling location.
31 commands.spawn(MyComponent(0.0));
32 commands.insert_resource(MyResource(0.0));
33}
34
35fn change_component(time: Res<Time>, mut query: Query<(Entity, &mut MyComponent)>) {
36 for (entity, mut component) in &mut query {
37 if rand::thread_rng().gen_bool(0.1) {
38 let new_component = MyComponent(time.elapsed_secs().round());
39 info!("New value: {new_component:?} {entity}");
40 // Change detection occurs on mutable dereference, and does not consider whether or not
41 // a value is actually equal. To avoid triggering change detection when nothing has
42 // actually changed, you can use the `set_if_neq` method on any component or resource
43 // that implements PartialEq.
44 component.set_if_neq(new_component);
45 }
46 }
47}
48
49/// This is a duplicate of the `change_component` system, added to show that change tracking can
50/// help you find *where* your component is being changed, when there are multiple possible
51/// locations.
52fn change_component_2(time: Res<Time>, mut query: Query<(Entity, &mut MyComponent)>) {
53 for (entity, mut component) in &mut query {
54 if rand::thread_rng().gen_bool(0.1) {
55 let new_component = MyComponent(time.elapsed_secs().round());
56 info!("New value: {new_component:?} {entity}");
57 component.set_if_neq(new_component);
58 }
59 }
60}
61
62/// Change detection concepts for components apply similarly to resources.
63fn change_resource(time: Res<Time>, mut my_resource: ResMut<MyResource>) {
64 if rand::thread_rng().gen_bool(0.1) {
65 let new_resource = MyResource(time.elapsed_secs().round());
66 info!("New value: {new_resource:?}");
67 my_resource.set_if_neq(new_resource);
68 }
69}
70
71/// Query filters like [`Changed<T>`] and [`Added<T>`] ensure only entities matching these filters
72/// will be returned by the query.
73///
74/// Using the [`Ref<T>`] system param allows you to access change detection information, but does
75/// not filter the query.
76fn change_detection(
77 changed_components: Query<Ref<MyComponent>, Changed<MyComponent>>,
78 my_resource: Res<MyResource>,
79) {
80 for component in &changed_components {
81 // By default, you can only tell that a component was changed.
82 //
83 // This is useful, but what if you have multiple systems modifying the same component, how
84 // will you know which system is causing the component to change?
85 warn!(
86 "Change detected!\n\t-> value: {:?}\n\t-> added: {}\n\t-> changed: {}\n\t-> changed by: {}",
87 component,
88 component.is_added(),
89 component.is_changed(),
90 // If you enable the `track_location` feature, you can unlock the `changed_by()`
91 // method. It returns the file and line number that the component or resource was
92 // changed in. It's not recommended for released games, but great for debugging!
93 component.changed_by()
94 );
95 }
96
97 if my_resource.is_changed() {
98 warn!(
99 "Change detected!\n\t-> value: {:?}\n\t-> added: {}\n\t-> changed: {}\n\t-> changed by: {}",
100 my_resource,
101 my_resource.is_added(),
102 my_resource.is_changed(),
103 my_resource.changed_by() // Like components, requires `track_location` feature.
104 );
105 }
106}