docs/bevy/examples/ecs/entity_disabling.rs 4.9 K · 152 lines · raw · history

1 //! Disabling entities is a powerful feature that allows you to hide entities from the ECS without deleting them.
2 //!
3 //! This can be useful for implementing features like "sleeping" objects that are offscreen
4 //! or managing networked entities.
5 //!
6 //! While disabling entities *will* make them invisible,
7 //! that's not its primary purpose!
8 //! [`Visibility`] should be used to hide entities;
9 //! disabled entities are skipped entirely, which can lead to subtle bugs.
10 //!
11 //! # Default query filters
12 //!
13 //! Under the hood, Bevy uses a "default query filter" that skips entities with the
14 //! the [`Disabled`] component.
15 //! These filters act as a by-default exclusion list for all queries,
16 //! and can be bypassed by explicitly including these components in your queries.
17 //! For example, `Query<&A, With<Disabled>`, `Query<(Entity, Has<Disabled>>)` or
18 //! `Query<&A, Or<(With<Disabled>, With<B>)>>` will include disabled entities.
19
20 use bevy::ecs::entity_disabling::Disabled;
21 use bevy::prelude::*;
22
23 fn main() {
24 App::new()
25 .add_plugins((DefaultPlugins, MeshPickingPlugin))
26 .add_observer(disable_entities_on_click)
27 .add_systems(
28 Update,
29 (list_all_named_entities, reenable_entities_on_space),
30 )
31 .add_systems(Startup, (setup_scene, display_instructions))
32 .run();
33 }
34
35 #[derive(Component)]
36 struct DisableOnClick;
37
38 fn disable_entities_on_click(
39 click: On<PointerClick>,
40 valid_query: Query<&DisableOnClick>,
41 mut commands: Commands,
42 ) {
43 // Windows and text are entities and can be clicked!
44 // We definitely don't want to disable the window itself,
45 // because that would cause the app to close!
46 if valid_query.contains(click.entity) {
47 // Just add the `Disabled` component to the entity to disable it.
48 // Note that the `Disabled` component is *only* added to the entity,
49 // its children are not affected.
50 commands.entity(click.entity).insert(Disabled);
51 }
52 }
53
54 #[derive(Component)]
55 struct EntityNameText;
56
57 // The query here will not find entities with the `Disabled` component,
58 // because it does not explicitly include it.
59 fn list_all_named_entities(
60 query: Query<&Name>,
61 mut name_text_query: Query<&mut Text, With<EntityNameText>>,
62 mut commands: Commands,
63 ) {
64 let mut text_string = String::from("Named entities found:\n");
65 // Query iteration order is not guaranteed, so we sort the names
66 // to ensure the output is consistent.
67 for name in query.iter().sort::<&Name>() {
68 text_string.push_str(&format!("{name:?}\n"));
69 }
70
71 if let Ok(mut text) = name_text_query.single_mut() {
72 *text = Text::new(text_string);
73 } else {
74 commands.spawn((
75 EntityNameText,
76 Text::default(),
77 Node {
78 position_type: PositionType::Absolute,
79 top: px(12),
80 right: px(12),
81 ..default()
82 },
83 ));
84 }
85 }
86
87 fn reenable_entities_on_space(
88 mut commands: Commands,
89 // This query can find disabled entities,
90 // because it explicitly includes the `Disabled` component.
91 disabled_entities: Query<Entity, With<Disabled>>,
92 input: Res<ButtonInput<KeyCode>>,
93 ) {
94 if input.just_pressed(KeyCode::Space) {
95 for entity in disabled_entities.iter() {
96 // To re-enable an entity, just remove the `Disabled` component.
97 commands.entity(entity).remove::<Disabled>();
98 }
99 }
100 }
101
102 const X_EXTENT: f32 = 900.;
103
104 fn setup_scene(
105 mut commands: Commands,
106 mut meshes: ResMut<Assets<Mesh>>,
107 mut materials: ResMut<Assets<ColorMaterial>>,
108 ) {
109 commands.spawn(Camera2d);
110
111 let named_shapes = [
112 (Name::new("Annulus"), meshes.add(Annulus::new(25.0, 50.0))),
113 (
114 Name::new("Bestagon"),
115 meshes.add(RegularPolygon::new(50.0, 6)),
116 ),
117 (Name::new("Rhombus"), meshes.add(Rhombus::new(75.0, 100.0))),
118 ];
119 let num_shapes = named_shapes.len();
120
121 for (i, (name, shape)) in named_shapes.into_iter().enumerate() {
122 // Distribute colors evenly across the rainbow.
123 let color = Color::hsl(360. * i as f32 / num_shapes as f32, 0.95, 0.7);
124
125 commands.spawn((
126 name,
127 DisableOnClick,
128 Mesh2d(shape),
129 MeshMaterial2d(materials.add(color)),
130 Transform::from_xyz(
131 // Distribute shapes from -X_EXTENT/2 to +X_EXTENT/2.
132 -X_EXTENT / 2. + i as f32 / (num_shapes - 1) as f32 * X_EXTENT,
133 0.0,
134 0.0,
135 ),
136 ));
137 }
138 }
139
140 fn display_instructions(mut commands: Commands) {
141 commands.spawn((
142 Text::new(
143 "Click an entity to disable it.\n\nPress Space to re-enable all disabled entities.",
144 ),
145 Node {
146 position_type: PositionType::Absolute,
147 top: px(12),
148 left: px(12),
149 ..default()
150 },
151 ));
152 }