pub struct EntityCommands<'a> { /* private fields */ }
Expand description
A list of commands that will be run to modify an Entity
.
§Note
Most Commands
(and thereby EntityCommands
) are deferred:
when you call the command, if it requires mutable access to the World
(that is, if it removes, adds, or changes something), it’s not executed immediately.
Instead, the command is added to a “command queue.”
The command queue is applied later
when the ApplyDeferred
system runs.
Commands are executed one-by-one so that
each command can have exclusive access to the World
.
§Fallible
Due to their deferred nature, an entity you’re trying to change with an EntityCommand
can be despawned by the time the command is executed.
All deferred entity commands will check whether the entity exists at the time of execution and will return an error if it doesn’t.
§Error handling
An EntityCommand
can return a Result
,
which will be passed to an error handler if the Result
is an error.
The default error handler panics. It can be configured via
the DefaultErrorHandler
resource.
Alternatively, you can customize the error handler for a specific command
by calling EntityCommands::queue_handled
.
The error
module provides some simple error handlers for convenience.
Implementations§
Source§impl<'a> EntityCommands<'a>
impl<'a> EntityCommands<'a>
Sourcepub fn with_children(
&mut self,
func: impl FnOnce(&mut RelatedSpawnerCommands<'_, ChildOf>),
) -> &mut EntityCommands<'a>
pub fn with_children( &mut self, func: impl FnOnce(&mut RelatedSpawnerCommands<'_, ChildOf>), ) -> &mut EntityCommands<'a>
Spawns children of this entity (with a ChildOf
relationship) by taking a function that operates on a ChildSpawner
.
Examples found in repository?
27fn setup(mut commands: Commands) {
28 commands
29 .spawn((Name::new("Goblin"), HitPoints(50)))
30 .observe(take_damage)
31 .with_children(|parent| {
32 parent
33 .spawn((Name::new("Helmet"), Armor(5)))
34 .observe(block_attack);
35 parent
36 .spawn((Name::new("Socks"), Armor(10)))
37 .observe(block_attack);
38 parent
39 .spawn((Name::new("Shirt"), Armor(15)))
40 .observe(block_attack);
41 });
42}
More examples
144fn print_logs(
145 mut log_message_reader: MessageReader<LogMessage>,
146 mut commands: Commands,
147 log_viewer_root: Single<Entity, With<LogViewerRoot>>,
148) {
149 let root_entity = *log_viewer_root;
150
151 commands.entity(root_entity).with_children(|child| {
152 for log_message in log_message_reader.read() {
153 child.spawn((
154 Text::default(),
155 children![
156 (
157 TextSpan::new(format!("{:5} ", log_message.level)),
158 TextColor(level_color(&log_message.level)),
159 ),
160 TextSpan::new(&log_message.message),
161 ],
162 ));
163 }
164 });
165}
85fn setup(mut commands: Commands, asset_server: Res<AssetServer>, mut state: ResMut<State>) {
86 let font_handle = asset_server.load("fonts/FiraSans-Bold.ttf");
87 state.handle = font_handle.clone();
88 commands.spawn(Camera2d);
89 commands
90 .spawn((
91 Node {
92 position_type: PositionType::Absolute,
93 bottom: Val::ZERO,
94 ..default()
95 },
96 BackgroundColor(Color::NONE),
97 ))
98 .with_children(|parent| {
99 parent.spawn((
100 Text::new("a"),
101 TextFont {
102 font: font_handle,
103 font_size: 50.0,
104 ..default()
105 },
106 TextColor(YELLOW.into()),
107 ));
108 });
109 // We're seeding the PRNG here to make this example deterministic for testing purposes.
110 // This isn't strictly required in practical use unless you need your app to be deterministic.
111 commands.insert_resource(SeededRng(ChaCha8Rng::seed_from_u64(19878367467713)));
112}
64fn setup(mut commands: Commands, args: Res<Args>) {
65 warn!(include_str!("warning_string.txt"));
66
67 commands.spawn(Camera2d);
68 let text_string = "0123456789".repeat(10_000);
69 let text_font = TextFont {
70 font_size: 4.,
71 ..Default::default()
72 };
73 let text_block = TextLayout {
74 justify: Justify::Left,
75 linebreak: LineBreak::AnyCharacter,
76 };
77
78 if !args.no_ui {
79 commands
80 .spawn(Node {
81 width: percent(100),
82 align_items: AlignItems::Center,
83 justify_content: JustifyContent::Center,
84 ..default()
85 })
86 .with_children(|commands| {
87 commands
88 .spawn(Node {
89 width: px(1000),
90 ..Default::default()
91 })
92 .with_child((Text(text_string.clone()), text_font.clone(), text_block));
93 });
94 }
95
96 if !args.no_text2d {
97 commands.spawn((
98 Text2d::new(text_string),
99 text_font.clone(),
100 TextColor(RED.into()),
101 bevy::sprite::Anchor::CENTER,
102 TextBounds::new_horizontal(1000.),
103 text_block,
104 ));
105 }
106}
34fn spawn(mut commands: Commands, asset_server: Res<AssetServer>) {
35 warn!(include_str!("warning_string.txt"));
36
37 commands.spawn(Camera2d);
38
39 let make_spans = |i| {
40 [
41 (
42 TextSpan("text".repeat(i)),
43 TextFont {
44 font: asset_server.load("fonts/FiraMono-Medium.ttf"),
45 font_size: (4 + i % 10) as f32,
46 ..Default::default()
47 },
48 TextColor(BLUE.into()),
49 ),
50 (
51 TextSpan("pipeline".repeat(i)),
52 TextFont {
53 font: asset_server.load("fonts/FiraSans-Bold.ttf"),
54 font_size: (4 + i % 11) as f32,
55 ..default()
56 },
57 TextColor(YELLOW.into()),
58 ),
59 ]
60 };
61
62 let spans = (1..50).flat_map(|i| make_spans(i).into_iter());
63
64 commands
65 .spawn((
66 Text2d::default(),
67 TextLayout {
68 justify: Justify::Center,
69 linebreak: LineBreak::AnyCharacter,
70 },
71 TextBounds::default(),
72 ))
73 .with_children(|p| {
74 for span in spans {
75 p.spawn(span);
76 }
77 });
78}
24fn setup(mut commands: Commands) {
25 // UI camera
26 commands.spawn(Camera2d);
27
28 let message_text = [
29 "T", "e", "x", "t\n", "B", "a", "c", "k", "g", "r", "o", "u", "n", "d\n", "C", "o", "l",
30 "o", "r", "s", "!",
31 ];
32
33 commands
34 .spawn(Node {
35 width: percent(100),
36 height: percent(100),
37 justify_content: JustifyContent::Center,
38 align_items: AlignItems::Center,
39 ..Default::default()
40 })
41 .with_children(|commands| {
42 commands
43 .spawn((
44 Text::default(),
45 TextLayout {
46 justify: Justify::Center,
47 ..Default::default()
48 },
49 ))
50 .with_children(|commands| {
51 for (i, section_str) in message_text.iter().enumerate() {
52 commands.spawn((
53 TextSpan::new(*section_str),
54 TextColor::BLACK,
55 TextFont {
56 font_size: 100.,
57 ..default()
58 },
59 TextBackgroundColor(PALETTE[i % PALETTE.len()]),
60 ));
61 }
62 });
63 });
64}
- examples/ecs/hierarchy.rs
- examples/ui/relative_cursor_position.rs
- examples/ui/ui_material.rs
- examples/ui/ui_scaling.rs
- examples/ui/ui_texture_atlas.rs
- examples/ui/ghost_nodes.rs
- examples/ui/overflow_debug.rs
- examples/stress_tests/many_gradients.rs
- examples/stress_tests/many_buttons.rs
- examples/ui/ui_texture_slice.rs
- examples/math/cubic_splines.rs
- examples/ui/viewport_debug.rs
- examples/ui/ui_texture_slice_flip_and_tile.rs
- examples/ui/size_constraints.rs
- examples/ui/transparency_ui.rs
- examples/ui/ui_texture_atlas_slice.rs
- examples/picking/sprite_picking.rs
- examples/testbed/ui.rs
- examples/ui/stacked_gradients.rs
- examples/ui/overflow.rs
- examples/ui/overflow_clip_margin.rs
- examples/ui/flex_layout.rs
- examples/stress_tests/bevymark.rs
- examples/ui/display_and_visibility.rs
- examples/ui/drag_to_scroll.rs
- examples/ui/tab_navigation.rs
- examples/ui/scroll.rs
- examples/ui/z_index.rs
- examples/ui/render_ui_to_texture.rs
- examples/stress_tests/many_foxes.rs
- examples/games/desk_toy.rs
- examples/ui/ui_drag_and_drop.rs
- examples/ui/text_debug.rs
- examples/ui/grid.rs
- examples/ui/gradients.rs
- examples/testbed/full_ui.rs
Sourcepub fn add_children(&mut self, children: &[Entity]) -> &mut EntityCommands<'a>
pub fn add_children(&mut self, children: &[Entity]) -> &mut EntityCommands<'a>
Adds the given children to this entity
Examples found in repository?
103fn setup_ui(
104 mut commands: Commands,
105 mut directional_nav_map: ResMut<DirectionalNavigationMap>,
106 mut input_focus: ResMut<InputFocus>,
107) {
108 const N_ROWS: u16 = 5;
109 const N_COLS: u16 = 3;
110
111 // Rendering UI elements requires a camera
112 commands.spawn(Camera2d);
113
114 // Create a full-screen background node
115 let root_node = commands
116 .spawn(Node {
117 width: percent(100),
118 height: percent(100),
119 ..default()
120 })
121 .id();
122
123 // Add instruction to the left of the grid
124 let instructions = commands
125 .spawn((
126 Text::new("Use arrow keys or D-pad to navigate. \
127 Click the buttons, or press Enter / the South gamepad button to interact with the focused button."),
128 Node {
129 width: px(300),
130 justify_content: JustifyContent::Center,
131 align_items: AlignItems::Center,
132 margin: UiRect::all(px(12)),
133 ..default()
134 },
135 ))
136 .id();
137
138 // Set up the root entity to hold the grid
139 let grid_root_entity = commands
140 .spawn(Node {
141 display: Display::Grid,
142 // Allow the grid to take up the full height and the rest of the width of the window
143 width: percent(100),
144 height: percent(100),
145 // Set the number of rows and columns in the grid
146 // allowing the grid to automatically size the cells
147 grid_template_columns: RepeatedGridTrack::auto(N_COLS),
148 grid_template_rows: RepeatedGridTrack::auto(N_ROWS),
149 ..default()
150 })
151 .id();
152
153 // Add the instructions and grid to the root node
154 commands
155 .entity(root_node)
156 .add_children(&[instructions, grid_root_entity]);
157
158 let mut button_entities: HashMap<(u16, u16), Entity> = HashMap::default();
159 for row in 0..N_ROWS {
160 for col in 0..N_COLS {
161 let button_name = format!("Button {row}-{col}");
162
163 let button_entity = commands
164 .spawn((
165 Button,
166 Node {
167 width: px(200),
168 height: px(120),
169 // Add a border so we can show which element is focused
170 border: UiRect::all(px(4)),
171 // Center the button's text label
172 justify_content: JustifyContent::Center,
173 align_items: AlignItems::Center,
174 // Center the button within the grid cell
175 align_self: AlignSelf::Center,
176 justify_self: JustifySelf::Center,
177 ..default()
178 },
179 ResetTimer::default(),
180 BorderRadius::all(px(16)),
181 BackgroundColor::from(NORMAL_BUTTON),
182 Name::new(button_name.clone()),
183 ))
184 // Add a text element to the button
185 .with_child((
186 Text::new(button_name),
187 // And center the text if it flows onto multiple lines
188 TextLayout {
189 justify: Justify::Center,
190 ..default()
191 },
192 ))
193 .id();
194
195 // Add the button to the grid
196 commands.entity(grid_root_entity).add_child(button_entity);
197
198 // Keep track of the button entities so we can set up our navigation graph
199 button_entities.insert((row, col), button_entity);
200 }
201 }
202
203 // Connect all of the buttons in the same row to each other,
204 // looping around when the edge is reached.
205 for row in 0..N_ROWS {
206 let entities_in_row: Vec<Entity> = (0..N_COLS)
207 .map(|col| button_entities.get(&(row, col)).unwrap())
208 .copied()
209 .collect();
210 directional_nav_map.add_looping_edges(&entities_in_row, CompassOctant::East);
211 }
212
213 // Connect all of the buttons in the same column to each other,
214 // but don't loop around when the edge is reached.
215 // While looping is a very reasonable choice, we're not doing it here to demonstrate the different options.
216 for col in 0..N_COLS {
217 let entities_in_column: Vec<Entity> = (0..N_ROWS)
218 .map(|row| button_entities.get(&(row, col)).unwrap())
219 .copied()
220 .collect();
221
222 directional_nav_map.add_edges(&entities_in_column, CompassOctant::South);
223 }
224
225 // When changing scenes, remember to set an initial focus!
226 let top_left_entity = *button_entities.get(&(0, 0)).unwrap();
227 input_focus.set(top_left_entity);
228}
More examples
37fn setup(
38 mut commands: Commands,
39 asset_server: Res<AssetServer>,
40 mut meshes: ResMut<Assets<Mesh>>,
41 mut materials: ResMut<Assets<StandardMaterial>>,
42 mut skinned_mesh_inverse_bindposes_assets: ResMut<Assets<SkinnedMeshInverseBindposes>>,
43) {
44 // Create a camera
45 commands.spawn((
46 Camera3d::default(),
47 Transform::from_xyz(2.5, 2.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
48 ));
49
50 // Create inverse bindpose matrices for a skeleton consists of 2 joints
51 let inverse_bindposes = skinned_mesh_inverse_bindposes_assets.add(vec![
52 Mat4::from_translation(Vec3::new(-0.5, -1.0, 0.0)),
53 Mat4::from_translation(Vec3::new(-0.5, -1.0, 0.0)),
54 ]);
55
56 // Create a mesh
57 let mesh = Mesh::new(
58 PrimitiveTopology::TriangleList,
59 RenderAssetUsages::RENDER_WORLD,
60 )
61 // Set mesh vertex positions
62 .with_inserted_attribute(
63 Mesh::ATTRIBUTE_POSITION,
64 vec![
65 [0.0, 0.0, 0.0],
66 [1.0, 0.0, 0.0],
67 [0.0, 0.5, 0.0],
68 [1.0, 0.5, 0.0],
69 [0.0, 1.0, 0.0],
70 [1.0, 1.0, 0.0],
71 [0.0, 1.5, 0.0],
72 [1.0, 1.5, 0.0],
73 [0.0, 2.0, 0.0],
74 [1.0, 2.0, 0.0],
75 ],
76 )
77 // Add UV coordinates that map the left half of the texture since its a 1 x
78 // 2 rectangle.
79 .with_inserted_attribute(
80 Mesh::ATTRIBUTE_UV_0,
81 vec![
82 [0.0, 0.00],
83 [0.5, 0.00],
84 [0.0, 0.25],
85 [0.5, 0.25],
86 [0.0, 0.50],
87 [0.5, 0.50],
88 [0.0, 0.75],
89 [0.5, 0.75],
90 [0.0, 1.00],
91 [0.5, 1.00],
92 ],
93 )
94 // Set mesh vertex normals
95 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, vec![[0.0, 0.0, 1.0]; 10])
96 // Set mesh vertex joint indices for mesh skinning.
97 // Each vertex gets 4 indices used to address the `JointTransforms` array in the vertex shader
98 // as well as `SkinnedMeshJoint` array in the `SkinnedMesh` component.
99 // This means that a maximum of 4 joints can affect a single vertex.
100 .with_inserted_attribute(
101 Mesh::ATTRIBUTE_JOINT_INDEX,
102 // Need to be explicit here as [u16; 4] could be either Uint16x4 or Unorm16x4.
103 VertexAttributeValues::Uint16x4(vec![
104 [0, 0, 0, 0],
105 [0, 0, 0, 0],
106 [0, 1, 0, 0],
107 [0, 1, 0, 0],
108 [0, 1, 0, 0],
109 [0, 1, 0, 0],
110 [0, 1, 0, 0],
111 [0, 1, 0, 0],
112 [0, 1, 0, 0],
113 [0, 1, 0, 0],
114 ]),
115 )
116 // Set mesh vertex joint weights for mesh skinning.
117 // Each vertex gets 4 joint weights corresponding to the 4 joint indices assigned to it.
118 // The sum of these weights should equal to 1.
119 .with_inserted_attribute(
120 Mesh::ATTRIBUTE_JOINT_WEIGHT,
121 vec![
122 [1.00, 0.00, 0.0, 0.0],
123 [1.00, 0.00, 0.0, 0.0],
124 [0.75, 0.25, 0.0, 0.0],
125 [0.75, 0.25, 0.0, 0.0],
126 [0.50, 0.50, 0.0, 0.0],
127 [0.50, 0.50, 0.0, 0.0],
128 [0.25, 0.75, 0.0, 0.0],
129 [0.25, 0.75, 0.0, 0.0],
130 [0.00, 1.00, 0.0, 0.0],
131 [0.00, 1.00, 0.0, 0.0],
132 ],
133 )
134 // Tell bevy to construct triangles from a list of vertex indices,
135 // where each 3 vertex indices form a triangle.
136 .with_inserted_indices(Indices::U16(vec![
137 0, 1, 3, 0, 3, 2, 2, 3, 5, 2, 5, 4, 4, 5, 7, 4, 7, 6, 6, 7, 9, 6, 9, 8,
138 ]));
139
140 let mesh = meshes.add(mesh);
141
142 // We're seeding the PRNG here to make this example deterministic for testing purposes.
143 // This isn't strictly required in practical use unless you need your app to be deterministic.
144 let mut rng = ChaCha8Rng::seed_from_u64(42);
145
146 for i in -5..5 {
147 // Create joint entities
148 let joint_0 = commands
149 .spawn(Transform::from_xyz(
150 i as f32 * 1.5,
151 0.0,
152 // Move quads back a small amount to avoid Z-fighting and not
153 // obscure the transform gizmos.
154 -(i as f32 * 0.01).abs(),
155 ))
156 .id();
157 let joint_1 = commands.spawn((AnimatedJoint(i), Transform::IDENTITY)).id();
158
159 // Set joint_1 as a child of joint_0.
160 commands.entity(joint_0).add_children(&[joint_1]);
161
162 // Each joint in this vector corresponds to each inverse bindpose matrix in `SkinnedMeshInverseBindposes`.
163 let joint_entities = vec![joint_0, joint_1];
164
165 // Create skinned mesh renderer. Note that its transform doesn't affect the position of the mesh.
166 commands.spawn((
167 Mesh3d(mesh.clone()),
168 MeshMaterial3d(materials.add(StandardMaterial {
169 base_color: Color::srgb(
170 rng.random_range(0.0..1.0),
171 rng.random_range(0.0..1.0),
172 rng.random_range(0.0..1.0),
173 ),
174 base_color_texture: Some(asset_server.load("textures/uv_checker_bw.png")),
175 ..default()
176 })),
177 SkinnedMesh {
178 inverse_bindposes: inverse_bindposes.clone(),
179 joints: joint_entities,
180 },
181 ));
182 }
183}
33fn infotext_system(mut commands: Commands, asset_server: Res<AssetServer>) {
34 let font = asset_server.load("fonts/FiraSans-Bold.ttf");
35 let background_color = MAROON.into();
36 commands.spawn(Camera2d);
37
38 let root_uinode = commands
39 .spawn(Node {
40 width: percent(100),
41 height: percent(100),
42 justify_content: JustifyContent::SpaceBetween,
43 ..default()
44 })
45 .id();
46
47 let left_column = commands
48 .spawn(Node {
49 flex_direction: FlexDirection::Column,
50 justify_content: JustifyContent::SpaceBetween,
51 align_items: AlignItems::Start,
52 flex_grow: 1.,
53 margin: UiRect::axes(px(15), px(5)),
54 ..default()
55 }).with_children(|builder| {
56 builder.spawn((
57 Text::new("This is\ntext with\nline breaks\nin the top left."),
58 TextFont {
59 font: font.clone(),
60 font_size: 25.0,
61 ..default()
62 },
63 BackgroundColor(background_color)
64 ));
65 builder.spawn((
66 Text::new(
67 "This text is right-justified. The `Justify` component controls the horizontal alignment of the lines of multi-line text relative to each other, and does not affect the text node's position in the UI layout.",
68 ),
69 TextFont {
70 font: font.clone(),
71 font_size: 25.0,
72 ..default()
73 },
74 TextColor(YELLOW.into()),
75 TextLayout::new_with_justify(Justify::Right),
76 Node {
77 max_width: px(300),
78 ..default()
79 },
80 BackgroundColor(background_color)
81 ));
82 builder.spawn((
83 Text::new(
84 "This\ntext has\nline breaks and also a set width in the bottom left."),
85 TextFont {
86 font: font.clone(),
87 font_size: 25.0,
88 ..default()
89 },
90 Node {
91 max_width: px(300),
92 ..default()
93 },
94 BackgroundColor(background_color)
95 )
96 );
97 }).id();
98
99 let right_column = commands
100 .spawn(Node {
101 flex_direction: FlexDirection::Column,
102 justify_content: JustifyContent::SpaceBetween,
103 align_items: AlignItems::End,
104 flex_grow: 1.,
105 margin: UiRect::axes(px(15), px(5)),
106 ..default()
107 })
108 .with_children(|builder| {
109 builder.spawn((
110 Text::new("This text is very long, has a limited width, is center-justified, is positioned in the top right and is also colored pink."),
111 TextFont {
112 font: font.clone(),
113 font_size: 33.0,
114 ..default()
115 },
116 TextColor(Color::srgb(0.8, 0.2, 0.7)),
117 TextLayout::new_with_justify(Justify::Center),
118 Node {
119 max_width: px(400),
120 ..default()
121 },
122 BackgroundColor(background_color),
123 ));
124
125 builder.spawn((
126 Text::new("This text is left-justified and is vertically positioned to distribute the empty space equally above and below it."),
127 TextFont {
128 font: font.clone(),
129 font_size: 29.0,
130 ..default()
131 },
132 TextColor(YELLOW.into()),
133 TextLayout::new_with_justify(Justify::Left),
134 Node {
135 max_width: px(300),
136 ..default()
137 },
138 BackgroundColor(background_color),
139 ));
140
141 builder.spawn((
142 Text::new("This text is fully justified and is positioned in the same way."),
143 TextFont {
144 font: font.clone(),
145 font_size: 29.0,
146 ..default()
147 },
148 TextLayout::new_with_justify(Justify::Justified),
149 TextColor(GREEN_YELLOW.into()),
150 Node {
151 max_width: px(300),
152 ..default()
153 },
154 BackgroundColor(background_color),
155 ));
156
157 builder
158 .spawn((
159 Text::default(),
160 TextFont {
161 font: font.clone(),
162 font_size: 21.0,
163 ..default()
164 },
165 TextChanges,
166 BackgroundColor(background_color),
167 ))
168 .with_children(|p| {
169 p.spawn((
170 TextSpan::new("\nThis text changes in the bottom right"),
171 TextFont {
172 font: font.clone(),
173 font_size: 21.0,
174 ..default()
175 },
176 ));
177 p.spawn((
178 TextSpan::new(" this text has zero font size"),
179 TextFont {
180 font: font.clone(),
181 font_size: 0.0,
182 ..default()
183 },
184 TextColor(BLUE.into()),
185 ));
186 p.spawn((
187 TextSpan::new("\nThis text changes in the bottom right - "),
188 TextFont {
189 font: font.clone(),
190 font_size: 21.0,
191 ..default()
192 },
193 TextColor(RED.into()),
194 ));
195 p.spawn((
196 TextSpan::default(),
197 TextFont {
198 font: font.clone(),
199 font_size: 21.0,
200 ..default()
201 },
202 TextColor(ORANGE_RED.into()),
203 ));
204 p.spawn((
205 TextSpan::new(" fps, "),
206 TextFont {
207 font: font.clone(),
208 font_size: 10.0,
209 ..default()
210 },
211 TextColor(YELLOW.into()),
212 ));
213 p.spawn((
214 TextSpan::default(),
215 TextFont {
216 font: font.clone(),
217 font_size: 21.0,
218 ..default()
219 },
220 TextColor(LIME.into()),
221 ));
222 p.spawn((
223 TextSpan::new(" ms/frame"),
224 TextFont {
225 font: font.clone(),
226 font_size: 42.0,
227 ..default()
228 },
229 TextColor(BLUE.into()),
230 ));
231 p.spawn((
232 TextSpan::new(" this text has negative font size"),
233 TextFont {
234 font: font.clone(),
235 font_size: -42.0,
236 ..default()
237 },
238 TextColor(BLUE.into()),
239 ));
240 });
241 })
242 .id();
243 commands
244 .entity(root_uinode)
245 .add_children(&[left_column, right_column]);
246}
Sourcepub fn clear_children(&mut self) -> &mut EntityCommands<'a>
pub fn clear_children(&mut self) -> &mut EntityCommands<'a>
Removes all the children from this entity.
See also clear_related
Sourcepub fn insert_children(
&mut self,
index: usize,
children: &[Entity],
) -> &mut EntityCommands<'a>
pub fn insert_children( &mut self, index: usize, children: &[Entity], ) -> &mut EntityCommands<'a>
Insert children at specific index.
See also insert_related
.
Sourcepub fn insert_child(
&mut self,
index: usize,
child: Entity,
) -> &mut EntityCommands<'a>
pub fn insert_child( &mut self, index: usize, child: Entity, ) -> &mut EntityCommands<'a>
Insert children at specific index.
See also insert_related
.
Sourcepub fn add_child(&mut self, child: Entity) -> &mut EntityCommands<'a>
pub fn add_child(&mut self, child: Entity) -> &mut EntityCommands<'a>
Adds the given child to this entity
Examples found in repository?
19fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
20 commands.spawn(Camera2d);
21 let texture = asset_server.load("branding/icon.png");
22
23 // Spawn a root entity with no parent
24 let parent = commands
25 .spawn((
26 Sprite::from_image(texture.clone()),
27 Transform::from_scale(Vec3::splat(0.75)),
28 ))
29 // With that entity as a parent, run a lambda that spawns its children
30 .with_children(|parent| {
31 // parent is a ChildSpawnerCommands, which has a similar API to Commands
32 parent.spawn((
33 Transform::from_xyz(250.0, 0.0, 0.0).with_scale(Vec3::splat(0.75)),
34 Sprite {
35 image: texture.clone(),
36 color: BLUE.into(),
37 ..default()
38 },
39 ));
40 })
41 // Store parent entity for next sections
42 .id();
43
44 // Another way is to use the add_child function to add children after the parent
45 // entity has already been spawned.
46 let child = commands
47 .spawn((
48 Sprite {
49 image: texture,
50 color: LIME.into(),
51 ..default()
52 },
53 Transform::from_xyz(0.0, 250.0, 0.0).with_scale(Vec3::splat(0.75)),
54 ))
55 .id();
56
57 // Add child to the parent.
58 commands.entity(parent).add_child(child);
59}
More examples
281fn spawn_small_cubes(
282 commands: &mut Commands,
283 meshes: &mut Assets<Mesh>,
284 materials: &mut Assets<StandardMaterial>,
285) {
286 // Add the cube mesh.
287 let small_cube = meshes.add(Cuboid::new(
288 SMALL_CUBE_SIZE,
289 SMALL_CUBE_SIZE,
290 SMALL_CUBE_SIZE,
291 ));
292
293 // Add the cube material.
294 let small_cube_material = materials.add(StandardMaterial {
295 base_color: SILVER.into(),
296 ..default()
297 });
298
299 // Create the entity that the small cubes will be parented to. This is the
300 // entity that we rotate.
301 let sphere_parent = commands
302 .spawn(Transform::from_translation(Vec3::ZERO))
303 .insert(Visibility::default())
304 .insert(SphereParent)
305 .id();
306
307 // Now we have to figure out where to place the cubes. To do that, we create
308 // a sphere mesh, but we don't add it to the scene. Instead, we inspect the
309 // sphere mesh to find the positions of its vertices, and spawn a small cube
310 // at each one. That way, we end up with a bunch of cubes arranged in a
311 // spherical shape.
312
313 // Create the sphere mesh, and extract the positions of its vertices.
314 let sphere = Sphere::new(OUTER_RADIUS)
315 .mesh()
316 .ico(OUTER_SUBDIVISION_COUNT)
317 .unwrap();
318 let sphere_positions = sphere.attribute(Mesh::ATTRIBUTE_POSITION).unwrap();
319
320 // At each vertex, create a small cube.
321 for sphere_position in sphere_positions.as_float3().unwrap() {
322 let sphere_position = Vec3::from_slice(sphere_position);
323 let small_cube = commands
324 .spawn(Mesh3d(small_cube.clone()))
325 .insert(MeshMaterial3d(small_cube_material.clone()))
326 .insert(Transform::from_translation(sphere_position))
327 .id();
328 commands.entity(sphere_parent).add_child(small_cube);
329 }
330}
271fn setup_node_rects(commands: &mut Commands) {
272 for (node_rect, node_type) in NODE_RECTS.iter().zip(NODE_TYPES.iter()) {
273 let node_string = match *node_type {
274 NodeType::Clip(ref clip) => clip.text,
275 NodeType::Blend(text) => text,
276 };
277
278 let text = commands
279 .spawn((
280 Text::new(node_string),
281 TextFont {
282 font_size: 16.0,
283 ..default()
284 },
285 TextColor(ANTIQUE_WHITE.into()),
286 TextLayout::new_with_justify(Justify::Center),
287 ))
288 .id();
289
290 let container = {
291 let mut container = commands.spawn((
292 Node {
293 position_type: PositionType::Absolute,
294 bottom: px(node_rect.bottom),
295 left: px(node_rect.left),
296 height: px(node_rect.height),
297 width: px(node_rect.width),
298 align_items: AlignItems::Center,
299 justify_items: JustifyItems::Center,
300 align_content: AlignContent::Center,
301 justify_content: JustifyContent::Center,
302 ..default()
303 },
304 BorderColor::all(WHITE),
305 Outline::new(px(1), Val::ZERO, Color::WHITE),
306 ));
307
308 if let NodeType::Clip(clip) = node_type {
309 container.insert((
310 Interaction::None,
311 RelativeCursorPosition::default(),
312 (*clip).clone(),
313 ));
314 }
315
316 container.id()
317 };
318
319 // Create the background color.
320 if let NodeType::Clip(_) = node_type {
321 let background = commands
322 .spawn((
323 Node {
324 position_type: PositionType::Absolute,
325 top: px(0),
326 left: px(0),
327 height: px(node_rect.height),
328 width: px(node_rect.width),
329 ..default()
330 },
331 BackgroundColor(DARK_GREEN.into()),
332 ))
333 .id();
334
335 commands.entity(container).add_child(background);
336 }
337
338 commands.entity(container).add_child(text);
339 }
340}
525fn create_cubes(
526 image_assets: Res<Assets<Image>>,
527 mut commands: Commands,
528 irradiance_volumes: Query<(&IrradianceVolume, &GlobalTransform)>,
529 voxel_cube_parents: Query<Entity, With<VoxelCubeParent>>,
530 voxel_cubes: Query<Entity, With<VoxelCube>>,
531 example_assets: Res<ExampleAssets>,
532 mut voxel_visualization_material_assets: ResMut<Assets<VoxelVisualizationMaterial>>,
533) {
534 // If voxel cubes have already been spawned, don't do anything.
535 if !voxel_cubes.is_empty() {
536 return;
537 }
538
539 let Some(voxel_cube_parent) = voxel_cube_parents.iter().next() else {
540 return;
541 };
542
543 for (irradiance_volume, global_transform) in irradiance_volumes.iter() {
544 let Some(image) = image_assets.get(&irradiance_volume.voxels) else {
545 continue;
546 };
547
548 let resolution = image.texture_descriptor.size;
549
550 let voxel_cube_material = voxel_visualization_material_assets.add(ExtendedMaterial {
551 base: StandardMaterial::from(Color::from(RED)),
552 extension: VoxelVisualizationExtension {
553 irradiance_volume_info: VoxelVisualizationIrradianceVolumeInfo {
554 world_from_voxel: VOXEL_FROM_WORLD.inverse(),
555 voxel_from_world: VOXEL_FROM_WORLD,
556 resolution: uvec3(
557 resolution.width,
558 resolution.height,
559 resolution.depth_or_array_layers,
560 ),
561 intensity: IRRADIANCE_VOLUME_INTENSITY,
562 },
563 },
564 });
565
566 let scale = vec3(
567 1.0 / resolution.width as f32,
568 1.0 / resolution.height as f32,
569 1.0 / resolution.depth_or_array_layers as f32,
570 );
571
572 // Spawn a cube for each voxel.
573 for z in 0..resolution.depth_or_array_layers {
574 for y in 0..resolution.height {
575 for x in 0..resolution.width {
576 let uvw = (uvec3(x, y, z).as_vec3() + 0.5) * scale - 0.5;
577 let pos = global_transform.transform_point(uvw);
578 let voxel_cube = commands
579 .spawn((
580 Mesh3d(example_assets.voxel_cube.clone()),
581 MeshMaterial3d(voxel_cube_material.clone()),
582 Transform::from_scale(Vec3::splat(VOXEL_CUBE_SCALE))
583 .with_translation(pos),
584 ))
585 .insert(VoxelCube)
586 .insert(NotShadowCaster)
587 .id();
588
589 commands.entity(voxel_cube_parent).add_child(voxel_cube);
590 }
591 }
592 }
593 }
594}
354fn spawn_tree(
355 parent_map: &[usize],
356 commands: &mut Commands,
357 update_filter: &UpdateFilter,
358 root_transform: Transform,
359) -> InsertResult {
360 // total count (# of nodes + root)
361 let count = parent_map.len() + 1;
362
363 #[derive(Default, Clone, Copy)]
364 struct NodeInfo {
365 child_count: u32,
366 depth: u32,
367 }
368
369 // node index -> entity lookup list
370 let mut ents: Vec<Entity> = Vec::with_capacity(count);
371 let mut node_info: Vec<NodeInfo> = vec![default(); count];
372 for (i, &parent_idx) in parent_map.iter().enumerate() {
373 // assert spawn order (parent must be processed before child)
374 assert!(parent_idx <= i, "invalid spawn order");
375 node_info[parent_idx].child_count += 1;
376 }
377
378 // insert root
379 ents.push(commands.spawn(root_transform).id());
380
381 let mut result = InsertResult::default();
382 let mut rng = rand::rng();
383 // used to count through the number of children (used only for visual layout)
384 let mut child_idx: Vec<u16> = vec![0; count];
385
386 // insert children
387 for (current_idx, &parent_idx) in parent_map.iter().enumerate() {
388 let current_idx = current_idx + 1;
389
390 // separation factor to visually separate children (0..1)
391 let sep = child_idx[parent_idx] as f32 / node_info[parent_idx].child_count as f32;
392 child_idx[parent_idx] += 1;
393
394 // calculate and set depth
395 // this works because it's guaranteed that we have already iterated over the parent
396 let depth = node_info[parent_idx].depth + 1;
397 let info = &mut node_info[current_idx];
398 info.depth = depth;
399
400 // update max depth of tree
401 result.maximum_depth = result.maximum_depth.max(depth.try_into().unwrap());
402
403 // insert child
404 let child_entity = {
405 let mut cmd = commands.spawn_empty();
406
407 // check whether or not to update this node
408 let update = (rng.random::<f32>() <= update_filter.probability)
409 && (depth >= update_filter.min_depth && depth <= update_filter.max_depth);
410
411 if update {
412 cmd.insert(UpdateValue(sep));
413 result.active_nodes += 1;
414 }
415
416 let transform = {
417 let mut translation = Vec3::ZERO;
418 // use the same placement fn as the `update` system
419 // this way the entities won't be all at (0, 0, 0) when they don't have an `Update` component
420 set_translation(&mut translation, sep);
421 Transform::from_translation(translation)
422 };
423
424 // only insert the components necessary for the transform propagation
425 cmd.insert(transform);
426
427 cmd.id()
428 };
429
430 commands.entity(ents[parent_idx]).add_child(child_entity);
431
432 ents.push(child_entity);
433 }
434
435 result.inserted_nodes = ents.len();
436 result
437}
44fn spawn(mut commands: Commands, asset_server: Res<AssetServer>) {
45 commands.spawn(Camera2d);
46
47 let text_font = TextFont {
48 font: asset_server.load("fonts/FiraSans-Bold.ttf"),
49 font_size: 12.0,
50 ..default()
51 };
52
53 let root = commands
54 .spawn((
55 Node {
56 width: percent(100),
57 height: percent(100),
58 flex_direction: FlexDirection::Column,
59 ..default()
60 },
61 BackgroundColor(Color::BLACK),
62 ))
63 .id();
64
65 for linebreak in [
66 LineBreak::AnyCharacter,
67 LineBreak::WordBoundary,
68 LineBreak::WordOrCharacter,
69 LineBreak::NoWrap,
70 ] {
71 let row_id = commands
72 .spawn(Node {
73 flex_direction: FlexDirection::Row,
74 justify_content: JustifyContent::SpaceAround,
75 align_items: AlignItems::Center,
76 width: percent(100),
77 height: percent(50),
78 ..default()
79 })
80 .id();
81
82 let justifications = vec![
83 JustifyContent::Center,
84 JustifyContent::FlexStart,
85 JustifyContent::FlexEnd,
86 JustifyContent::SpaceAround,
87 JustifyContent::SpaceBetween,
88 JustifyContent::SpaceEvenly,
89 ];
90
91 for (i, justification) in justifications.into_iter().enumerate() {
92 let c = 0.3 + i as f32 * 0.1;
93 let column_id = commands
94 .spawn((
95 Node {
96 justify_content: justification,
97 flex_direction: FlexDirection::Column,
98 width: percent(16),
99 height: percent(95),
100 overflow: Overflow::clip_x(),
101 ..default()
102 },
103 BackgroundColor(Color::srgb(0.5, c, 1.0 - c)),
104 ))
105 .id();
106
107 let messages = [
108 format!("JustifyContent::{justification:?}"),
109 format!("LineBreakOn::{linebreak:?}"),
110 "Line 1\nLine 2".to_string(),
111 "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas auctor, nunc ac faucibus fringilla.".to_string(),
112 "pneumonoultramicroscopicsilicovolcanoconiosis".to_string()
113 ];
114
115 for (j, message) in messages.into_iter().enumerate() {
116 commands.entity(column_id).with_child((
117 Text(message.clone()),
118 text_font.clone(),
119 TextLayout::new(Justify::Left, linebreak),
120 BackgroundColor(Color::srgb(0.8 - j as f32 * 0.2, 0., 0.)),
121 ));
122 }
123 commands.entity(row_id).add_child(column_id);
124 }
125 commands.entity(root).add_child(row_id);
126 }
127}
Sourcepub fn remove_children(
&mut self,
children: &[Entity],
) -> &mut EntityCommands<'a>
pub fn remove_children( &mut self, children: &[Entity], ) -> &mut EntityCommands<'a>
Removes the relationship between this entity and the given entities.
Sourcepub fn remove_child(&mut self, child: Entity) -> &mut EntityCommands<'a>
pub fn remove_child(&mut self, child: Entity) -> &mut EntityCommands<'a>
Removes the relationship between this entity and the given entity.
Sourcepub fn replace_children(
&mut self,
children: &[Entity],
) -> &mut EntityCommands<'a>
pub fn replace_children( &mut self, children: &[Entity], ) -> &mut EntityCommands<'a>
Replaces the children on this entity with a new list of children.
Sourcepub fn replace_children_with_difference(
&mut self,
entities_to_unrelate: &[Entity],
entities_to_relate: &[Entity],
newly_related_entities: &[Entity],
) -> &mut EntityCommands<'a>
pub fn replace_children_with_difference( &mut self, entities_to_unrelate: &[Entity], entities_to_relate: &[Entity], newly_related_entities: &[Entity], ) -> &mut EntityCommands<'a>
Replaces all the related entities with a new set of entities.
§Warning
Failing to maintain the functions invariants may lead to erratic engine behavior including random crashes.
Refer to EntityWorldMut::replace_related_with_difference
for a list of these invariants.
§Panics
Panics when debug assertions are enabled if an invariant is broken and the command is executed.
Sourcepub fn with_child(&mut self, bundle: impl Bundle) -> &mut EntityCommands<'a>
pub fn with_child(&mut self, bundle: impl Bundle) -> &mut EntityCommands<'a>
Spawns the passed bundle and adds it to this entity as a child.
For efficient spawning of multiple children, use with_children
.
Examples found in repository?
35fn setup_ui(mut commands: Commands) {
36 // Node that fills entire background
37 commands
38 .spawn(Node {
39 width: percent(100),
40 ..default()
41 })
42 // Text where we display current resolution
43 .with_child((
44 Text::new("Resolution"),
45 TextFont {
46 font_size: 42.0,
47 ..default()
48 },
49 ResolutionText,
50 ));
51}
More examples
190fn spawn_text(mut commands: Commands) {
191 commands
192 .spawn(Node {
193 position_type: PositionType::Absolute,
194 bottom: px(12),
195 left: px(12),
196 ..default()
197 })
198 .with_child(Text::new(concat!(
199 "Move the camera with your mouse.\n",
200 "Press arrow up to decrease the FOV of the world model.\n",
201 "Press arrow down to increase the FOV of the world model."
202 )));
203}
138fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
139 commands.spawn(Camera2d);
140
141 let text_style = TextFont {
142 font: asset_server.load("fonts/FiraSans-Bold.ttf"),
143 font_size: 60.0,
144 ..default()
145 };
146
147 commands
148 .spawn((
149 Text::new("Contributor showcase"),
150 text_style.clone(),
151 ContributorDisplay,
152 Node {
153 position_type: PositionType::Absolute,
154 top: px(12),
155 left: px(12),
156 ..default()
157 },
158 ))
159 .with_child((
160 TextSpan::default(),
161 TextFont {
162 font_size: 30.,
163 ..text_style
164 },
165 ));
166}
71fn setup(mut commands: Commands) {
72 let level_data = LevelData {
73 unload_level_id: commands.register_system(unload_current_level),
74 level_1_id: commands.register_system(load_level_1),
75 level_2_id: commands.register_system(load_level_2),
76 };
77 commands.insert_resource(level_data);
78
79 // Spawns the UI that will show the user prompts.
80 let text_style = TextFont {
81 font_size: 42.0,
82 ..default()
83 };
84 commands
85 .spawn((
86 Node {
87 justify_self: JustifySelf::Center,
88 align_self: AlignSelf::FlexEnd,
89 ..default()
90 },
91 BackgroundColor(Color::NONE),
92 ))
93 .with_child((Text::new("Press 1 or 2 to load a new scene."), text_style));
94}
95
96// Selects the level you want to load.
97fn level_selection(
98 mut commands: Commands,
99 keyboard: Res<ButtonInput<KeyCode>>,
100 level_data: Res<LevelData>,
101 loading_state: Res<LoadingState>,
102) {
103 // Only trigger a load if the current level is fully loaded.
104 if let LoadingState::LevelReady = loading_state.as_ref() {
105 if keyboard.just_pressed(KeyCode::Digit1) {
106 commands.run_system(level_data.unload_level_id);
107 commands.run_system(level_data.level_1_id);
108 } else if keyboard.just_pressed(KeyCode::Digit2) {
109 commands.run_system(level_data.unload_level_id);
110 commands.run_system(level_data.level_2_id);
111 }
112 }
113}
114
115// Marker component for easier deletion of entities.
116#[derive(Component)]
117struct LevelComponents;
118
119// Removes all currently loaded level assets from the game World.
120fn unload_current_level(
121 mut commands: Commands,
122 mut loading_state: ResMut<LoadingState>,
123 entities: Query<Entity, With<LevelComponents>>,
124) {
125 *loading_state = LoadingState::LevelLoading;
126 for entity in entities.iter() {
127 commands.entity(entity).despawn();
128 }
129}
130
131fn load_level_1(
132 mut commands: Commands,
133 mut loading_data: ResMut<LoadingData>,
134 asset_server: Res<AssetServer>,
135) {
136 // Spawn the camera.
137 commands.spawn((
138 Camera3d::default(),
139 Transform::from_xyz(155.0, 155.0, 155.0).looking_at(Vec3::new(0.0, 40.0, 0.0), Vec3::Y),
140 LevelComponents,
141 ));
142
143 // Save the asset into the `loading_assets` vector.
144 let fox = asset_server.load(GltfAssetLabel::Scene(0).from_asset("models/animated/Fox.glb"));
145 loading_data.loading_assets.push(fox.clone().into());
146 // Spawn the fox.
147 commands.spawn((
148 SceneRoot(fox.clone()),
149 Transform::from_xyz(0.0, 0.0, 0.0),
150 LevelComponents,
151 ));
152
153 // Spawn the light.
154 commands.spawn((
155 DirectionalLight {
156 shadows_enabled: true,
157 ..default()
158 },
159 Transform::from_xyz(3.0, 3.0, 2.0).looking_at(Vec3::ZERO, Vec3::Y),
160 LevelComponents,
161 ));
162}
163
164fn load_level_2(
165 mut commands: Commands,
166 mut loading_data: ResMut<LoadingData>,
167 asset_server: Res<AssetServer>,
168) {
169 // Spawn the camera.
170 commands.spawn((
171 Camera3d::default(),
172 Transform::from_xyz(1.0, 1.0, 1.0).looking_at(Vec3::new(0.0, 0.2, 0.0), Vec3::Y),
173 LevelComponents,
174 ));
175
176 // Spawn the helmet.
177 let helmet_scene = asset_server
178 .load(GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf"));
179 loading_data
180 .loading_assets
181 .push(helmet_scene.clone().into());
182 commands.spawn((SceneRoot(helmet_scene.clone()), LevelComponents));
183
184 // Spawn the light.
185 commands.spawn((
186 DirectionalLight {
187 shadows_enabled: true,
188 ..default()
189 },
190 Transform::from_xyz(3.0, 3.0, 2.0).looking_at(Vec3::ZERO, Vec3::Y),
191 LevelComponents,
192 ));
193}
194
195// Monitors current loading status of assets.
196fn update_loading_data(
197 mut loading_data: ResMut<LoadingData>,
198 mut loading_state: ResMut<LoadingState>,
199 asset_server: Res<AssetServer>,
200 pipelines_ready: Res<PipelinesReady>,
201) {
202 if !loading_data.loading_assets.is_empty() || !pipelines_ready.0 {
203 // If we are still loading assets / pipelines are not fully compiled,
204 // we reset the confirmation frame count.
205 loading_data.confirmation_frames_count = 0;
206
207 loading_data.loading_assets.retain(|asset| {
208 asset_server
209 .get_recursive_dependency_load_state(asset)
210 .is_none_or(|state| !state.is_loaded())
211 });
212
213 // If there are no more assets being monitored, and pipelines
214 // are compiled, then start counting confirmation frames.
215 // Once enough confirmations have passed, everything will be
216 // considered to be fully loaded.
217 } else {
218 loading_data.confirmation_frames_count += 1;
219 if loading_data.confirmation_frames_count == loading_data.confirmation_frames_target {
220 *loading_state = LoadingState::LevelReady;
221 }
222 }
223}
224
225// Marker tag for loading screen components.
226#[derive(Component)]
227struct LoadingScreen;
228
229// Spawns the necessary components for the loading screen.
230fn load_loading_screen(mut commands: Commands) {
231 let text_style = TextFont {
232 font_size: 67.0,
233 ..default()
234 };
235
236 // Spawn the UI and Loading screen camera.
237 commands.spawn((
238 Camera2d,
239 Camera {
240 order: 1,
241 ..default()
242 },
243 LoadingScreen,
244 ));
245
246 // Spawn the UI that will make up the loading screen.
247 commands
248 .spawn((
249 Node {
250 height: percent(100),
251 width: percent(100),
252 justify_content: JustifyContent::Center,
253 align_items: AlignItems::Center,
254 ..default()
255 },
256 BackgroundColor(Color::BLACK),
257 LoadingScreen,
258 ))
259 .with_child((Text::new("Loading..."), text_style.clone()));
260}
64fn setup(mut commands: Commands, args: Res<Args>) {
65 warn!(include_str!("warning_string.txt"));
66
67 commands.spawn(Camera2d);
68 let text_string = "0123456789".repeat(10_000);
69 let text_font = TextFont {
70 font_size: 4.,
71 ..Default::default()
72 };
73 let text_block = TextLayout {
74 justify: Justify::Left,
75 linebreak: LineBreak::AnyCharacter,
76 };
77
78 if !args.no_ui {
79 commands
80 .spawn(Node {
81 width: percent(100),
82 align_items: AlignItems::Center,
83 justify_content: JustifyContent::Center,
84 ..default()
85 })
86 .with_children(|commands| {
87 commands
88 .spawn(Node {
89 width: px(1000),
90 ..Default::default()
91 })
92 .with_child((Text(text_string.clone()), text_font.clone(), text_block));
93 });
94 }
95
96 if !args.no_text2d {
97 commands.spawn((
98 Text2d::new(text_string),
99 text_font.clone(),
100 TextColor(RED.into()),
101 bevy::sprite::Anchor::CENTER,
102 TextBounds::new_horizontal(1000.),
103 text_block,
104 ));
105 }
106}
454 pub fn setup(mut commands: Commands) {
455 commands.spawn((Camera2d, DespawnOnExit(super::Scene::TextWrap)));
456
457 let root = commands
458 .spawn((
459 Node {
460 flex_direction: FlexDirection::Column,
461 width: px(200),
462 height: percent(100),
463 overflow: Overflow::clip_x(),
464 ..default()
465 },
466 BackgroundColor(Color::BLACK),
467 DespawnOnExit(super::Scene::TextWrap),
468 ))
469 .id();
470
471 for linebreak in [
472 LineBreak::AnyCharacter,
473 LineBreak::WordBoundary,
474 LineBreak::WordOrCharacter,
475 LineBreak::NoWrap,
476 ] {
477 let messages = [
478 "Lorem ipsum dolor sit amet, consectetur adipiscing elit.".to_string(),
479 "pneumonoultramicroscopicsilicovolcanoconiosis".to_string(),
480 ];
481
482 for (j, message) in messages.into_iter().enumerate() {
483 commands.entity(root).with_child((
484 Text(message.clone()),
485 TextLayout::new(Justify::Left, linebreak),
486 BackgroundColor(Color::srgb(0.8 - j as f32 * 0.3, 0., 0.)),
487 ));
488 }
489 }
490 }
- examples/stress_tests/many_buttons.rs
- examples/3d/spherical_area_lights.rs
- examples/ui/ui_texture_atlas.rs
- examples/window/multiple_windows.rs
- examples/ui/ghost_nodes.rs
- examples/ui/overflow_debug.rs
- examples/ecs/error_handling.rs
- examples/ui/ui_texture_slice.rs
- examples/ui/size_constraints.rs
- examples/ecs/iter_combinations.rs
- examples/ui/text_wrap_debug.rs
- examples/ui/text.rs
- examples/gizmos/light_gizmos.rs
- examples/ui/overflow_clip_margin.rs
- examples/window/multi_window_text.rs
- examples/ui/directional_navigation.rs
- examples/ui/ui_drag_and_drop.rs
Source§impl<'a> EntityCommands<'a>
impl<'a> EntityCommands<'a>
Spawns a entity related to this entity (with the R
relationship) by taking a bundle
Examples found in repository?
47 fn spawning_entities_with_relationships(mut commands: Commands) {
48 // Calling .id() after spawning an entity will return the `Entity` identifier of the spawned entity,
49 // even though the entity itself is not yet instantiated in the world.
50 // This works because Commands will reserve the entity ID before actually spawning the entity,
51 // through the use of atomic counters.
52 let alice = commands.spawn(Name::new("Alice")).id();
53 // Relations are just components, so we can add them into the bundle that we're spawning.
54 let bob = commands.spawn((Name::new("Bob"), Targeting(alice))).id();
55
56 // The `with_related` and `with_related_entities` helper methods on `EntityCommands` can be used to add relations in a more ergonomic way.
57 let charlie = commands
58 .spawn((Name::new("Charlie"), Targeting(bob)))
59 // The `with_related` method will spawn a bundle with `Targeting` relationship
60 .with_related::<Targeting>(Name::new("James"))
61 // The `with_related_entities` method will automatically add the `Targeting` component to any entities spawned within the closure,
62 // targeting the entity that we're calling `with_related` on.
63 .with_related_entities::<Targeting>(|related_spawner_commands| {
64 // We could spawn multiple entities here, and they would all target `charlie`.
65 related_spawner_commands.spawn(Name::new("Devon"));
66 })
67 .id();
68
69 // Simply inserting the `Targeting` component will automatically create and update the `TargetedBy` component on the target entity.
70 // We can do this at any point; not just when the entity is spawned.
71 commands.entity(alice).insert(Targeting(charlie));
72 }
Spawns entities related to this entity (with the R
relationship) by taking a function that operates on a RelatedSpawner
.
Examples found in repository?
47 fn spawning_entities_with_relationships(mut commands: Commands) {
48 // Calling .id() after spawning an entity will return the `Entity` identifier of the spawned entity,
49 // even though the entity itself is not yet instantiated in the world.
50 // This works because Commands will reserve the entity ID before actually spawning the entity,
51 // through the use of atomic counters.
52 let alice = commands.spawn(Name::new("Alice")).id();
53 // Relations are just components, so we can add them into the bundle that we're spawning.
54 let bob = commands.spawn((Name::new("Bob"), Targeting(alice))).id();
55
56 // The `with_related` and `with_related_entities` helper methods on `EntityCommands` can be used to add relations in a more ergonomic way.
57 let charlie = commands
58 .spawn((Name::new("Charlie"), Targeting(bob)))
59 // The `with_related` method will spawn a bundle with `Targeting` relationship
60 .with_related::<Targeting>(Name::new("James"))
61 // The `with_related_entities` method will automatically add the `Targeting` component to any entities spawned within the closure,
62 // targeting the entity that we're calling `with_related` on.
63 .with_related_entities::<Targeting>(|related_spawner_commands| {
64 // We could spawn multiple entities here, and they would all target `charlie`.
65 related_spawner_commands.spawn(Name::new("Devon"));
66 })
67 .id();
68
69 // Simply inserting the `Targeting` component will automatically create and update the `TargetedBy` component on the target entity.
70 // We can do this at any point; not just when the entity is spawned.
71 commands.entity(alice).insert(Targeting(charlie));
72 }
Relates the given entities to this entity with the relation R
.
See add_one_related
if you want relate only one entity.
Removes the relation R
between this entity and all its related entities.
Relates the given entities to this entity with the relation R
, starting at this particular index.
If the related
has duplicates, a related entity will take the index of its last occurrence in related
.
If the indices go out of bounds, they will be clamped into bounds.
This will not re-order existing related entities unless they are in related
.
Relates the given entity to this with the relation R
.
See add_related
if you want to relate more than one entity.
Removes the relation R
between this entity and the given entities.
Replaces all the related entities with the given set of new related entities.
Replaces all the related entities with a new set of entities.
§Warning
Failing to maintain the functions invariants may lead to erratic engine behavior including random crashes.
Refer to EntityWorldMut::replace_related_with_difference
for a list of these invariants.
§Panics
Panics when debug assertions are enable, an invariant is are broken and the command is executed.
Despawns entities that relate to this one via the given RelationshipTarget
.
This entity will not be despawned.
Examples found in repository?
180fn update_commands(
181 mut commands: Commands,
182 log_commands: Single<Entity, With<LogDiagnosticsCommands>>,
183 status: Res<LogDiagnosticsStatus>,
184 filters: Res<LogDiagnosticsFilters>,
185) {
186 let enabled = *status == LogDiagnosticsStatus::Enabled;
187 let alpha = if enabled { 1. } else { 0.25 };
188 let enabled_color = |enabled| {
189 if enabled {
190 Color::from(palettes::tailwind::GREEN_400)
191 } else {
192 Color::from(palettes::tailwind::RED_400)
193 }
194 };
195 commands
196 .entity(*log_commands)
197 .despawn_related::<Children>()
198 .insert(children![
199 (
200 Node {
201 flex_direction: FlexDirection::Row,
202 column_gap: px(5),
203 ..default()
204 },
205 children![
206 Text::new("[Q] Toggle filtering:"),
207 (
208 Text::new(format!("{:?}", *status)),
209 TextColor(enabled_color(enabled))
210 )
211 ]
212 ),
213 (
214 Node {
215 flex_direction: FlexDirection::Row,
216 column_gap: px(5),
217 ..default()
218 },
219 children![
220 (
221 Text::new("[1] Frame times:"),
222 TextColor(Color::WHITE.with_alpha(alpha))
223 ),
224 (
225 Text::new(format!("{:?}", filters.frame_time)),
226 TextColor(enabled_color(filters.frame_time).with_alpha(alpha))
227 )
228 ]
229 ),
230 (
231 Node {
232 flex_direction: FlexDirection::Row,
233 column_gap: px(5),
234 ..default()
235 },
236 children![
237 (
238 Text::new("[2] Entity count:"),
239 TextColor(Color::WHITE.with_alpha(alpha))
240 ),
241 (
242 Text::new(format!("{:?}", filters.entity_count)),
243 TextColor(enabled_color(filters.entity_count).with_alpha(alpha))
244 )
245 ]
246 ),
247 (
248 Node {
249 flex_direction: FlexDirection::Row,
250 column_gap: px(5),
251 ..default()
252 },
253 children![
254 (
255 Text::new("[3] System info:"),
256 TextColor(Color::WHITE.with_alpha(alpha))
257 ),
258 (
259 Text::new(format!("{:?}", filters.system_info)),
260 TextColor(enabled_color(filters.system_info).with_alpha(alpha))
261 )
262 ]
263 ),
264 (
265 Node {
266 flex_direction: FlexDirection::Row,
267 column_gap: px(5),
268 ..default()
269 },
270 children![
271 (
272 Text::new("[4] Render diagnostics:"),
273 TextColor(Color::WHITE.with_alpha(alpha))
274 ),
275 (
276 Text::new("Private"),
277 TextColor(enabled_color(false).with_alpha(alpha))
278 )
279 ]
280 ),
281 ]);
282}
Sourcepub fn despawn_children(&mut self) -> &mut EntityCommands<'a>
pub fn despawn_children(&mut self) -> &mut EntityCommands<'a>
Despawns the children of this entity. This entity will not be despawned.
This is a specialization of despawn_related
, a more general method for despawning via relationships.
Sourcepub fn insert_recursive<S>(
&mut self,
bundle: impl Bundle + Clone,
) -> &mut EntityCommands<'a>where
S: RelationshipTarget,
pub fn insert_recursive<S>(
&mut self,
bundle: impl Bundle + Clone,
) -> &mut EntityCommands<'a>where
S: RelationshipTarget,
Inserts a component or bundle of components into the entity and all related entities,
traversing the relationship tracked in S
in a breadth-first manner.
§Warning
This method should only be called on relationships that form a tree-like structure. Any cycles will cause this method to loop infinitely.
Sourcepub fn remove_recursive<S, B>(&mut self) -> &mut EntityCommands<'a>where
S: RelationshipTarget,
B: Bundle,
pub fn remove_recursive<S, B>(&mut self) -> &mut EntityCommands<'a>where
S: RelationshipTarget,
B: Bundle,
Removes a component or bundle of components of type B
from the entity and all related entities,
traversing the relationship tracked in S
in a breadth-first manner.
§Warning
This method should only be called on relationships that form a tree-like structure. Any cycles will cause this method to loop infinitely.
Source§impl<'a> EntityCommands<'a>
impl<'a> EntityCommands<'a>
Sourcepub fn id(&self) -> Entity
pub fn id(&self) -> Entity
Returns the Entity
id of the entity.
§Example
fn my_system(mut commands: Commands) {
let entity_id = commands.spawn_empty().id();
}
Examples found in repository?
30fn setup_view_root(mut commands: Commands) {
31 let camera = commands.spawn((Camera::default(), Camera2d)).id();
32
33 commands.spawn((
34 Node {
35 display: Display::Flex,
36 flex_direction: FlexDirection::Column,
37 position_type: PositionType::Absolute,
38 left: px(0),
39 top: px(0),
40 right: px(0),
41 bottom: px(0),
42 padding: UiRect::all(px(3)),
43 row_gap: px(6),
44 ..Default::default()
45 },
46 BackgroundColor(Color::srgb(0.1, 0.1, 0.1)),
47 UiTargetCamera(camera),
48 TabGroup::default(),
49 Children::spawn((Spawn(Text::new("Scrolling")), Spawn(scroll_area_demo()))),
50 ));
51}
More examples
244fn setup_menu(mut commands: Commands) {
245 let button_entity = commands
246 .spawn((
247 Node {
248 // center button
249 width: percent(100),
250 height: percent(100),
251 justify_content: JustifyContent::Center,
252 align_items: AlignItems::Center,
253 ..default()
254 },
255 children![(
256 Button,
257 Node {
258 width: px(150),
259 height: px(65),
260 // horizontally center child text
261 justify_content: JustifyContent::Center,
262 // vertically center child text
263 align_items: AlignItems::Center,
264 ..default()
265 },
266 BackgroundColor(NORMAL_BUTTON),
267 children![(
268 Text::new("Play"),
269 TextFont {
270 font_size: 33.0,
271 ..default()
272 },
273 TextColor(Color::srgb(0.9, 0.9, 0.9)),
274 )]
275 )],
276 ))
277 .id();
278 commands.insert_resource(MenuData { button_entity });
279}
52fn setup_menu(mut commands: Commands) {
53 let button_entity = commands
54 .spawn((
55 Node {
56 // center button
57 width: percent(100),
58 height: percent(100),
59 justify_content: JustifyContent::Center,
60 align_items: AlignItems::Center,
61 ..default()
62 },
63 children![(
64 Button,
65 Node {
66 width: px(150),
67 height: px(65),
68 // horizontally center child text
69 justify_content: JustifyContent::Center,
70 // vertically center child text
71 align_items: AlignItems::Center,
72 ..default()
73 },
74 BackgroundColor(NORMAL_BUTTON),
75 children![(
76 Text::new("Play"),
77 TextFont {
78 font_size: 33.0,
79 ..default()
80 },
81 TextColor(Color::srgb(0.9, 0.9, 0.9)),
82 )],
83 )],
84 ))
85 .id();
86 commands.insert_resource(MenuData { button_entity });
87}
157 pub fn setup_menu(mut commands: Commands) {
158 let button_entity = commands
159 .spawn((
160 Node {
161 // center button
162 width: percent(100),
163 height: percent(100),
164 justify_content: JustifyContent::Center,
165 align_items: AlignItems::Center,
166 ..default()
167 },
168 children![(
169 Button,
170 Node {
171 width: px(150),
172 height: px(65),
173 // horizontally center child text
174 justify_content: JustifyContent::Center,
175 // vertically center child text
176 align_items: AlignItems::Center,
177 ..default()
178 },
179 BackgroundColor(NORMAL_BUTTON),
180 children![(
181 Text::new("Play"),
182 TextFont {
183 font_size: 33.0,
184 ..default()
185 },
186 TextColor(Color::srgb(0.9, 0.9, 0.9)),
187 )]
188 )],
189 ))
190 .id();
191 commands.insert_resource(MenuData { button_entity });
192 }
19fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
20 commands.spawn(Camera2d);
21 let texture = asset_server.load("branding/icon.png");
22
23 // Spawn a root entity with no parent
24 let parent = commands
25 .spawn((
26 Sprite::from_image(texture.clone()),
27 Transform::from_scale(Vec3::splat(0.75)),
28 ))
29 // With that entity as a parent, run a lambda that spawns its children
30 .with_children(|parent| {
31 // parent is a ChildSpawnerCommands, which has a similar API to Commands
32 parent.spawn((
33 Transform::from_xyz(250.0, 0.0, 0.0).with_scale(Vec3::splat(0.75)),
34 Sprite {
35 image: texture.clone(),
36 color: BLUE.into(),
37 ..default()
38 },
39 ));
40 })
41 // Store parent entity for next sections
42 .id();
43
44 // Another way is to use the add_child function to add children after the parent
45 // entity has already been spawned.
46 let child = commands
47 .spawn((
48 Sprite {
49 image: texture,
50 color: LIME.into(),
51 ..default()
52 },
53 Transform::from_xyz(0.0, 250.0, 0.0).with_scale(Vec3::splat(0.75)),
54 ))
55 .id();
56
57 // Add child to the parent.
58 commands.entity(parent).add_child(child);
59}
83fn setup_contributor_selection(
84 mut commands: Commands,
85 asset_server: Res<AssetServer>,
86 mut rng: ResMut<SharedRng>,
87) {
88 let contribs = contributors_or_fallback();
89
90 let texture_handle = asset_server.load("branding/icon.png");
91
92 let mut contributor_selection = ContributorSelection {
93 order: Vec::with_capacity(contribs.len()),
94 idx: 0,
95 };
96
97 for (name, num_commits) in contribs {
98 let transform = Transform::from_xyz(
99 rng.random_range(-400.0..400.0),
100 rng.random_range(0.0..400.0),
101 rng.random(),
102 );
103 let dir = rng.random_range(-1.0..1.0);
104 let velocity = Vec3::new(dir * 500.0, 0.0, 0.0);
105 let hue = name_to_hue(&name);
106
107 // Some sprites should be flipped for variety
108 let flipped = rng.random();
109
110 let entity = commands
111 .spawn((
112 Contributor {
113 name,
114 num_commits,
115 hue,
116 },
117 Velocity {
118 translation: velocity,
119 rotation: -dir * 5.0,
120 },
121 Sprite {
122 image: texture_handle.clone(),
123 custom_size: Some(Vec2::splat(SPRITE_SIZE)),
124 color: DESELECTED.with_hue(hue).into(),
125 flip_x: flipped,
126 ..default()
127 },
128 transform,
129 ))
130 .id();
131
132 contributor_selection.order.push(entity);
133 }
134
135 commands.insert_resource(contributor_selection);
136}
- examples/ecs/observers.rs
- examples/window/multiple_windows.rs
- examples/ecs/relationships.rs
- examples/3d/occlusion_culling.rs
- examples/animation/animated_ui.rs
- examples/animation/eased_motion.rs
- examples/ui/viewport_node.rs
- examples/async_tasks/async_compute.rs
- examples/animation/animation_graph.rs
- examples/window/monitor_info.rs
- examples/3d/irradiance_volumes.rs
- examples/state/computed_states.rs
- examples/stress_tests/transform_hierarchy.rs
- examples/ui/text_wrap_debug.rs
- examples/stress_tests/many_buttons.rs
- examples/testbed/ui.rs
- examples/games/alien_cake_addict.rs
- examples/ui/ui_target_camera.rs
- examples/window/multi_window_text.rs
- examples/3d/split_screen.rs
- tests/3d/test_invalid_skinned_mesh.rs
- examples/ui/render_ui_to_texture.rs
- examples/stress_tests/many_foxes.rs
- examples/ui/display_and_visibility.rs
- examples/ui/directional_navigation.rs
- examples/animation/custom_skinned_mesh.rs
- examples/3d/blend_modes.rs
- examples/animation/animated_transform.rs
- examples/ui/text_debug.rs
- examples/ui/gradients.rs
Sourcepub fn reborrow(&mut self) -> EntityCommands<'_>
pub fn reborrow(&mut self) -> EntityCommands<'_>
Returns an EntityCommands
with a smaller lifetime.
This is useful if you have &mut EntityCommands
but you need EntityCommands
.
Sourcepub fn entry<T>(&mut self) -> EntityEntryCommands<'_, T>where
T: Component,
pub fn entry<T>(&mut self) -> EntityEntryCommands<'_, T>where
T: Component,
Get an EntityEntryCommands
for the Component
T
,
allowing you to modify it or insert it if it isn’t already present.
See also insert_if_new
,
which lets you insert a Bundle
without overwriting it.
§Example
#[derive(Component)]
struct Level(u32);
#[derive(Component, Default)]
struct Mana {
max: u32,
current: u32,
}
fn level_up_system(mut commands: Commands, player: Res<PlayerEntity>) {
// If a component already exists then modify it, otherwise insert a default value
commands
.entity(player.entity)
.entry::<Level>()
.and_modify(|mut lvl| lvl.0 += 1)
.or_insert(Level(0));
// Add a default value if none exists, and then modify the existing or new value
commands
.entity(player.entity)
.entry::<Mana>()
.or_default()
.and_modify(|mut mana| {
mana.max += 10;
mana.current = mana.max;
});
}
Sourcepub fn insert(&mut self, bundle: impl Bundle) -> &mut EntityCommands<'a>
pub fn insert(&mut self, bundle: impl Bundle) -> &mut EntityCommands<'a>
Adds a Bundle
of components to the entity.
This will overwrite any previous value(s) of the same component type.
See EntityCommands::insert_if_new
to keep the old value instead.
§Example
#[derive(Component)]
struct Health(u32);
#[derive(Component)]
struct Strength(u32);
#[derive(Component)]
struct Defense(u32);
#[derive(Bundle)]
struct CombatBundle {
health: Health,
strength: Strength,
}
fn add_combat_stats_system(mut commands: Commands, player: Res<PlayerEntity>) {
commands
.entity(player.entity)
// You can insert individual components:
.insert(Defense(10))
// You can also insert pre-defined bundles of components:
.insert(CombatBundle {
health: Health(100),
strength: Strength(40),
})
// You can also insert tuples of components and bundles.
// This is equivalent to the calls above:
.insert((
Defense(10),
CombatBundle {
health: Health(100),
strength: Strength(40),
},
));
}
Examples found in repository?
157fn spawn_camera(commands: &mut Commands) {
158 commands
159 .spawn(Camera3d::default())
160 .insert(Transform::from_xyz(-0.7, 0.7, 1.0).looking_at(vec3(0.0, 0.3, 0.0), Vec3::Y));
161}
162
163/// Spawns the scene.
164///
165/// The scene is loaded from a glTF file.
166fn spawn_scene(commands: &mut Commands, asset_server: &AssetServer) {
167 commands
168 .spawn(SceneRoot(
169 asset_server.load(
170 GltfAssetLabel::Scene(0)
171 .from_asset("models/MixedLightingExample/MixedLightingExample.gltf"),
172 ),
173 ))
174 .observe(
175 |_: On<SceneInstanceReady>,
176 mut lighting_mode_changed_writer: MessageWriter<LightingModeChanged>| {
177 // When the scene loads, send a `LightingModeChanged` event so
178 // that we set up the lightmaps.
179 lighting_mode_changed_writer.write(LightingModeChanged);
180 },
181 );
182}
183
184/// Spawns the buttons that allow the user to change the lighting mode.
185fn spawn_buttons(commands: &mut Commands) {
186 commands.spawn((
187 widgets::main_ui_node(),
188 children![widgets::option_buttons(
189 "Lighting",
190 &[
191 (LightingMode::Baked, "Baked"),
192 (LightingMode::MixedDirect, "Mixed (Direct)"),
193 (LightingMode::MixedIndirect, "Mixed (Indirect)"),
194 (LightingMode::RealTime, "Real-Time"),
195 ],
196 )],
197 ));
198}
199
200/// Spawns the help text at the top of the window.
201fn spawn_help_text(commands: &mut Commands, app_status: &AppStatus) {
202 commands.spawn((
203 create_help_text(app_status),
204 Node {
205 position_type: PositionType::Absolute,
206 top: px(12),
207 left: px(12),
208 ..default()
209 },
210 HelpText,
211 ));
212}
213
214/// Adds lightmaps to and/or removes lightmaps from objects in the scene when
215/// the lighting mode changes.
216///
217/// This is also called right after the scene loads in order to set up the
218/// lightmaps.
219fn update_lightmaps(
220 mut commands: Commands,
221 asset_server: Res<AssetServer>,
222 mut materials: ResMut<Assets<StandardMaterial>>,
223 meshes: Query<(Entity, &GltfMeshName, &MeshMaterial3d<StandardMaterial>), With<Mesh3d>>,
224 mut lighting_mode_changed_reader: MessageReader<LightingModeChanged>,
225 app_status: Res<AppStatus>,
226) {
227 // Only run if the lighting mode changed. (Note that a change event is fired
228 // when the scene first loads.)
229 if lighting_mode_changed_reader.read().next().is_none() {
230 return;
231 }
232
233 // Select the lightmap to use, based on the lighting mode.
234 let lightmap: Option<Handle<Image>> = match app_status.lighting_mode {
235 LightingMode::Baked => {
236 Some(asset_server.load("lightmaps/MixedLightingExample-Baked.zstd.ktx2"))
237 }
238 LightingMode::MixedDirect => {
239 Some(asset_server.load("lightmaps/MixedLightingExample-MixedDirect.zstd.ktx2"))
240 }
241 LightingMode::MixedIndirect => {
242 Some(asset_server.load("lightmaps/MixedLightingExample-MixedIndirect.zstd.ktx2"))
243 }
244 LightingMode::RealTime => None,
245 };
246
247 'outer: for (entity, name, material) in &meshes {
248 // Add lightmaps to or remove lightmaps from the scenery objects in the
249 // scene (all objects but the sphere).
250 //
251 // Note that doing a linear search through the `LIGHTMAPS` array is
252 // inefficient, but we do it anyway in this example to improve clarity.
253 for (lightmap_name, uv_rect) in LIGHTMAPS {
254 if &**name != lightmap_name {
255 continue;
256 }
257
258 // Lightmap exposure defaults to zero, so we need to set it.
259 if let Some(ref mut material) = materials.get_mut(material) {
260 material.lightmap_exposure = LIGHTMAP_EXPOSURE;
261 }
262
263 // Add or remove the lightmap.
264 match lightmap {
265 Some(ref lightmap) => {
266 commands.entity(entity).insert(Lightmap {
267 image: (*lightmap).clone(),
268 uv_rect,
269 bicubic_sampling: false,
270 });
271 }
272 None => {
273 commands.entity(entity).remove::<Lightmap>();
274 }
275 }
276 continue 'outer;
277 }
278
279 // Add lightmaps to or remove lightmaps from the sphere.
280 if &**name == "Sphere" {
281 // Lightmap exposure defaults to zero, so we need to set it.
282 if let Some(ref mut material) = materials.get_mut(material) {
283 material.lightmap_exposure = LIGHTMAP_EXPOSURE;
284 }
285
286 // Add or remove the lightmap from the sphere. We only apply the
287 // lightmap in fully-baked mode.
288 match (&lightmap, app_status.lighting_mode) {
289 (Some(lightmap), LightingMode::Baked) => {
290 commands.entity(entity).insert(Lightmap {
291 image: (*lightmap).clone(),
292 uv_rect: SPHERE_UV_RECT,
293 bicubic_sampling: false,
294 });
295 }
296 _ => {
297 commands.entity(entity).remove::<Lightmap>();
298 }
299 }
300 }
301 }
302}
303
304/// Converts a uv rectangle from the OpenGL coordinate system (origin in the
305/// lower left) to the Vulkan coordinate system (origin in the upper left) that
306/// Bevy uses.
307///
308/// For this particular example, the baking tool happened to use the OpenGL
309/// coordinate system, so it was more convenient to do the conversion at compile
310/// time than to pre-calculate and hard-code the values.
311const fn uv_rect_opengl(gl_min: Vec2, size: Vec2) -> Rect {
312 let min = vec2(gl_min.x, 1.0 - gl_min.y - size.y);
313 Rect {
314 min,
315 max: vec2(min.x + size.x, min.y + size.y),
316 }
317}
318
319/// Ensures that clicking on the scene to move the sphere doesn't result in a
320/// hit on the sphere itself.
321fn make_sphere_nonpickable(
322 mut commands: Commands,
323 mut query: Query<(Entity, &Name), (With<Mesh3d>, Without<Pickable>)>,
324) {
325 for (sphere, name) in &mut query {
326 if &**name == "Sphere" {
327 commands.entity(sphere).insert(Pickable::IGNORE);
328 }
329 }
330}
331
332/// Updates the directional light settings as necessary when the lighting mode
333/// changes.
334fn update_directional_light(
335 mut lights: Query<&mut DirectionalLight>,
336 mut lighting_mode_changed_reader: MessageReader<LightingModeChanged>,
337 app_status: Res<AppStatus>,
338) {
339 // Only run if the lighting mode changed. (Note that a change event is fired
340 // when the scene first loads.)
341 if lighting_mode_changed_reader.read().next().is_none() {
342 return;
343 }
344
345 // Real-time direct light is used on the scenery if we're using mixed
346 // indirect or real-time mode.
347 let scenery_is_lit_in_real_time = matches!(
348 app_status.lighting_mode,
349 LightingMode::MixedIndirect | LightingMode::RealTime
350 );
351
352 for mut light in &mut lights {
353 light.affects_lightmapped_mesh_diffuse = scenery_is_lit_in_real_time;
354 // Don't bother enabling shadows if they won't show up on the scenery.
355 light.shadows_enabled = scenery_is_lit_in_real_time;
356 }
357}
358
359/// Updates the state of the selection widgets at the bottom of the window when
360/// the lighting mode changes.
361fn update_radio_buttons(
362 mut widgets: Query<
363 (
364 Entity,
365 Option<&mut BackgroundColor>,
366 Has<Text>,
367 &WidgetClickSender<LightingMode>,
368 ),
369 Or<(With<RadioButton>, With<RadioButtonText>)>,
370 >,
371 app_status: Res<AppStatus>,
372 mut writer: TextUiWriter,
373) {
374 for (entity, image, has_text, sender) in &mut widgets {
375 let selected = **sender == app_status.lighting_mode;
376
377 if let Some(mut bg_color) = image {
378 widgets::update_ui_radio_button(&mut bg_color, selected);
379 }
380 if has_text {
381 widgets::update_ui_radio_button_text(entity, &mut writer, selected);
382 }
383 }
384}
385
386/// Handles clicks on the widgets at the bottom of the screen and fires
387/// [`LightingModeChanged`] events.
388fn handle_lighting_mode_change(
389 mut widget_click_event_reader: MessageReader<WidgetClickEvent<LightingMode>>,
390 mut lighting_mode_changed_writer: MessageWriter<LightingModeChanged>,
391 mut app_status: ResMut<AppStatus>,
392) {
393 for event in widget_click_event_reader.read() {
394 app_status.lighting_mode = **event;
395 lighting_mode_changed_writer.write(LightingModeChanged);
396 }
397}
398
399/// Moves the sphere to its original position when the user selects the baked
400/// lighting mode.
401///
402/// As the light from the sphere is precomputed and depends on the sphere's
403/// original position, the sphere must be placed there in order for the lighting
404/// to be correct.
405fn reset_sphere_position(
406 mut objects: Query<(&Name, &mut Transform)>,
407 mut lighting_mode_changed_reader: MessageReader<LightingModeChanged>,
408 app_status: Res<AppStatus>,
409) {
410 // Only run if the lighting mode changed and if the lighting mode is
411 // `LightingMode::Baked`. (Note that a change event is fired when the scene
412 // first loads.)
413 if lighting_mode_changed_reader.read().next().is_none()
414 || app_status.lighting_mode != LightingMode::Baked
415 {
416 return;
417 }
418
419 for (name, mut transform) in &mut objects {
420 if &**name == "Sphere" {
421 transform.translation = INITIAL_SPHERE_POSITION;
422 break;
423 }
424 }
425}
426
427/// Updates the position of the sphere when the user clicks on a spot in the
428/// scene.
429///
430/// Note that the position of the sphere is locked in baked lighting mode.
431fn move_sphere(
432 mouse_button_input: Res<ButtonInput<MouseButton>>,
433 pointers: Query<&PointerInteraction>,
434 mut meshes: Query<(&GltfMeshName, &ChildOf), With<Mesh3d>>,
435 mut transforms: Query<&mut Transform>,
436 app_status: Res<AppStatus>,
437) {
438 // Only run when the left button is clicked and we're not in baked lighting
439 // mode.
440 if app_status.lighting_mode == LightingMode::Baked
441 || !mouse_button_input.pressed(MouseButton::Left)
442 {
443 return;
444 }
445
446 // Find the sphere.
447 let Some(child_of) = meshes
448 .iter_mut()
449 .filter_map(|(name, child_of)| {
450 if &**name == "Sphere" {
451 Some(child_of)
452 } else {
453 None
454 }
455 })
456 .next()
457 else {
458 return;
459 };
460
461 // Grab its transform.
462 let Ok(mut transform) = transforms.get_mut(child_of.parent()) else {
463 return;
464 };
465
466 // Set its transform to the appropriate position, as determined by the
467 // picking subsystem.
468 for interaction in pointers.iter() {
469 if let Some(&(
470 _,
471 HitData {
472 position: Some(position),
473 ..
474 },
475 )) = interaction.get_nearest_hit()
476 {
477 transform.translation = position + vec3(0.0, SPHERE_OFFSET, 0.0);
478 }
479 }
480}
481
482/// Changes the help text at the top of the screen when the lighting mode
483/// changes.
484fn adjust_help_text(
485 mut commands: Commands,
486 help_texts: Query<Entity, With<HelpText>>,
487 app_status: Res<AppStatus>,
488 mut lighting_mode_changed_reader: MessageReader<LightingModeChanged>,
489) {
490 if lighting_mode_changed_reader.read().next().is_none() {
491 return;
492 }
493
494 for help_text in &help_texts {
495 commands
496 .entity(help_text)
497 .insert(create_help_text(&app_status));
498 }
499}
More examples
209fn spawn_camera(commands: &mut Commands) {
210 commands
211 .spawn(Camera3d::default())
212 .insert(Transform::from_xyz(0.0, 2.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y))
213 // Tag the camera with `Selection::Camera`.
214 .insert(Selection::Camera);
215}
216
217/// Spawns the actual clustered decals.
218fn spawn_decals(commands: &mut Commands, asset_server: &AssetServer) {
219 let image = asset_server.load("branding/icon.png");
220
221 commands.spawn((
222 ClusteredDecal {
223 image: image.clone(),
224 // Tint with red.
225 tag: 1,
226 },
227 calculate_initial_decal_transform(vec3(1.0, 3.0, 5.0), Vec3::ZERO, Vec2::splat(1.1)),
228 Selection::DecalA,
229 ));
230
231 commands.spawn((
232 ClusteredDecal {
233 image: image.clone(),
234 // Tint with blue.
235 tag: 2,
236 },
237 calculate_initial_decal_transform(vec3(-2.0, -1.0, 4.0), Vec3::ZERO, Vec2::splat(2.0)),
238 Selection::DecalB,
239 ));
240}
241
242/// Spawns the buttons at the bottom of the screen.
243fn spawn_buttons(commands: &mut Commands) {
244 // Spawn the radio buttons that allow the user to select an object to
245 // control.
246 commands.spawn((
247 widgets::main_ui_node(),
248 children![widgets::option_buttons(
249 "Drag to Move",
250 &[
251 (Selection::Camera, "Camera"),
252 (Selection::DecalA, "Decal A"),
253 (Selection::DecalB, "Decal B"),
254 ],
255 )],
256 ));
257
258 // Spawn the drag buttons that allow the user to control the scale and roll
259 // of the selected object.
260 commands.spawn((
261 Node {
262 flex_direction: FlexDirection::Row,
263 position_type: PositionType::Absolute,
264 right: px(10),
265 bottom: px(10),
266 column_gap: px(6),
267 ..default()
268 },
269 children![
270 (drag_button("Scale"), DragMode::Scale),
271 (drag_button("Roll"), DragMode::Roll),
272 ],
273 ));
274}
275
276/// Spawns a button that the user can drag to change a parameter.
277fn drag_button(label: &str) -> impl Bundle {
278 (
279 Node {
280 border: BUTTON_BORDER,
281 justify_content: JustifyContent::Center,
282 align_items: AlignItems::Center,
283 padding: BUTTON_PADDING,
284 ..default()
285 },
286 Button,
287 BackgroundColor(Color::BLACK),
288 BorderRadius::all(BUTTON_BORDER_RADIUS_SIZE),
289 BUTTON_BORDER_COLOR,
290 children![widgets::ui_text(label, Color::WHITE)],
291 )
292}
293
294/// Spawns the help text at the top of the screen.
295fn spawn_help_text(commands: &mut Commands, app_status: &AppStatus) {
296 commands.spawn((
297 Text::new(create_help_string(app_status)),
298 Node {
299 position_type: PositionType::Absolute,
300 top: px(12),
301 left: px(12),
302 ..default()
303 },
304 HelpText,
305 ));
306}
307
308/// Draws the outlines that show the bounds of the clustered decals.
309fn draw_gizmos(
310 mut gizmos: Gizmos,
311 decals: Query<(&GlobalTransform, &Selection), With<ClusteredDecal>>,
312) {
313 for (global_transform, selection) in &decals {
314 let color = match *selection {
315 Selection::Camera => continue,
316 Selection::DecalA => ORANGE_RED,
317 Selection::DecalB => LIME,
318 };
319
320 gizmos.primitive_3d(
321 &Cuboid {
322 // Since the clustered decal is a 1×1×1 cube in model space, its
323 // half-size is half of the scaling part of its transform.
324 half_size: global_transform.scale() * 0.5,
325 },
326 Isometry3d {
327 rotation: global_transform.rotation(),
328 translation: global_transform.translation_vec3a(),
329 },
330 color,
331 );
332 }
333}
334
335/// Calculates the initial transform of the clustered decal.
336fn calculate_initial_decal_transform(start: Vec3, looking_at: Vec3, size: Vec2) -> Transform {
337 let direction = looking_at - start;
338 let center = start + direction * 0.5;
339 Transform::from_translation(center)
340 .with_scale((size * 0.5).extend(direction.length()))
341 .looking_to(direction, Vec3::Y)
342}
343
344/// Rotates the cube a bit every frame.
345fn rotate_cube(mut meshes: Query<&mut Transform, With<Mesh3d>>) {
346 for mut transform in &mut meshes {
347 transform.rotate_y(CUBE_ROTATION_SPEED);
348 }
349}
350
351/// Updates the state of the radio buttons when the user clicks on one.
352fn update_radio_buttons(
353 mut widgets: Query<(
354 Entity,
355 Option<&mut BackgroundColor>,
356 Has<Text>,
357 &WidgetClickSender<Selection>,
358 )>,
359 app_status: Res<AppStatus>,
360 mut writer: TextUiWriter,
361) {
362 for (entity, maybe_bg_color, has_text, sender) in &mut widgets {
363 let selected = app_status.selection == **sender;
364 if let Some(mut bg_color) = maybe_bg_color {
365 widgets::update_ui_radio_button(&mut bg_color, selected);
366 }
367 if has_text {
368 widgets::update_ui_radio_button_text(entity, &mut writer, selected);
369 }
370 }
371}
372
373/// Changes the selection when the user clicks a radio button.
374fn handle_selection_change(
375 mut events: MessageReader<WidgetClickEvent<Selection>>,
376 mut app_status: ResMut<AppStatus>,
377) {
378 for event in events.read() {
379 app_status.selection = **event;
380 }
381}
382
383/// Process a drag event that moves the selected object.
384fn process_move_input(
385 mut selections: Query<(&mut Transform, &Selection)>,
386 mouse_buttons: Res<ButtonInput<MouseButton>>,
387 mouse_motion: Res<AccumulatedMouseMotion>,
388 app_status: Res<AppStatus>,
389) {
390 // Only process drags when movement is selected.
391 if !mouse_buttons.pressed(MouseButton::Left) || app_status.drag_mode != DragMode::Move {
392 return;
393 }
394
395 for (mut transform, selection) in &mut selections {
396 if app_status.selection != *selection {
397 continue;
398 }
399
400 let position = transform.translation;
401
402 // Convert to spherical coordinates.
403 let radius = position.length();
404 let mut theta = acos(position.y / radius);
405 let mut phi = position.z.signum() * acos(position.x * position.xz().length_recip());
406
407 // Camera movement is the inverse of object movement.
408 let (phi_factor, theta_factor) = match *selection {
409 Selection::Camera => (1.0, -1.0),
410 Selection::DecalA | Selection::DecalB => (-1.0, 1.0),
411 };
412
413 // Adjust the spherical coordinates. Clamp the inclination to (0, π).
414 phi += phi_factor * mouse_motion.delta.x * MOVE_SPEED;
415 theta = f32::clamp(
416 theta + theta_factor * mouse_motion.delta.y * MOVE_SPEED,
417 0.001,
418 PI - 0.001,
419 );
420
421 // Convert spherical coordinates back to Cartesian coordinates.
422 transform.translation =
423 radius * vec3(sin(theta) * cos(phi), cos(theta), sin(theta) * sin(phi));
424
425 // Look at the center, but preserve the previous roll angle.
426 let roll = transform.rotation.to_euler(EulerRot::YXZ).2;
427 transform.look_at(Vec3::ZERO, Vec3::Y);
428 let (yaw, pitch, _) = transform.rotation.to_euler(EulerRot::YXZ);
429 transform.rotation = Quat::from_euler(EulerRot::YXZ, yaw, pitch, roll);
430 }
431}
432
433/// Processes a drag event that scales the selected target.
434fn process_scale_input(
435 mut selections: Query<(&mut Transform, &Selection)>,
436 mouse_buttons: Res<ButtonInput<MouseButton>>,
437 mouse_motion: Res<AccumulatedMouseMotion>,
438 app_status: Res<AppStatus>,
439) {
440 // Only process drags when the scaling operation is selected.
441 if !mouse_buttons.pressed(MouseButton::Left) || app_status.drag_mode != DragMode::Scale {
442 return;
443 }
444
445 for (mut transform, selection) in &mut selections {
446 if app_status.selection == *selection {
447 transform.scale *= 1.0 + mouse_motion.delta.x * SCALE_SPEED;
448 }
449 }
450}
451
452/// Processes a drag event that rotates the selected target along its local Z
453/// axis.
454fn process_roll_input(
455 mut selections: Query<(&mut Transform, &Selection)>,
456 mouse_buttons: Res<ButtonInput<MouseButton>>,
457 mouse_motion: Res<AccumulatedMouseMotion>,
458 app_status: Res<AppStatus>,
459) {
460 // Only process drags when the rolling operation is selected.
461 if !mouse_buttons.pressed(MouseButton::Left) || app_status.drag_mode != DragMode::Roll {
462 return;
463 }
464
465 for (mut transform, selection) in &mut selections {
466 if app_status.selection != *selection {
467 continue;
468 }
469
470 let (yaw, pitch, mut roll) = transform.rotation.to_euler(EulerRot::YXZ);
471 roll += mouse_motion.delta.x * ROLL_SPEED;
472 transform.rotation = Quat::from_euler(EulerRot::YXZ, yaw, pitch, roll);
473 }
474}
475
476/// Creates the help string at the top left of the screen.
477fn create_help_string(app_status: &AppStatus) -> String {
478 format!(
479 "Click and drag to {} {}",
480 app_status.drag_mode, app_status.selection
481 )
482}
483
484/// Changes the drag mode when the user hovers over the "Scale" and "Roll"
485/// buttons in the lower right.
486///
487/// If the user is hovering over no such button, this system changes the drag
488/// mode back to its default value of [`DragMode::Move`].
489fn switch_drag_mode(
490 mut commands: Commands,
491 mut interactions: Query<(&Interaction, &DragMode)>,
492 mut windows: Query<Entity, With<Window>>,
493 mouse_buttons: Res<ButtonInput<MouseButton>>,
494 mut app_status: ResMut<AppStatus>,
495) {
496 if mouse_buttons.pressed(MouseButton::Left) {
497 return;
498 }
499
500 for (interaction, drag_mode) in &mut interactions {
501 if *interaction != Interaction::Hovered {
502 continue;
503 }
504
505 app_status.drag_mode = *drag_mode;
506
507 // Set the cursor to provide the user with a nice visual hint.
508 for window in &mut windows {
509 commands
510 .entity(window)
511 .insert(CursorIcon::from(SystemCursorIcon::EwResize));
512 }
513 return;
514 }
515
516 app_status.drag_mode = DragMode::Move;
517
518 for window in &mut windows {
519 commands.entity(window).remove::<CursorIcon>();
520 }
521}
220fn spawn_camera(commands: &mut Commands) {
221 commands
222 .spawn(Camera3d::default())
223 .insert(Transform::from_xyz(0.0, 2.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y))
224 // Tag the camera with `Selection::Camera`.
225 .insert(Selection::Camera);
226}
227
228fn spawn_light_textures(
229 commands: &mut Commands,
230 asset_server: &AssetServer,
231 meshes: &mut Assets<Mesh>,
232 materials: &mut Assets<StandardMaterial>,
233) {
234 commands.spawn((
235 SpotLight {
236 color: Color::srgb(1.0, 1.0, 0.8),
237 intensity: 10e6,
238 outer_angle: 0.25,
239 inner_angle: 0.25,
240 shadows_enabled: true,
241 ..default()
242 },
243 Transform::from_translation(Vec3::new(6.0, 1.0, 2.0)).looking_at(Vec3::ZERO, Vec3::Y),
244 SpotLightTexture {
245 image: asset_server.load("lightmaps/torch_spotlight_texture.png"),
246 },
247 Visibility::Inherited,
248 Selection::SpotLight,
249 ));
250
251 commands.spawn((
252 Visibility::Hidden,
253 Transform::from_translation(Vec3::new(0.0, 1.8, 0.01)).with_scale(Vec3::splat(0.1)),
254 Selection::PointLight,
255 children![
256 SceneRoot(
257 asset_server.load(GltfAssetLabel::Scene(0).from_asset("models/Faces/faces.glb")),
258 ),
259 (
260 Mesh3d(meshes.add(Sphere::new(1.0))),
261 MeshMaterial3d(materials.add(StandardMaterial {
262 emissive: Color::srgb(0.0, 0.0, 300.0).to_linear(),
263 ..default()
264 })),
265 ),
266 (
267 PointLight {
268 color: Color::srgb(0.0, 0.0, 1.0),
269 intensity: 1e6,
270 shadows_enabled: true,
271 ..default()
272 },
273 PointLightTexture {
274 image: asset_server.load("lightmaps/faces_pointlight_texture_blurred.png"),
275 cubemap_layout: CubemapLayout::CrossVertical,
276 },
277 )
278 ],
279 ));
280}
281
282/// Spawns the buttons at the bottom of the screen.
283fn spawn_buttons(commands: &mut Commands) {
284 // Spawn the radio buttons that allow the user to select an object to
285 // control.
286 commands.spawn((
287 widgets::main_ui_node(),
288 children![widgets::option_buttons(
289 "Drag to Move",
290 &[
291 (Selection::Camera, "Camera"),
292 (Selection::SpotLight, "Spotlight"),
293 (Selection::PointLight, "Point Light"),
294 (Selection::DirectionalLight, "Directional Light"),
295 ],
296 )],
297 ));
298
299 // Spawn the drag buttons that allow the user to control the scale and roll
300 // of the selected object.
301 commands.spawn((
302 Node {
303 flex_direction: FlexDirection::Row,
304 position_type: PositionType::Absolute,
305 right: px(10),
306 bottom: px(10),
307 column_gap: px(6),
308 ..default()
309 },
310 children![
311 widgets::option_buttons(
312 "",
313 &[
314 (Visibility::Inherited, "Show"),
315 (Visibility::Hidden, "Hide"),
316 ],
317 ),
318 (drag_button("Scale"), DragMode::Scale),
319 (drag_button("Roll"), DragMode::Roll),
320 ],
321 ));
322}
323
324/// Spawns a button that the user can drag to change a parameter.
325fn drag_button(label: &str) -> impl Bundle {
326 (
327 Node {
328 border: BUTTON_BORDER,
329 justify_content: JustifyContent::Center,
330 align_items: AlignItems::Center,
331 padding: BUTTON_PADDING,
332 ..default()
333 },
334 Button,
335 BackgroundColor(Color::BLACK),
336 BorderRadius::all(BUTTON_BORDER_RADIUS_SIZE),
337 BUTTON_BORDER_COLOR,
338 children![widgets::ui_text(label, Color::WHITE),],
339 )
340}
341
342/// Spawns the help text at the top of the screen.
343fn spawn_help_text(commands: &mut Commands, app_status: &AppStatus) {
344 commands.spawn((
345 Text::new(create_help_string(app_status)),
346 Node {
347 position_type: PositionType::Absolute,
348 top: px(12),
349 left: px(12),
350 ..default()
351 },
352 HelpText,
353 ));
354}
355
356/// Draws the outlines that show the bounds of the spotlight.
357fn draw_gizmos(mut gizmos: Gizmos, spotlight: Query<(&GlobalTransform, &SpotLight, &Visibility)>) {
358 if let Ok((global_transform, spotlight, visibility)) = spotlight.single()
359 && visibility != Visibility::Hidden
360 {
361 gizmos.primitive_3d(
362 &Cone::new(7.0 * spotlight.outer_angle, 7.0),
363 Isometry3d {
364 rotation: global_transform.rotation() * Quat::from_rotation_x(FRAC_PI_2),
365 translation: global_transform.translation_vec3a() * 0.5,
366 },
367 YELLOW,
368 );
369 }
370}
371
372/// Rotates the cube a bit every frame.
373fn rotate_cube(mut meshes: Query<&mut Transform, With<Rotate>>) {
374 for mut transform in &mut meshes {
375 transform.rotate_y(CUBE_ROTATION_SPEED);
376 }
377}
378
379/// Hide shadows on all meshes except the main cube
380fn hide_shadows(
381 mut commands: Commands,
382 meshes: Query<Entity, (With<Mesh3d>, Without<NotShadowCaster>, Without<Rotate>)>,
383) {
384 for ent in &meshes {
385 commands.entity(ent).insert(NotShadowCaster);
386 }
387}
388
389/// Updates the state of the radio buttons when the user clicks on one.
390fn update_radio_buttons(
391 mut widgets: Query<(
392 Entity,
393 Option<&mut BackgroundColor>,
394 Has<Text>,
395 &WidgetClickSender<Selection>,
396 )>,
397 app_status: Res<AppStatus>,
398 mut writer: TextUiWriter,
399 visible: Query<(&Visibility, &Selection)>,
400 mut visibility_widgets: Query<
401 (
402 Entity,
403 Option<&mut BackgroundColor>,
404 Has<Text>,
405 &WidgetClickSender<Visibility>,
406 ),
407 Without<WidgetClickSender<Selection>>,
408 >,
409) {
410 for (entity, maybe_bg_color, has_text, sender) in &mut widgets {
411 let selected = app_status.selection == **sender;
412 if let Some(mut bg_color) = maybe_bg_color {
413 widgets::update_ui_radio_button(&mut bg_color, selected);
414 }
415 if has_text {
416 widgets::update_ui_radio_button_text(entity, &mut writer, selected);
417 }
418 }
419
420 let visibility = visible
421 .iter()
422 .filter(|(_, selection)| **selection == app_status.selection)
423 .map(|(visibility, _)| *visibility)
424 .next()
425 .unwrap_or_default();
426 for (entity, maybe_bg_color, has_text, sender) in &mut visibility_widgets {
427 if let Some(mut bg_color) = maybe_bg_color {
428 widgets::update_ui_radio_button(&mut bg_color, **sender == visibility);
429 }
430 if has_text {
431 widgets::update_ui_radio_button_text(entity, &mut writer, **sender == visibility);
432 }
433 }
434}
435
436/// Changes the selection when the user clicks a radio button.
437fn handle_selection_change(
438 mut events: MessageReader<WidgetClickEvent<Selection>>,
439 mut app_status: ResMut<AppStatus>,
440) {
441 for event in events.read() {
442 app_status.selection = **event;
443 }
444}
445
446fn toggle_visibility(
447 mut events: MessageReader<WidgetClickEvent<Visibility>>,
448 app_status: Res<AppStatus>,
449 mut visibility: Query<(&mut Visibility, &Selection)>,
450) {
451 if let Some(vis) = events.read().last() {
452 for (mut visibility, selection) in visibility.iter_mut() {
453 if selection == &app_status.selection {
454 *visibility = **vis;
455 }
456 }
457 }
458}
459
460/// Process a drag event that moves the selected object.
461fn process_move_input(
462 mut selections: Query<(&mut Transform, &Selection)>,
463 mouse_buttons: Res<ButtonInput<MouseButton>>,
464 mouse_motion: Res<AccumulatedMouseMotion>,
465 app_status: Res<AppStatus>,
466) {
467 // Only process drags when movement is selected.
468 if !mouse_buttons.pressed(MouseButton::Left) || app_status.drag_mode != DragMode::Move {
469 return;
470 }
471
472 for (mut transform, selection) in &mut selections {
473 if app_status.selection != *selection {
474 continue;
475 }
476
477 // use simple movement for the point light
478 if *selection == Selection::PointLight {
479 transform.translation +=
480 (mouse_motion.delta * Vec2::new(1.0, -1.0) * MOVE_SPEED).extend(0.0);
481 return;
482 }
483
484 let position = transform.translation;
485
486 // Convert to spherical coordinates.
487 let radius = position.length();
488 let mut theta = acos(position.y / radius);
489 let mut phi = position.z.signum() * acos(position.x * position.xz().length_recip());
490
491 // Camera movement is the inverse of object movement.
492 let (phi_factor, theta_factor) = match *selection {
493 Selection::Camera => (1.0, -1.0),
494 _ => (-1.0, 1.0),
495 };
496
497 // Adjust the spherical coordinates. Clamp the inclination to (0, π).
498 phi += phi_factor * mouse_motion.delta.x * MOVE_SPEED;
499 theta = f32::clamp(
500 theta + theta_factor * mouse_motion.delta.y * MOVE_SPEED,
501 0.001,
502 PI - 0.001,
503 );
504
505 // Convert spherical coordinates back to Cartesian coordinates.
506 transform.translation =
507 radius * vec3(sin(theta) * cos(phi), cos(theta), sin(theta) * sin(phi));
508
509 // Look at the center, but preserve the previous roll angle.
510 let roll = transform.rotation.to_euler(EulerRot::YXZ).2;
511 transform.look_at(Vec3::ZERO, Vec3::Y);
512 let (yaw, pitch, _) = transform.rotation.to_euler(EulerRot::YXZ);
513 transform.rotation = Quat::from_euler(EulerRot::YXZ, yaw, pitch, roll);
514 }
515}
516
517/// Processes a drag event that scales the selected target.
518fn process_scale_input(
519 mut scale_selections: Query<(&mut Transform, &Selection)>,
520 mut spotlight_selections: Query<(&mut SpotLight, &Selection)>,
521 mouse_buttons: Res<ButtonInput<MouseButton>>,
522 mouse_motion: Res<AccumulatedMouseMotion>,
523 app_status: Res<AppStatus>,
524) {
525 // Only process drags when the scaling operation is selected.
526 if !mouse_buttons.pressed(MouseButton::Left) || app_status.drag_mode != DragMode::Scale {
527 return;
528 }
529
530 for (mut transform, selection) in &mut scale_selections {
531 if app_status.selection == *selection {
532 transform.scale = (transform.scale * (1.0 + mouse_motion.delta.x * SCALE_SPEED))
533 .clamp(Vec3::splat(0.01), Vec3::splat(5.0));
534 }
535 }
536
537 for (mut spotlight, selection) in &mut spotlight_selections {
538 if app_status.selection == *selection {
539 spotlight.outer_angle = (spotlight.outer_angle
540 * (1.0 + mouse_motion.delta.x * SCALE_SPEED))
541 .clamp(0.01, FRAC_PI_4);
542 spotlight.inner_angle = spotlight.outer_angle;
543 }
544 }
545}
546
547/// Processes a drag event that rotates the selected target along its local Z
548/// axis.
549fn process_roll_input(
550 mut selections: Query<(&mut Transform, &Selection)>,
551 mouse_buttons: Res<ButtonInput<MouseButton>>,
552 mouse_motion: Res<AccumulatedMouseMotion>,
553 app_status: Res<AppStatus>,
554) {
555 // Only process drags when the rolling operation is selected.
556 if !mouse_buttons.pressed(MouseButton::Left) || app_status.drag_mode != DragMode::Roll {
557 return;
558 }
559
560 for (mut transform, selection) in &mut selections {
561 if app_status.selection != *selection {
562 continue;
563 }
564
565 let (yaw, pitch, mut roll) = transform.rotation.to_euler(EulerRot::YXZ);
566 roll += mouse_motion.delta.x * ROLL_SPEED;
567 transform.rotation = Quat::from_euler(EulerRot::YXZ, yaw, pitch, roll);
568 }
569}
570
571/// Creates the help string at the top left of the screen.
572fn create_help_string(app_status: &AppStatus) -> String {
573 format!(
574 "Click and drag to {} {}",
575 app_status.drag_mode, app_status.selection
576 )
577}
578
579/// Changes the drag mode when the user hovers over the "Scale" and "Roll"
580/// buttons in the lower right.
581///
582/// If the user is hovering over no such button, this system changes the drag
583/// mode back to its default value of [`DragMode::Move`].
584fn switch_drag_mode(
585 mut commands: Commands,
586 mut interactions: Query<(&Interaction, &DragMode)>,
587 mut windows: Query<Entity, With<Window>>,
588 mouse_buttons: Res<ButtonInput<MouseButton>>,
589 mut app_status: ResMut<AppStatus>,
590) {
591 if mouse_buttons.pressed(MouseButton::Left) {
592 return;
593 }
594
595 for (interaction, drag_mode) in &mut interactions {
596 if *interaction != Interaction::Hovered {
597 continue;
598 }
599
600 app_status.drag_mode = *drag_mode;
601
602 // Set the cursor to provide the user with a nice visual hint.
603 for window in &mut windows {
604 commands
605 .entity(window)
606 .insert(CursorIcon::from(SystemCursorIcon::EwResize));
607 }
608 return;
609 }
610
611 app_status.drag_mode = DragMode::Move;
612
613 for window in &mut windows {
614 commands.entity(window).remove::<CursorIcon>();
615 }
616}
617
618/// Updates the help text in the top left of the screen to reflect the current
619/// selection and drag mode.
620fn update_help_text(mut help_text: Query<&mut Text, With<HelpText>>, app_status: Res<AppStatus>) {
621 for mut text in &mut help_text {
622 text.0 = create_help_string(&app_status);
623 }
624}
625
626/// Updates the visibility of the drag mode buttons so that they aren't visible
627/// if the camera is selected.
628fn update_button_visibility(
629 mut nodes: Query<&mut Visibility, Or<(With<DragMode>, With<WidgetClickSender<Visibility>>)>>,
630 app_status: Res<AppStatus>,
631) {
632 for mut visibility in &mut nodes {
633 *visibility = match app_status.selection {
634 Selection::Camera => Visibility::Hidden,
635 _ => Visibility::Visible,
636 };
637 }
638}
639
640fn update_directional_light(
641 mut commands: Commands,
642 asset_server: Res<AssetServer>,
643 selections: Query<(&Selection, &Visibility)>,
644 mut light: Query<(
645 Entity,
646 &mut DirectionalLight,
647 Option<&DirectionalLightTexture>,
648 )>,
649) {
650 let directional_visible = selections
651 .iter()
652 .filter(|(selection, _)| **selection == Selection::DirectionalLight)
653 .any(|(_, visibility)| visibility != Visibility::Hidden);
654 let any_texture_light_visible = selections
655 .iter()
656 .filter(|(selection, _)| {
657 **selection == Selection::PointLight || **selection == Selection::SpotLight
658 })
659 .any(|(_, visibility)| visibility != Visibility::Hidden);
660
661 let (entity, mut light, maybe_texture) = light
662 .single_mut()
663 .expect("there should be a single directional light");
664
665 if directional_visible {
666 light.illuminance = AMBIENT_DAYLIGHT;
667 if maybe_texture.is_none() {
668 commands.entity(entity).insert(DirectionalLightTexture {
669 image: asset_server.load("lightmaps/caustic_directional_texture.png"),
670 tiled: true,
671 });
672 }
673 } else if any_texture_light_visible {
674 light.illuminance = CLEAR_SUNRISE;
675 if maybe_texture.is_some() {
676 commands.entity(entity).remove::<DirectionalLightTexture>();
677 }
678 } else {
679 light.illuminance = AMBIENT_DAYLIGHT;
680 if maybe_texture.is_some() {
681 commands.entity(entity).remove::<DirectionalLightTexture>();
682 }
683 }
684}
453fn update_widget_values(
454 res: Res<DemoWidgetStates>,
455 mut sliders: Query<Entity, With<DemoSlider>>,
456 mut commands: Commands,
457) {
458 if res.is_changed() {
459 for slider_ent in sliders.iter_mut() {
460 commands
461 .entity(slider_ent)
462 .insert(SliderValue(res.slider_value));
463 }
464 }
465}
466
467fn toggle_disabled(
468 input: Res<ButtonInput<KeyCode>>,
469 mut interaction_query: Query<
470 (Entity, Has<InteractionDisabled>),
471 Or<(With<Button>, With<Slider>, With<Checkbox>)>,
472 >,
473 mut commands: Commands,
474) {
475 if input.just_pressed(KeyCode::KeyD) {
476 for (entity, disabled) in &mut interaction_query {
477 if disabled {
478 info!("Widget enabled");
479 commands.entity(entity).remove::<InteractionDisabled>();
480 } else {
481 info!("Widget disabled");
482 commands.entity(entity).insert(InteractionDisabled);
483 }
484 }
485 }
486}
268fn spawn_sphere(commands: &mut Commands, assets: &ExampleAssets) {
269 commands
270 .spawn((
271 Mesh3d(assets.main_sphere.clone()),
272 MeshMaterial3d(assets.main_sphere_material.clone()),
273 Transform::from_xyz(0.0, SPHERE_SCALE, 0.0).with_scale(Vec3::splat(SPHERE_SCALE)),
274 ))
275 .insert(MainObject);
276}
277
278fn spawn_voxel_cube_parent(commands: &mut Commands) {
279 commands.spawn((Visibility::Hidden, Transform::default(), VoxelCubeParent));
280}
281
282fn spawn_fox(commands: &mut Commands, assets: &ExampleAssets) {
283 commands.spawn((
284 SceneRoot(assets.fox.clone()),
285 Visibility::Hidden,
286 Transform::from_scale(Vec3::splat(FOX_SCALE)),
287 MainObject,
288 ));
289}
290
291fn spawn_text(commands: &mut Commands, app_status: &AppStatus) {
292 commands.spawn((
293 app_status.create_text(),
294 Node {
295 position_type: PositionType::Absolute,
296 bottom: px(12),
297 left: px(12),
298 ..default()
299 },
300 ));
301}
302
303// A system that updates the help text.
304fn update_text(mut text_query: Query<&mut Text>, app_status: Res<AppStatus>) {
305 for mut text in text_query.iter_mut() {
306 *text = app_status.create_text();
307 }
308}
309
310impl AppStatus {
311 // Constructs the help text at the bottom of the screen based on the
312 // application status.
313 fn create_text(&self) -> Text {
314 let irradiance_volume_help_text = if self.irradiance_volume_present {
315 DISABLE_IRRADIANCE_VOLUME_HELP_TEXT
316 } else {
317 ENABLE_IRRADIANCE_VOLUME_HELP_TEXT
318 };
319
320 let voxels_help_text = if self.voxels_visible {
321 HIDE_VOXELS_HELP_TEXT
322 } else {
323 SHOW_VOXELS_HELP_TEXT
324 };
325
326 let rotation_help_text = if self.rotating {
327 STOP_ROTATION_HELP_TEXT
328 } else {
329 START_ROTATION_HELP_TEXT
330 };
331
332 let switch_mesh_help_text = match self.model {
333 ExampleModel::Sphere => SWITCH_TO_FOX_HELP_TEXT,
334 ExampleModel::Fox => SWITCH_TO_SPHERE_HELP_TEXT,
335 };
336
337 format!(
338 "{CLICK_TO_MOVE_HELP_TEXT}\n\
339 {voxels_help_text}\n\
340 {irradiance_volume_help_text}\n\
341 {rotation_help_text}\n\
342 {switch_mesh_help_text}"
343 )
344 .into()
345 }
346}
347
348// Rotates the camera a bit every frame.
349fn rotate_camera(
350 mut camera_query: Query<&mut Transform, With<Camera3d>>,
351 time: Res<Time>,
352 app_status: Res<AppStatus>,
353) {
354 if !app_status.rotating {
355 return;
356 }
357
358 for mut transform in camera_query.iter_mut() {
359 transform.translation = Vec2::from_angle(ROTATION_SPEED * time.delta_secs())
360 .rotate(transform.translation.xz())
361 .extend(transform.translation.y)
362 .xzy();
363 transform.look_at(Vec3::ZERO, Vec3::Y);
364 }
365}
366
367// Toggles between the unskinned sphere model and the skinned fox model if the
368// user requests it.
369fn change_main_object(
370 keyboard: Res<ButtonInput<KeyCode>>,
371 mut app_status: ResMut<AppStatus>,
372 mut sphere_query: Query<&mut Visibility, (With<MainObject>, With<Mesh3d>, Without<SceneRoot>)>,
373 mut fox_query: Query<&mut Visibility, (With<MainObject>, With<SceneRoot>)>,
374) {
375 if !keyboard.just_pressed(KeyCode::Tab) {
376 return;
377 }
378 let Some(mut sphere_visibility) = sphere_query.iter_mut().next() else {
379 return;
380 };
381 let Some(mut fox_visibility) = fox_query.iter_mut().next() else {
382 return;
383 };
384
385 match app_status.model {
386 ExampleModel::Sphere => {
387 *sphere_visibility = Visibility::Hidden;
388 *fox_visibility = Visibility::Visible;
389 app_status.model = ExampleModel::Fox;
390 }
391 ExampleModel::Fox => {
392 *sphere_visibility = Visibility::Visible;
393 *fox_visibility = Visibility::Hidden;
394 app_status.model = ExampleModel::Sphere;
395 }
396 }
397}
398
399impl Default for AppStatus {
400 fn default() -> Self {
401 Self {
402 irradiance_volume_present: true,
403 rotating: true,
404 model: ExampleModel::Sphere,
405 voxels_visible: false,
406 }
407 }
408}
409
410// Turns on and off the irradiance volume as requested by the user.
411fn toggle_irradiance_volumes(
412 mut commands: Commands,
413 keyboard: Res<ButtonInput<KeyCode>>,
414 light_probe_query: Query<Entity, With<LightProbe>>,
415 mut app_status: ResMut<AppStatus>,
416 assets: Res<ExampleAssets>,
417 mut ambient_light: ResMut<AmbientLight>,
418) {
419 if !keyboard.just_pressed(KeyCode::Space) {
420 return;
421 };
422
423 let Some(light_probe) = light_probe_query.iter().next() else {
424 return;
425 };
426
427 if app_status.irradiance_volume_present {
428 commands.entity(light_probe).remove::<IrradianceVolume>();
429 ambient_light.brightness = AMBIENT_LIGHT_BRIGHTNESS * IRRADIANCE_VOLUME_INTENSITY;
430 app_status.irradiance_volume_present = false;
431 } else {
432 commands.entity(light_probe).insert(IrradianceVolume {
433 voxels: assets.irradiance_volume.clone(),
434 intensity: IRRADIANCE_VOLUME_INTENSITY,
435 ..default()
436 });
437 ambient_light.brightness = 0.0;
438 app_status.irradiance_volume_present = true;
439 }
440}
441
442fn toggle_rotation(keyboard: Res<ButtonInput<KeyCode>>, mut app_status: ResMut<AppStatus>) {
443 if keyboard.just_pressed(KeyCode::Enter) {
444 app_status.rotating = !app_status.rotating;
445 }
446}
447
448// Handles clicks on the plane that reposition the object.
449fn handle_mouse_clicks(
450 buttons: Res<ButtonInput<MouseButton>>,
451 windows: Query<&Window, With<PrimaryWindow>>,
452 cameras: Query<(&Camera, &GlobalTransform)>,
453 mut main_objects: Query<&mut Transform, With<MainObject>>,
454) {
455 if !buttons.pressed(MouseButton::Left) {
456 return;
457 }
458 let Some(mouse_position) = windows.iter().next().and_then(Window::cursor_position) else {
459 return;
460 };
461 let Some((camera, camera_transform)) = cameras.iter().next() else {
462 return;
463 };
464
465 // Figure out where the user clicked on the plane.
466 let Ok(ray) = camera.viewport_to_world(camera_transform, mouse_position) else {
467 return;
468 };
469 let Some(ray_distance) = ray.intersect_plane(Vec3::ZERO, InfinitePlane3d::new(Vec3::Y)) else {
470 return;
471 };
472 let plane_intersection = ray.origin + ray.direction.normalize() * ray_distance;
473
474 // Move all the main objects.
475 for mut transform in main_objects.iter_mut() {
476 transform.translation = vec3(
477 plane_intersection.x,
478 transform.translation.y,
479 plane_intersection.z,
480 );
481 }
482}
483
484impl FromWorld for ExampleAssets {
485 fn from_world(world: &mut World) -> Self {
486 let fox_animation =
487 world.load_asset(GltfAssetLabel::Animation(1).from_asset("models/animated/Fox.glb"));
488 let (fox_animation_graph, fox_animation_node) =
489 AnimationGraph::from_clip(fox_animation.clone());
490
491 ExampleAssets {
492 main_sphere: world.add_asset(Sphere::default().mesh().uv(32, 18)),
493 fox: world.load_asset(GltfAssetLabel::Scene(0).from_asset("models/animated/Fox.glb")),
494 main_sphere_material: world.add_asset(Color::from(SILVER)),
495 main_scene: world.load_asset(
496 GltfAssetLabel::Scene(0)
497 .from_asset("models/IrradianceVolumeExample/IrradianceVolumeExample.glb"),
498 ),
499 irradiance_volume: world.load_asset("irradiance_volumes/Example.vxgi.ktx2"),
500 fox_animation_graph: world.add_asset(fox_animation_graph),
501 fox_animation_node,
502 voxel_cube: world.add_asset(Cuboid::default()),
503 // Just use a specular map for the skybox since it's not too blurry.
504 // In reality you wouldn't do this--you'd use a real skybox texture--but
505 // reusing the textures like this saves space in the Bevy repository.
506 skybox: world.load_asset("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
507 }
508 }
509}
510
511// Plays the animation on the fox.
512fn play_animations(
513 mut commands: Commands,
514 assets: Res<ExampleAssets>,
515 mut players: Query<(Entity, &mut AnimationPlayer), Without<AnimationGraphHandle>>,
516) {
517 for (entity, mut player) in players.iter_mut() {
518 commands
519 .entity(entity)
520 .insert(AnimationGraphHandle(assets.fox_animation_graph.clone()));
521 player.play(assets.fox_animation_node).repeat();
522 }
523}
524
525fn create_cubes(
526 image_assets: Res<Assets<Image>>,
527 mut commands: Commands,
528 irradiance_volumes: Query<(&IrradianceVolume, &GlobalTransform)>,
529 voxel_cube_parents: Query<Entity, With<VoxelCubeParent>>,
530 voxel_cubes: Query<Entity, With<VoxelCube>>,
531 example_assets: Res<ExampleAssets>,
532 mut voxel_visualization_material_assets: ResMut<Assets<VoxelVisualizationMaterial>>,
533) {
534 // If voxel cubes have already been spawned, don't do anything.
535 if !voxel_cubes.is_empty() {
536 return;
537 }
538
539 let Some(voxel_cube_parent) = voxel_cube_parents.iter().next() else {
540 return;
541 };
542
543 for (irradiance_volume, global_transform) in irradiance_volumes.iter() {
544 let Some(image) = image_assets.get(&irradiance_volume.voxels) else {
545 continue;
546 };
547
548 let resolution = image.texture_descriptor.size;
549
550 let voxel_cube_material = voxel_visualization_material_assets.add(ExtendedMaterial {
551 base: StandardMaterial::from(Color::from(RED)),
552 extension: VoxelVisualizationExtension {
553 irradiance_volume_info: VoxelVisualizationIrradianceVolumeInfo {
554 world_from_voxel: VOXEL_FROM_WORLD.inverse(),
555 voxel_from_world: VOXEL_FROM_WORLD,
556 resolution: uvec3(
557 resolution.width,
558 resolution.height,
559 resolution.depth_or_array_layers,
560 ),
561 intensity: IRRADIANCE_VOLUME_INTENSITY,
562 },
563 },
564 });
565
566 let scale = vec3(
567 1.0 / resolution.width as f32,
568 1.0 / resolution.height as f32,
569 1.0 / resolution.depth_or_array_layers as f32,
570 );
571
572 // Spawn a cube for each voxel.
573 for z in 0..resolution.depth_or_array_layers {
574 for y in 0..resolution.height {
575 for x in 0..resolution.width {
576 let uvw = (uvec3(x, y, z).as_vec3() + 0.5) * scale - 0.5;
577 let pos = global_transform.transform_point(uvw);
578 let voxel_cube = commands
579 .spawn((
580 Mesh3d(example_assets.voxel_cube.clone()),
581 MeshMaterial3d(voxel_cube_material.clone()),
582 Transform::from_scale(Vec3::splat(VOXEL_CUBE_SCALE))
583 .with_translation(pos),
584 ))
585 .insert(VoxelCube)
586 .insert(NotShadowCaster)
587 .id();
588
589 commands.entity(voxel_cube_parent).add_child(voxel_cube);
590 }
591 }
592 }
593 }
594}
56fn trigger_system(
57 mut commands: Commands,
58 query_a: Single<Entity, With<A>>,
59 query_b: Single<Entity, With<B>>,
60 input: Res<ButtonInput<KeyCode>>,
61) {
62 if input.just_pressed(KeyCode::KeyA) {
63 let entity = *query_a;
64 commands.entity(entity).insert(Triggered);
65 }
66 if input.just_pressed(KeyCode::KeyB) {
67 let entity = *query_b;
68 commands.entity(entity).insert(Triggered);
69 }
70}
- examples/window/screenshot.rs
- examples/3d/reflection_probes.rs
- examples/3d/lightmaps.rs
- examples/ui/tab_navigation.rs
- examples/3d/ssr.rs
- examples/ecs/entity_disabling.rs
- examples/shader_advanced/custom_shader_instancing.rs
- examples/asset/asset_decompression.rs
- examples/animation/morph_targets.rs
- examples/window/window_settings.rs
- examples/stress_tests/many_foxes.rs
- examples/ecs/error_handling.rs
- examples/testbed/3d.rs
- examples/animation/animated_mesh_control.rs
- examples/games/game_menu.rs
- examples/3d/rotate_environment_map.rs
- examples/ui/standard_widgets.rs
- examples/3d/anisotropy.rs
- examples/3d/clearcoat.rs
- examples/usage/cooldown.rs
- examples/math/sampling_primitives.rs
- examples/3d/order_independent_transparency.rs
- examples/3d/pcss.rs
- examples/3d/depth_of_field.rs
- examples/3d/shadow_caster_receiver.rs
- examples/games/stepping.rs
- examples/window/custom_cursor_image.rs
- examples/audio/soundtrack.rs
- examples/ui/relative_cursor_position.rs
- tests/window/desktop_request_redraw.rs
- examples/animation/animated_mesh.rs
- examples/stress_tests/many_buttons.rs
- examples/math/bounding_2d.rs
- examples/ecs/relationships.rs
- examples/3d/occlusion_culling.rs
- examples/animation/animated_ui.rs
- examples/animation/eased_motion.rs
- examples/3d/edit_material_on_gltf.rs
- examples/3d/visibility_range.rs
- examples/3d/volumetric_fog.rs
- examples/testbed/2d.rs
- examples/animation/animated_mesh_events.rs
- examples/async_tasks/async_compute.rs
- examples/animation/animation_graph.rs
- examples/3d/blend_modes.rs
- examples/stress_tests/transform_hierarchy.rs
- examples/animation/animation_masks.rs
- examples/testbed/ui.rs
- examples/3d/deferred_rendering.rs
- examples/diagnostics/log_diagnostics.rs
- examples/3d/solari.rs
- examples/3d/ssao.rs
- tests/3d/test_invalid_skinned_mesh.rs
- examples/3d/bloom_3d.rs
- examples/2d/bloom_2d.rs
- examples/ui/box_shadow.rs
- examples/3d/anti_aliasing.rs
- examples/stress_tests/many_cubes.rs
- examples/animation/animated_transform.rs
- examples/3d/transmission.rs
- examples/ui/feathers.rs
- examples/testbed/full_ui.rs
Sourcepub fn insert_if<F>(
&mut self,
bundle: impl Bundle,
condition: F,
) -> &mut EntityCommands<'a>
pub fn insert_if<F>( &mut self, bundle: impl Bundle, condition: F, ) -> &mut EntityCommands<'a>
Adds a Bundle
of components to the entity if the predicate returns true.
This is useful for chaining method calls.
§Example
#[derive(Component)]
struct StillLoadingStats;
#[derive(Component)]
struct Health(u32);
fn add_health_system(mut commands: Commands, player: Res<PlayerEntity>) {
commands
.entity(player.entity)
.insert_if(Health(10), || !player.is_spectator())
.remove::<StillLoadingStats>();
}
Examples found in repository?
105fn spawn_cars(
106 asset_server: &AssetServer,
107 meshes: &mut Assets<Mesh>,
108 materials: &mut Assets<StandardMaterial>,
109 commands: &mut Commands,
110) {
111 const N_CARS: usize = 20;
112 let box_mesh = meshes.add(Cuboid::new(0.3, 0.15, 0.55));
113 let cylinder = meshes.add(Cylinder::default());
114 let logo = asset_server.load("branding/icon.png");
115 let wheel_matl = materials.add(StandardMaterial {
116 base_color: Color::WHITE,
117 base_color_texture: Some(logo.clone()),
118 ..default()
119 });
120
121 let mut matl = |color| {
122 materials.add(StandardMaterial {
123 base_color: color,
124 ..default()
125 })
126 };
127
128 let colors = [
129 matl(Color::linear_rgb(1.0, 0.0, 0.0)),
130 matl(Color::linear_rgb(1.0, 1.0, 0.0)),
131 matl(Color::BLACK),
132 matl(Color::linear_rgb(0.0, 0.0, 1.0)),
133 matl(Color::linear_rgb(0.0, 1.0, 0.0)),
134 matl(Color::linear_rgb(1.0, 0.0, 1.0)),
135 matl(Color::linear_rgb(0.5, 0.5, 0.0)),
136 matl(Color::linear_rgb(1.0, 0.5, 0.0)),
137 ];
138
139 let make_wheel = |x: f32, z: f32| {
140 (
141 Mesh3d(cylinder.clone()),
142 MeshMaterial3d(wheel_matl.clone()),
143 Transform::from_xyz(0.14 * x, -0.045, 0.15 * z)
144 .with_scale(Vec3::new(0.15, 0.04, 0.15))
145 .with_rotation(Quat::from_rotation_z(std::f32::consts::FRAC_PI_2)),
146 Rotates,
147 )
148 };
149
150 for i in 0..N_CARS {
151 let color = colors[i % colors.len()].clone();
152 commands
153 .spawn((
154 Mesh3d(box_mesh.clone()),
155 MeshMaterial3d(color.clone()),
156 Transform::from_scale(Vec3::splat(0.5)),
157 Moves(i as f32 * 2.0),
158 children![
159 (
160 Mesh3d(box_mesh.clone()),
161 MeshMaterial3d(color),
162 Transform::from_xyz(0.0, 0.08, 0.03).with_scale(Vec3::new(1.0, 1.0, 0.5)),
163 ),
164 make_wheel(1.0, 1.0),
165 make_wheel(1.0, -1.0),
166 make_wheel(-1.0, 1.0),
167 make_wheel(-1.0, -1.0)
168 ],
169 ))
170 .insert_if(CameraTracked, || i == 0);
171 }
172}
More examples
89fn update(
90 camera: Single<
91 (
92 Entity,
93 Option<&ScreenSpaceAmbientOcclusion>,
94 Option<&TemporalJitter>,
95 ),
96 With<Camera>,
97 >,
98 mut text: Single<&mut Text>,
99 mut sphere: Single<&mut Transform, With<SphereMarker>>,
100 mut commands: Commands,
101 keycode: Res<ButtonInput<KeyCode>>,
102 time: Res<Time>,
103) {
104 sphere.translation.y = ops::sin(time.elapsed_secs() / 1.7) * 0.7;
105
106 let (camera_entity, ssao, temporal_jitter) = *camera;
107 let current_ssao = ssao.cloned().unwrap_or_default();
108
109 let mut commands = commands.entity(camera_entity);
110 commands
111 .insert_if(
112 ScreenSpaceAmbientOcclusion {
113 quality_level: ScreenSpaceAmbientOcclusionQualityLevel::Low,
114 ..current_ssao
115 },
116 || keycode.just_pressed(KeyCode::Digit2),
117 )
118 .insert_if(
119 ScreenSpaceAmbientOcclusion {
120 quality_level: ScreenSpaceAmbientOcclusionQualityLevel::Medium,
121 ..current_ssao
122 },
123 || keycode.just_pressed(KeyCode::Digit3),
124 )
125 .insert_if(
126 ScreenSpaceAmbientOcclusion {
127 quality_level: ScreenSpaceAmbientOcclusionQualityLevel::High,
128 ..current_ssao
129 },
130 || keycode.just_pressed(KeyCode::Digit4),
131 )
132 .insert_if(
133 ScreenSpaceAmbientOcclusion {
134 quality_level: ScreenSpaceAmbientOcclusionQualityLevel::Ultra,
135 ..current_ssao
136 },
137 || keycode.just_pressed(KeyCode::Digit5),
138 )
139 .insert_if(
140 ScreenSpaceAmbientOcclusion {
141 constant_object_thickness: (current_ssao.constant_object_thickness * 2.0).min(4.0),
142 ..current_ssao
143 },
144 || keycode.just_pressed(KeyCode::ArrowUp),
145 )
146 .insert_if(
147 ScreenSpaceAmbientOcclusion {
148 constant_object_thickness: (current_ssao.constant_object_thickness * 0.5)
149 .max(0.0625),
150 ..current_ssao
151 },
152 || keycode.just_pressed(KeyCode::ArrowDown),
153 );
154 if keycode.just_pressed(KeyCode::Digit1) {
155 commands.remove::<ScreenSpaceAmbientOcclusion>();
156 }
157 if keycode.just_pressed(KeyCode::Space) {
158 if temporal_jitter.is_some() {
159 commands.remove::<TemporalJitter>();
160 } else {
161 commands.insert(TemporalJitter::default());
162 }
163 }
164
165 text.clear();
166
167 let (o, l, m, h, u) = match ssao.map(|s| s.quality_level) {
168 None => ("*", "", "", "", ""),
169 Some(ScreenSpaceAmbientOcclusionQualityLevel::Low) => ("", "*", "", "", ""),
170 Some(ScreenSpaceAmbientOcclusionQualityLevel::Medium) => ("", "", "*", "", ""),
171 Some(ScreenSpaceAmbientOcclusionQualityLevel::High) => ("", "", "", "*", ""),
172 Some(ScreenSpaceAmbientOcclusionQualityLevel::Ultra) => ("", "", "", "", "*"),
173 _ => unreachable!(),
174 };
175
176 if let Some(thickness) = ssao.map(|s| s.constant_object_thickness) {
177 text.push_str(&format!(
178 "Constant object thickness: {thickness} (Up/Down)\n\n"
179 ));
180 }
181
182 text.push_str("SSAO Quality:\n");
183 text.push_str(&format!("(1) {o}Off{o}\n"));
184 text.push_str(&format!("(2) {l}Low{l}\n"));
185 text.push_str(&format!("(3) {m}Medium{m}\n"));
186 text.push_str(&format!("(4) {h}High{h}\n"));
187 text.push_str(&format!("(5) {u}Ultra{u}\n\n"));
188
189 text.push_str("Temporal Antialiasing:\n");
190 text.push_str(match temporal_jitter {
191 Some(_) => "(Space) Enabled",
192 None => "(Space) Disabled",
193 });
194}
138fn setup(
139 mut commands: Commands,
140 args: Res<Args>,
141 mesh_assets: ResMut<Assets<Mesh>>,
142 material_assets: ResMut<Assets<StandardMaterial>>,
143 images: ResMut<Assets<Image>>,
144) {
145 warn!(include_str!("warning_string.txt"));
146
147 let args = args.into_inner();
148 let images = images.into_inner();
149 let material_assets = material_assets.into_inner();
150 let mesh_assets = mesh_assets.into_inner();
151
152 let meshes = init_meshes(args, mesh_assets);
153
154 let material_textures = init_textures(args, images);
155 let materials = init_materials(args, &material_textures, material_assets);
156
157 // We're seeding the PRNG here to make this example deterministic for testing purposes.
158 // This isn't strictly required in practical use unless you need your app to be deterministic.
159 let mut material_rng = ChaCha8Rng::seed_from_u64(42);
160 match args.layout {
161 Layout::Sphere => {
162 // NOTE: This pattern is good for testing performance of culling as it provides roughly
163 // the same number of visible meshes regardless of the viewing angle.
164 const N_POINTS: usize = WIDTH * HEIGHT * 4;
165 // NOTE: f64 is used to avoid precision issues that produce visual artifacts in the distribution
166 let radius = WIDTH as f64 * 2.5;
167 let golden_ratio = 0.5f64 * (1.0f64 + 5.0f64.sqrt());
168 for i in 0..N_POINTS {
169 let spherical_polar_theta_phi =
170 fibonacci_spiral_on_sphere(golden_ratio, i, N_POINTS);
171 let unit_sphere_p = spherical_polar_to_cartesian(spherical_polar_theta_phi);
172 let (mesh, transform) = meshes.choose(&mut material_rng).unwrap();
173 commands
174 .spawn((
175 Mesh3d(mesh.clone()),
176 MeshMaterial3d(materials.choose(&mut material_rng).unwrap().clone()),
177 Transform::from_translation((radius * unit_sphere_p).as_vec3())
178 .looking_at(Vec3::ZERO, Vec3::Y)
179 .mul_transform(*transform),
180 ))
181 .insert_if(NoFrustumCulling, || args.no_frustum_culling)
182 .insert_if(NoAutomaticBatching, || args.no_automatic_batching);
183 }
184
185 // camera
186 let mut camera = commands.spawn(Camera3d::default());
187 if args.no_indirect_drawing {
188 camera.insert(NoIndirectDrawing);
189 }
190 if args.no_cpu_culling {
191 camera.insert(NoCpuCulling);
192 }
193
194 // Inside-out box around the meshes onto which shadows are cast (though you cannot see them...)
195 commands.spawn((
196 Mesh3d(mesh_assets.add(Cuboid::from_size(Vec3::splat(radius as f32 * 2.2)))),
197 MeshMaterial3d(material_assets.add(StandardMaterial::from(Color::WHITE))),
198 Transform::from_scale(-Vec3::ONE),
199 NotShadowCaster,
200 ));
201 }
202 _ => {
203 // NOTE: This pattern is good for demonstrating that frustum culling is working correctly
204 // as the number of visible meshes rises and falls depending on the viewing angle.
205 let scale = 2.5;
206 for x in 0..WIDTH {
207 for y in 0..HEIGHT {
208 // introduce spaces to break any kind of moiré pattern
209 if x % 10 == 0 || y % 10 == 0 {
210 continue;
211 }
212 // cube
213 commands.spawn((
214 Mesh3d(meshes.choose(&mut material_rng).unwrap().0.clone()),
215 MeshMaterial3d(materials.choose(&mut material_rng).unwrap().clone()),
216 Transform::from_xyz((x as f32) * scale, (y as f32) * scale, 0.0),
217 ));
218 commands.spawn((
219 Mesh3d(meshes.choose(&mut material_rng).unwrap().0.clone()),
220 MeshMaterial3d(materials.choose(&mut material_rng).unwrap().clone()),
221 Transform::from_xyz(
222 (x as f32) * scale,
223 HEIGHT as f32 * scale,
224 (y as f32) * scale,
225 ),
226 ));
227 commands.spawn((
228 Mesh3d(meshes.choose(&mut material_rng).unwrap().0.clone()),
229 MeshMaterial3d(materials.choose(&mut material_rng).unwrap().clone()),
230 Transform::from_xyz((x as f32) * scale, 0.0, (y as f32) * scale),
231 ));
232 commands.spawn((
233 Mesh3d(meshes.choose(&mut material_rng).unwrap().0.clone()),
234 MeshMaterial3d(materials.choose(&mut material_rng).unwrap().clone()),
235 Transform::from_xyz(0.0, (x as f32) * scale, (y as f32) * scale),
236 ));
237 }
238 }
239 // camera
240 let center = 0.5 * scale * Vec3::new(WIDTH as f32, HEIGHT as f32, WIDTH as f32);
241 commands.spawn((Camera3d::default(), Transform::from_translation(center)));
242 // Inside-out box around the meshes onto which shadows are cast (though you cannot see them...)
243 commands.spawn((
244 Mesh3d(mesh_assets.add(Cuboid::from_size(2.0 * 1.1 * center))),
245 MeshMaterial3d(material_assets.add(StandardMaterial::from(Color::WHITE))),
246 Transform::from_scale(-Vec3::ONE).with_translation(center),
247 NotShadowCaster,
248 ));
249 }
250 }
251
252 commands.spawn((
253 DirectionalLight {
254 shadows_enabled: args.shadows,
255 ..default()
256 },
257 Transform::IDENTITY.looking_at(Vec3::new(0.0, -1.0, -1.0), Vec3::Y),
258 ));
259}
Sourcepub fn insert_if_new(&mut self, bundle: impl Bundle) -> &mut EntityCommands<'a>
pub fn insert_if_new(&mut self, bundle: impl Bundle) -> &mut EntityCommands<'a>
Adds a Bundle
of components to the entity without overwriting.
This is the same as EntityCommands::insert
, but in case of duplicate
components will leave the old values instead of replacing them with new ones.
See also entry
, which lets you modify a Component
if it’s present,
as well as initialize it with a default value.
Sourcepub fn insert_if_new_and<F>(
&mut self,
bundle: impl Bundle,
condition: F,
) -> &mut EntityCommands<'a>
pub fn insert_if_new_and<F>( &mut self, bundle: impl Bundle, condition: F, ) -> &mut EntityCommands<'a>
Adds a Bundle
of components to the entity without overwriting if the
predicate returns true.
This is the same as EntityCommands::insert_if
, but in case of duplicate
components will leave the old values instead of replacing them with new ones.
Sourcepub unsafe fn insert_by_id<T>(
&mut self,
component_id: ComponentId,
value: T,
) -> &mut EntityCommands<'a>where
T: Send + 'static,
pub unsafe fn insert_by_id<T>(
&mut self,
component_id: ComponentId,
value: T,
) -> &mut EntityCommands<'a>where
T: Send + 'static,
Adds a dynamic Component
to the entity.
This will overwrite any previous value(s) of the same component type.
You should prefer to use the typed API EntityCommands::insert
where possible.
§Safety
ComponentId
must be from the same world asself
.T
must have the same layout as the one passed duringcomponent_id
creation.
Sourcepub unsafe fn try_insert_by_id<T>(
&mut self,
component_id: ComponentId,
value: T,
) -> &mut EntityCommands<'a>where
T: Send + 'static,
pub unsafe fn try_insert_by_id<T>(
&mut self,
component_id: ComponentId,
value: T,
) -> &mut EntityCommands<'a>where
T: Send + 'static,
Adds a dynamic Component
to the entity.
This will overwrite any previous value(s) of the same component type.
You should prefer to use the typed API EntityCommands::try_insert
where possible.
§Note
If the entity does not exist when this command is executed, the resulting error will be ignored.
§Safety
ComponentId
must be from the same world asself
.T
must have the same layout as the one passed duringcomponent_id
creation.
Sourcepub fn try_insert(&mut self, bundle: impl Bundle) -> &mut EntityCommands<'a>
pub fn try_insert(&mut self, bundle: impl Bundle) -> &mut EntityCommands<'a>
Adds a Bundle
of components to the entity.
This will overwrite any previous value(s) of the same component type.
§Note
If the entity does not exist when this command is executed, the resulting error will be ignored.
§Example
#[derive(Component)]
struct Health(u32);
#[derive(Component)]
struct Strength(u32);
#[derive(Component)]
struct Defense(u32);
#[derive(Bundle)]
struct CombatBundle {
health: Health,
strength: Strength,
}
fn add_combat_stats_system(mut commands: Commands, player: Res<PlayerEntity>) {
commands.entity(player.entity)
// You can insert individual components:
.try_insert(Defense(10))
// You can also insert tuples of components:
.try_insert(CombatBundle {
health: Health(100),
strength: Strength(40),
});
// Suppose this occurs in a parallel adjacent system or process.
commands.entity(player.entity).despawn();
// This will not panic nor will it add the component.
commands.entity(player.entity).try_insert(Defense(5));
}
Sourcepub fn try_insert_if<F>(
&mut self,
bundle: impl Bundle,
condition: F,
) -> &mut EntityCommands<'a>
pub fn try_insert_if<F>( &mut self, bundle: impl Bundle, condition: F, ) -> &mut EntityCommands<'a>
Sourcepub fn try_insert_if_new_and<F>(
&mut self,
bundle: impl Bundle,
condition: F,
) -> &mut EntityCommands<'a>
pub fn try_insert_if_new_and<F>( &mut self, bundle: impl Bundle, condition: F, ) -> &mut EntityCommands<'a>
Adds a Bundle
of components to the entity without overwriting if the
predicate returns true.
This is the same as EntityCommands::try_insert_if
, but in case of duplicate
components will leave the old values instead of replacing them with new ones.
§Note
If the entity does not exist when this command is executed, the resulting error will be ignored.
Sourcepub fn try_insert_if_new(
&mut self,
bundle: impl Bundle,
) -> &mut EntityCommands<'a>
pub fn try_insert_if_new( &mut self, bundle: impl Bundle, ) -> &mut EntityCommands<'a>
Adds a Bundle
of components to the entity without overwriting.
This is the same as EntityCommands::try_insert
, but in case of duplicate
components will leave the old values instead of replacing them with new ones.
§Note
If the entity does not exist when this command is executed, the resulting error will be ignored.
Sourcepub fn remove<B>(&mut self) -> &mut EntityCommands<'a>where
B: Bundle,
pub fn remove<B>(&mut self) -> &mut EntityCommands<'a>where
B: Bundle,
Removes a Bundle
of components from the entity.
This will remove all components that intersect with the provided bundle; the entity does not need to have all the components in the bundle.
This will emit a warning if the entity does not exist.
§Example
#[derive(Component)]
struct Health(u32);
#[derive(Component)]
struct Strength(u32);
#[derive(Component)]
struct Defense(u32);
#[derive(Bundle)]
struct CombatBundle {
health: Health,
strength: Strength,
}
fn remove_combat_stats_system(mut commands: Commands, player: Res<PlayerEntity>) {
commands
.entity(player.entity)
// You can remove individual components:
.remove::<Defense>()
// You can also remove pre-defined bundles of components:
.remove::<CombatBundle>()
// You can also remove tuples of components and bundles.
// This is equivalent to the calls above:
.remove::<(Defense, CombatBundle)>();
}
Examples found in repository?
More examples
38fn remove_component(
39 time: Res<Time>,
40 mut commands: Commands,
41 query: Query<Entity, With<MyComponent>>,
42) {
43 // After two seconds have passed the `Component` is removed.
44 if time.elapsed_secs() > 2.0
45 && let Some(entity) = query.iter().next()
46 {
47 commands.entity(entity).remove::<MyComponent>();
48 }
49}
136fn trigger_hooks(
137 mut commands: Commands,
138 keys: Res<ButtonInput<KeyCode>>,
139 index: Res<MyComponentIndex>,
140) {
141 for (key, entity) in index.iter() {
142 if !keys.pressed(*key) {
143 commands.entity(*entity).remove::<MyComponent>();
144 }
145 }
146 for key in keys.get_just_pressed() {
147 commands.spawn(MyComponent(*key));
148 }
149}
31fn screenshot_saving(
32 mut commands: Commands,
33 screenshot_saving: Query<Entity, With<Capturing>>,
34 window: Single<Entity, With<Window>>,
35) {
36 match screenshot_saving.iter().count() {
37 0 => {
38 commands.entity(*window).remove::<CursorIcon>();
39 }
40 x if x > 0 => {
41 commands
42 .entity(*window)
43 .insert(CursorIcon::from(SystemCursorIcon::Progress));
44 }
45 _ => {}
46 }
47}
161fn update_dof_settings(
162 mut commands: Commands,
163 view_targets: Query<Entity, With<Camera>>,
164 app_settings: Res<AppSettings>,
165) {
166 let depth_of_field: Option<DepthOfField> = (*app_settings).into();
167 for view in view_targets.iter() {
168 match depth_of_field {
169 None => {
170 commands.entity(view).remove::<DepthOfField>();
171 }
172 Some(depth_of_field) => {
173 commands.entity(view).insert(depth_of_field);
174 }
175 }
176 }
177}
- examples/ecs/entity_disabling.rs
- examples/async_tasks/async_compute.rs
- examples/ui/tab_navigation.rs
- examples/audio/soundtrack.rs
- examples/ecs/relationships.rs
- examples/asset/asset_decompression.rs
- examples/ui/standard_widgets_observers.rs
- examples/usage/cooldown.rs
- examples/3d/visibility_range.rs
- examples/games/game_menu.rs
- examples/ui/standard_widgets.rs
- examples/3d/clearcoat.rs
- examples/3d/clustered_decals.rs
- examples/3d/light_textures.rs
- examples/3d/occlusion_culling.rs
- examples/math/sampling_primitives.rs
- examples/3d/order_independent_transparency.rs
- examples/3d/irradiance_volumes.rs
- examples/3d/shadow_caster_receiver.rs
- examples/3d/pcss.rs
- tests/window/desktop_request_redraw.rs
- examples/3d/volumetric_fog.rs
- examples/3d/ssr.rs
- examples/3d/reflection_probes.rs
- examples/3d/blend_modes.rs
- examples/3d/solari.rs
- examples/animation/animation_masks.rs
- examples/3d/anisotropy.rs
- examples/3d/deferred_rendering.rs
- examples/3d/mixed_lighting.rs
- examples/3d/ssao.rs
- examples/3d/bloom_3d.rs
- examples/2d/bloom_2d.rs
- examples/3d/anti_aliasing.rs
- examples/3d/transmission.rs
- examples/ui/feathers.rs
Sourcepub fn remove_if<B>(
&mut self,
condition: impl FnOnce() -> bool,
) -> &mut EntityCommands<'a>where
B: Bundle,
pub fn remove_if<B>(
&mut self,
condition: impl FnOnce() -> bool,
) -> &mut EntityCommands<'a>where
B: Bundle,
Removes a Bundle
of components from the entity if the predicate returns true.
This is useful for chaining method calls.
§Example
#[derive(Component)]
struct Health(u32);
#[derive(Component)]
struct Strength(u32);
#[derive(Component)]
struct Defense(u32);
#[derive(Bundle)]
struct CombatBundle {
health: Health,
strength: Strength,
}
fn remove_combat_stats_system(mut commands: Commands, player: Res<PlayerEntity>) {
commands
.entity(player.entity)
.remove_if::<(Defense, CombatBundle)>(|| !player.is_spectator());
}
Sourcepub fn try_remove_if<B>(
&mut self,
condition: impl FnOnce() -> bool,
) -> &mut EntityCommands<'a>where
B: Bundle,
pub fn try_remove_if<B>(
&mut self,
condition: impl FnOnce() -> bool,
) -> &mut EntityCommands<'a>where
B: Bundle,
Sourcepub fn try_remove<B>(&mut self) -> &mut EntityCommands<'a>where
B: Bundle,
pub fn try_remove<B>(&mut self) -> &mut EntityCommands<'a>where
B: Bundle,
Removes a Bundle
of components from the entity.
This will remove all components that intersect with the provided bundle; the entity does not need to have all the components in the bundle.
Unlike Self::remove
,
this will not emit a warning if the entity does not exist.
§Example
#[derive(Component)]
struct Health(u32);
#[derive(Component)]
struct Strength(u32);
#[derive(Component)]
struct Defense(u32);
#[derive(Bundle)]
struct CombatBundle {
health: Health,
strength: Strength,
}
fn remove_combat_stats_system(mut commands: Commands, player: Res<PlayerEntity>) {
commands
.entity(player.entity)
// You can remove individual components:
.try_remove::<Defense>()
// You can also remove pre-defined bundles of components:
.try_remove::<CombatBundle>()
// You can also remove tuples of components and bundles.
// This is equivalent to the calls above:
.try_remove::<(Defense, CombatBundle)>();
}
Sourcepub fn remove_with_requires<B>(&mut self) -> &mut EntityCommands<'a>where
B: Bundle,
pub fn remove_with_requires<B>(&mut self) -> &mut EntityCommands<'a>where
B: Bundle,
Removes a Bundle
of components from the entity,
and also removes any components required by the components in the bundle.
This will remove all components that intersect with the provided bundle; the entity does not need to have all the components in the bundle.
§Example
#[derive(Component)]
#[require(B)]
struct A;
#[derive(Component, Default)]
struct B;
fn remove_with_requires_system(mut commands: Commands, player: Res<PlayerEntity>) {
commands
.entity(player.entity)
// Removes both A and B from the entity, because B is required by A.
.remove_with_requires::<A>();
}
Sourcepub fn remove_by_id(
&mut self,
component_id: ComponentId,
) -> &mut EntityCommands<'a>
pub fn remove_by_id( &mut self, component_id: ComponentId, ) -> &mut EntityCommands<'a>
Removes a dynamic Component
from the entity if it exists.
§Panics
Panics if the provided ComponentId
does not exist in the World
.
Sourcepub fn clear(&mut self) -> &mut EntityCommands<'a>
pub fn clear(&mut self) -> &mut EntityCommands<'a>
Removes all components associated with the entity.
Sourcepub fn despawn(&mut self)
pub fn despawn(&mut self)
Despawns the entity.
This will emit a warning if the entity does not exist.
§Note
This will also despawn the entities in any RelationshipTarget
that is configured to despawn descendants.
For example, this will recursively despawn Children
.
§Example
fn remove_character_system(
mut commands: Commands,
character_to_remove: Res<CharacterToRemove>
) {
commands.entity(character_to_remove.entity).despawn();
}
Examples found in repository?
165fn cleanup_menu(mut commands: Commands, menu_data: Res<MenuData>) {
166 commands.entity(menu_data.button_entity).despawn();
167}
168
169const SPEED: f32 = 100.0;
170fn movement(
171 time: Res<Time>,
172 input: Res<ButtonInput<KeyCode>>,
173 mut query: Query<&mut Transform, With<Sprite>>,
174) {
175 for mut transform in &mut query {
176 let mut direction = Vec3::ZERO;
177 if input.pressed(KeyCode::ArrowLeft) {
178 direction.x -= 1.0;
179 }
180 if input.pressed(KeyCode::ArrowRight) {
181 direction.x += 1.0;
182 }
183 if input.pressed(KeyCode::ArrowUp) {
184 direction.y += 1.0;
185 }
186 if input.pressed(KeyCode::ArrowDown) {
187 direction.y -= 1.0;
188 }
189
190 if direction != Vec3::ZERO {
191 transform.translation += direction.normalize() * SPEED * time.delta_secs();
192 }
193 }
194}
195
196fn change_color(time: Res<Time>, mut query: Query<&mut Sprite>) {
197 for mut sprite in &mut query {
198 let new_color = LinearRgba {
199 blue: ops::sin(time.elapsed_secs() * 0.5) + 2.0,
200 ..LinearRgba::from(sprite.color)
201 };
202
203 sprite.color = new_color.into();
204 }
205}
206
207// We can restart the game by pressing "R".
208// This will trigger an [`AppState::InGame`] -> [`AppState::InGame`]
209// transition, which will run our custom schedules.
210fn trigger_game_restart(
211 input: Res<ButtonInput<KeyCode>>,
212 mut next_state: ResMut<NextState<AppState>>,
213) {
214 if input.just_pressed(KeyCode::KeyR) {
215 // Although we are already in this state setting it again will generate an identity transition.
216 // While default schedules ignore those kinds of transitions, our custom schedules will react to them.
217 next_state.set(AppState::InGame);
218 }
219}
220
221fn setup(mut commands: Commands) {
222 commands.spawn(Camera2d);
223}
224
225fn setup_game(mut commands: Commands, asset_server: Res<AssetServer>) {
226 commands.spawn(Sprite::from_image(asset_server.load("branding/icon.png")));
227 info!("Setup game");
228}
229
230fn teardown_game(mut commands: Commands, player: Single<Entity, With<Sprite>>) {
231 commands.entity(*player).despawn();
232 info!("Teardown game");
233}
More examples
- examples/usage/context_menu.rs
- examples/games/loading_screen.rs
- examples/async_tasks/external_source_external_thread.rs
- examples/asset/multi_asset_sync.rs
- examples/input/text_input.rs
- examples/audio/soundtrack.rs
- examples/ecs/observers.rs
- examples/ecs/observer_propagation.rs
- examples/3d/pbr.rs
- examples/animation/animated_mesh_events.rs
- examples/3d/order_independent_transparency.rs
- examples/3d/tonemapping.rs
- examples/ecs/fallible_params.rs
- examples/ecs/hierarchy.rs
- examples/3d/solari.rs
- examples/games/alien_cake_addict.rs
- examples/games/breakout.rs
- examples/3d/reflection_probes.rs
- examples/window/monitor_info.rs
- examples/math/random_sampling.rs
- examples/3d/anisotropy.rs
- examples/math/sampling_primitives.rs
- examples/ecs/component_hooks.rs
- examples/ui/scroll.rs
- tests/3d/test_invalid_skinned_mesh.rs
Sourcepub fn try_despawn(&mut self)
pub fn try_despawn(&mut self)
Despawns the entity.
Unlike Self::despawn
,
this will not emit a warning if the entity does not exist.
§Note
This will also despawn the entities in any RelationshipTarget
that is configured to despawn descendants.
For example, this will recursively despawn Children
.
Sourcepub fn queue<C, T, M>(&mut self, command: C) -> &mut EntityCommands<'a>where
C: EntityCommand<T> + CommandWithEntity<M>,
pub fn queue<C, T, M>(&mut self, command: C) -> &mut EntityCommands<'a>where
C: EntityCommand<T> + CommandWithEntity<M>,
Pushes an EntityCommand
to the queue,
which will get executed for the current Entity
.
The default error handler
will be used to handle error cases.
Every EntityCommand
checks whether the entity exists at the time of execution
and returns an error if it does not.
To use a custom error handler, see EntityCommands::queue_handled
.
The command can be:
- A custom struct that implements
EntityCommand
. - A closure or function that matches the following signature:
- A built-in command from the
entity_command
module.
§Example
commands
.spawn_empty()
// Closures with this signature implement `EntityCommand`.
.queue(|entity: EntityWorldMut| {
println!("Executed an EntityCommand for {}", entity.id());
});
Sourcepub fn queue_handled<C, T, M>(
&mut self,
command: C,
error_handler: fn(BevyError, ErrorContext),
) -> &mut EntityCommands<'a>where
C: EntityCommand<T> + CommandWithEntity<M>,
pub fn queue_handled<C, T, M>(
&mut self,
command: C,
error_handler: fn(BevyError, ErrorContext),
) -> &mut EntityCommands<'a>where
C: EntityCommand<T> + CommandWithEntity<M>,
Pushes an EntityCommand
to the queue,
which will get executed for the current Entity
.
The given error_handler
will be used to handle error cases.
Every EntityCommand
checks whether the entity exists at the time of execution
and returns an error if it does not.
To implicitly use the default error handler, see EntityCommands::queue
.
The command can be:
- A custom struct that implements
EntityCommand
. - A closure or function that matches the following signature:
- A built-in command from the
entity_command
module.
§Example
use bevy_ecs::error::warn;
commands
.spawn_empty()
// Closures with this signature implement `EntityCommand`.
.queue_handled(
|entity: EntityWorldMut| -> Result {
let value: usize = "100".parse()?;
println!("Successfully parsed the value {} for entity {}", value, entity.id());
Ok(())
},
warn
);
Sourcepub fn queue_silenced<C, T, M>(&mut self, command: C) -> &mut EntityCommands<'a>where
C: EntityCommand<T> + CommandWithEntity<M>,
pub fn queue_silenced<C, T, M>(&mut self, command: C) -> &mut EntityCommands<'a>where
C: EntityCommand<T> + CommandWithEntity<M>,
Pushes an EntityCommand
to the queue, which will get executed for the current Entity
.
Unlike EntityCommands::queue_handled
, this will completely ignore any errors that occur.
Sourcepub fn retain<B>(&mut self) -> &mut EntityCommands<'a>where
B: Bundle,
pub fn retain<B>(&mut self) -> &mut EntityCommands<'a>where
B: Bundle,
Removes all components except the given Bundle
from the entity.
§Example
#[derive(Component)]
struct Health(u32);
#[derive(Component)]
struct Strength(u32);
#[derive(Component)]
struct Defense(u32);
#[derive(Bundle)]
struct CombatBundle {
health: Health,
strength: Strength,
}
fn remove_combat_stats_system(mut commands: Commands, player: Res<PlayerEntity>) {
commands
.entity(player.entity)
// You can retain a pre-defined Bundle of components,
// with this removing only the Defense component.
.retain::<CombatBundle>()
// You can also retain only a single component.
.retain::<Health>();
}
Sourcepub fn log_components(&mut self) -> &mut EntityCommands<'a>
pub fn log_components(&mut self) -> &mut EntityCommands<'a>
Logs the components of the entity at the info
level.
Sourcepub fn commands_mut(&mut self) -> &mut Commands<'a, 'a>
pub fn commands_mut(&mut self) -> &mut Commands<'a, 'a>
Returns a mutable reference to the underlying Commands
.
Sourcepub fn observe<E, B, M>(
&mut self,
observer: impl IntoObserverSystem<E, B, M>,
) -> &mut EntityCommands<'a>where
E: EntityEvent,
B: Bundle,
pub fn observe<E, B, M>(
&mut self,
observer: impl IntoObserverSystem<E, B, M>,
) -> &mut EntityCommands<'a>where
E: EntityEvent,
B: Bundle,
Creates an Observer
watching for an EntityEvent
of type E
whose EntityEvent::event_target
targets this entity.
Examples found in repository?
60fn setup(mut commands: Commands) {
61 commands.spawn(Camera2d);
62
63 commands.spawn(background_and_button()).observe(
64 // any click bubbling up here should lead to closing any open menu
65 |_: On<Pointer<Press>>, mut commands: Commands| {
66 commands.trigger(CloseContextMenus);
67 },
68 );
69}
70
71fn on_trigger_close_menus(
72 _event: On<CloseContextMenus>,
73 mut commands: Commands,
74 menus: Query<Entity, With<ContextMenu>>,
75) {
76 for e in menus.iter() {
77 commands.entity(e).despawn();
78 }
79}
80
81fn on_trigger_menu(event: On<OpenContextMenu>, mut commands: Commands) {
82 commands.trigger(CloseContextMenus);
83
84 let pos = event.pos;
85
86 debug!("open context menu at: {pos}");
87
88 commands
89 .spawn((
90 Name::new("context menu"),
91 ContextMenu,
92 Node {
93 position_type: PositionType::Absolute,
94 left: px(pos.x),
95 top: px(pos.y),
96 flex_direction: FlexDirection::Column,
97 ..default()
98 },
99 BorderColor::all(Color::BLACK),
100 BorderRadius::all(px(4)),
101 BackgroundColor(Color::linear_rgb(0.1, 0.1, 0.1)),
102 children![
103 context_item("fuchsia", basic::FUCHSIA),
104 context_item("gray", basic::GRAY),
105 context_item("maroon", basic::MAROON),
106 context_item("purple", basic::PURPLE),
107 context_item("teal", basic::TEAL),
108 ],
109 ))
110 .observe(
111 |event: On<Pointer<Press>>,
112 menu_items: Query<&ContextMenuItem>,
113 mut clear_col: ResMut<ClearColor>,
114 mut commands: Commands| {
115 let target = event.original_event_target();
116
117 if let Ok(item) = menu_items.get(target) {
118 clear_col.0 = item.0.into();
119 commands.trigger(CloseContextMenus);
120 }
121 },
122 );
123}
More examples
17fn screenshot_on_spacebar(
18 mut commands: Commands,
19 input: Res<ButtonInput<KeyCode>>,
20 mut counter: Local<u32>,
21) {
22 if input.just_pressed(KeyCode::Space) {
23 let path = format!("./screenshot-{}.png", *counter);
24 *counter += 1;
25 commands
26 .spawn(Screenshot::primary_window())
27 .observe(save_to_disk(path));
28 }
29}
27fn setup(mut commands: Commands) {
28 commands
29 .spawn((Name::new("Goblin"), HitPoints(50)))
30 .observe(take_damage)
31 .with_children(|parent| {
32 parent
33 .spawn((Name::new("Helmet"), Armor(5)))
34 .observe(block_attack);
35 parent
36 .spawn((Name::new("Socks"), Armor(10)))
37 .observe(block_attack);
38 parent
39 .spawn((Name::new("Shirt"), Armor(15)))
40 .observe(block_attack);
41 });
42}
57fn setup(mut commands: Commands) {
58 commands.spawn(Camera2d);
59
60 commands
61 .spawn((
62 Node {
63 width: percent(100),
64 height: percent(100),
65 align_items: AlignItems::Center,
66 justify_content: JustifyContent::Center,
67 flex_direction: FlexDirection::Column,
68 ..default()
69 },
70 children![(
71 Text::default(),
72 TextFont {
73 font_size: 100.0,
74 ..default()
75 },
76 )],
77 ))
78 .observe(on_click);
79}
166fn spawn_scene(commands: &mut Commands, asset_server: &AssetServer) {
167 commands
168 .spawn(SceneRoot(
169 asset_server.load(
170 GltfAssetLabel::Scene(0)
171 .from_asset("models/MixedLightingExample/MixedLightingExample.gltf"),
172 ),
173 ))
174 .observe(
175 |_: On<SceneInstanceReady>,
176 mut lighting_mode_changed_writer: MessageWriter<LightingModeChanged>| {
177 // When the scene loads, send a `LightingModeChanged` event so
178 // that we set up the lightmaps.
179 lighting_mode_changed_writer.write(LightingModeChanged);
180 },
181 );
182}
28fn setup(
29 mut commands: Commands,
30 asset_server: Res<AssetServer>,
31 mut graphs: ResMut<Assets<AnimationGraph>>,
32) {
33 let (graph, index) = AnimationGraph::from_clip(
34 asset_server.load(GltfAssetLabel::Animation(2).from_asset(GLTF_PATH)),
35 );
36
37 commands
38 .spawn((
39 AnimationToPlay {
40 graph_handle: graphs.add(graph),
41 index,
42 },
43 SceneRoot(asset_server.load(GltfAssetLabel::Scene(0).from_asset(GLTF_PATH))),
44 ))
45 .observe(play_animation_when_ready);
46
47 commands.spawn((
48 DirectionalLight::default(),
49 Transform::from_rotation(Quat::from_rotation_z(PI / 2.0)),
50 ));
51
52 commands.spawn((
53 Camera3d::default(),
54 Transform::from_xyz(3.0, 2.1, 10.2).looking_at(Vec3::ZERO, Vec3::Y),
55 ));
56}
- examples/testbed/3d.rs
- examples/animation/animated_mesh.rs
- examples/picking/simple_picking.rs
- tests/window/desktop_request_redraw.rs
- examples/picking/debug_picking.rs
- examples/ecs/observers.rs
- examples/ui/viewport_node.rs
- examples/picking/sprite_picking.rs
- examples/shader/gpu_readback.rs
- examples/ui/ui_target_camera.rs
- examples/3d/solari.rs
- examples/ui/drag_to_scroll.rs
- examples/ui/tab_navigation.rs
- examples/ui/scroll.rs
- examples/picking/mesh_picking.rs
- examples/ui/render_ui_to_texture.rs
- examples/stress_tests/many_foxes.rs
- examples/ui/ui_drag_and_drop.rs
- examples/ui/gradients.rs
Sourcepub fn clone_with_opt_out(
&mut self,
target: Entity,
config: impl FnOnce(&mut EntityClonerBuilder<'_, OptOut>) + Send + Sync + 'static,
) -> &mut EntityCommands<'a>
pub fn clone_with_opt_out( &mut self, target: Entity, config: impl FnOnce(&mut EntityClonerBuilder<'_, OptOut>) + Send + Sync + 'static, ) -> &mut EntityCommands<'a>
Clones parts of an entity (components, observers, etc.) onto another entity,
configured through EntityClonerBuilder
.
The other entity will receive all the components of the original that implement
Clone
or Reflect
except those that are
denied in the config
.
§Panics
The command will panic when applied if the target entity does not exist.
§Example
Configure through EntityClonerBuilder<OptOut>
as follows:
#[derive(Component, Clone)]
struct ComponentA(u32);
#[derive(Component, Clone)]
struct ComponentB(u32);
fn example_system(mut commands: Commands) {
// Create an empty entity.
let target = commands.spawn_empty().id();
// Create a new entity and keep its EntityCommands.
let mut entity = commands.spawn((ComponentA(10), ComponentB(20)));
// Clone ComponentA but not ComponentB onto the target.
entity.clone_with_opt_out(target, |builder| {
builder.deny::<ComponentB>();
});
}
See EntityClonerBuilder
for more options.
Sourcepub fn clone_with_opt_in(
&mut self,
target: Entity,
config: impl FnOnce(&mut EntityClonerBuilder<'_, OptIn>) + Send + Sync + 'static,
) -> &mut EntityCommands<'a>
pub fn clone_with_opt_in( &mut self, target: Entity, config: impl FnOnce(&mut EntityClonerBuilder<'_, OptIn>) + Send + Sync + 'static, ) -> &mut EntityCommands<'a>
Clones parts of an entity (components, observers, etc.) onto another entity,
configured through EntityClonerBuilder
.
The other entity will receive only the components of the original that implement
Clone
or Reflect
and are
allowed in the config
.
§Panics
The command will panic when applied if the target entity does not exist.
§Example
Configure through EntityClonerBuilder<OptIn>
as follows:
#[derive(Component, Clone)]
struct ComponentA(u32);
#[derive(Component, Clone)]
struct ComponentB(u32);
fn example_system(mut commands: Commands) {
// Create an empty entity.
let target = commands.spawn_empty().id();
// Create a new entity and keep its EntityCommands.
let mut entity = commands.spawn((ComponentA(10), ComponentB(20)));
// Clone ComponentA but not ComponentB onto the target.
entity.clone_with_opt_in(target, |builder| {
builder.allow::<ComponentA>();
});
}
See EntityClonerBuilder
for more options.
Sourcepub fn clone_and_spawn(&mut self) -> EntityCommands<'_>
pub fn clone_and_spawn(&mut self) -> EntityCommands<'_>
Spawns a clone of this entity and returns the EntityCommands
of the clone.
The clone will receive all the components of the original that implement
Clone
or Reflect
.
To configure cloning behavior (such as only cloning certain components),
use EntityCommands::clone_and_spawn_with_opt_out
/
opt_out
.
§Note
If the original entity does not exist when this command is applied, the returned entity will have no components.
§Example
#[derive(Component, Clone)]
struct ComponentA(u32);
#[derive(Component, Clone)]
struct ComponentB(u32);
fn example_system(mut commands: Commands) {
// Create a new entity and store its EntityCommands.
let mut entity = commands.spawn((ComponentA(10), ComponentB(20)));
// Create a clone of the entity.
let mut entity_clone = entity.clone_and_spawn();
}
Sourcepub fn clone_and_spawn_with_opt_out(
&mut self,
config: impl FnOnce(&mut EntityClonerBuilder<'_, OptOut>) + Send + Sync + 'static,
) -> EntityCommands<'_>
pub fn clone_and_spawn_with_opt_out( &mut self, config: impl FnOnce(&mut EntityClonerBuilder<'_, OptOut>) + Send + Sync + 'static, ) -> EntityCommands<'_>
Spawns a clone of this entity and allows configuring cloning behavior
using EntityClonerBuilder
, returning the EntityCommands
of the clone.
The clone will receive all the components of the original that implement
Clone
or Reflect
except those that are
denied in the config
.
See the methods on EntityClonerBuilder<OptOut>
for more options.
§Note
If the original entity does not exist when this command is applied, the returned entity will have no components.
§Example
#[derive(Component, Clone)]
struct ComponentA(u32);
#[derive(Component, Clone)]
struct ComponentB(u32);
fn example_system(mut commands: Commands) {
// Create a new entity and store its EntityCommands.
let mut entity = commands.spawn((ComponentA(10), ComponentB(20)));
// Create a clone of the entity with ComponentA but without ComponentB.
let mut entity_clone = entity.clone_and_spawn_with_opt_out(|builder| {
builder.deny::<ComponentB>();
});
}
Sourcepub fn clone_and_spawn_with_opt_in(
&mut self,
config: impl FnOnce(&mut EntityClonerBuilder<'_, OptIn>) + Send + Sync + 'static,
) -> EntityCommands<'_>
pub fn clone_and_spawn_with_opt_in( &mut self, config: impl FnOnce(&mut EntityClonerBuilder<'_, OptIn>) + Send + Sync + 'static, ) -> EntityCommands<'_>
Spawns a clone of this entity and allows configuring cloning behavior
using EntityClonerBuilder
, returning the EntityCommands
of the clone.
The clone will receive only the components of the original that implement
Clone
or Reflect
and are
allowed in the config
.
See the methods on EntityClonerBuilder<OptIn>
for more options.
§Note
If the original entity does not exist when this command is applied, the returned entity will have no components.
§Example
#[derive(Component, Clone)]
struct ComponentA(u32);
#[derive(Component, Clone)]
struct ComponentB(u32);
fn example_system(mut commands: Commands) {
// Create a new entity and store its EntityCommands.
let mut entity = commands.spawn((ComponentA(10), ComponentB(20)));
// Create a clone of the entity with ComponentA but without ComponentB.
let mut entity_clone = entity.clone_and_spawn_with_opt_in(|builder| {
builder.allow::<ComponentA>();
});
}
Sourcepub fn clone_components<B>(&mut self, target: Entity) -> &mut EntityCommands<'a>where
B: Bundle,
pub fn clone_components<B>(&mut self, target: Entity) -> &mut EntityCommands<'a>where
B: Bundle,
Sourcepub fn move_components<B>(&mut self, target: Entity) -> &mut EntityCommands<'a>where
B: Bundle,
pub fn move_components<B>(&mut self, target: Entity) -> &mut EntityCommands<'a>where
B: Bundle,
Moves the specified components of this entity into another entity.
Components with Ignore
clone behavior will not be moved, while components that
have a Custom
clone behavior will be cloned using it and then removed from the source entity.
All other components will be moved without any other special handling.
Note that this will trigger on_remove
hooks/observers on this entity and on_insert
/on_add
hooks/observers on the target entity.
§Panics
The command will panic when applied if the target entity does not exist.
Sourcepub fn trigger<'t, E>(
&mut self,
event_fn: impl FnOnce(Entity) -> E,
) -> &mut EntityCommands<'a>
pub fn trigger<'t, E>( &mut self, event_fn: impl FnOnce(Entity) -> E, ) -> &mut EntityCommands<'a>
Passes the current entity into the given function, and triggers the EntityEvent
returned by that function.
§Example
A surprising number of functions meet the trait bounds for event_fn
:
#[derive(EntityEvent)]
struct Explode(Entity);
impl From<Entity> for Explode {
fn from(entity: Entity) -> Self {
Explode(entity)
}
}
fn trigger_via_constructor(mut commands: Commands) {
// The fact that `Epxlode` is a single-field tuple struct
// ensures that `Explode(entity)` is a function that generates
// an EntityEvent, meeting the trait bounds for `event_fn`.
commands.spawn_empty().trigger(Explode);
}
fn trigger_via_from_trait(mut commands: Commands) {
// This variant also works for events like `struct Explode { entity: Entity }`
commands.spawn_empty().trigger(Explode::from);
}
fn trigger_via_closure(mut commands: Commands) {
commands.spawn_empty().trigger(|entity| Explode(entity));
}
Trait Implementations§
Source§impl BuildChildrenTransformExt for EntityCommands<'_>
impl BuildChildrenTransformExt for EntityCommands<'_>
Source§fn set_parent_in_place(&mut self, parent: Entity) -> &mut EntityCommands<'_>
fn set_parent_in_place(&mut self, parent: Entity) -> &mut EntityCommands<'_>
GlobalTransform
by updating its Transform
. Read moreSource§fn remove_parent_in_place(&mut self) -> &mut EntityCommands<'_>
fn remove_parent_in_place(&mut self) -> &mut EntityCommands<'_>
GlobalTransform
by updating its Transform
to be equal to its current GlobalTransform
. Read moreSource§impl ReflectCommandExt for EntityCommands<'_>
impl ReflectCommandExt for EntityCommands<'_>
Source§fn insert_reflect(
&mut self,
component: Box<dyn PartialReflect>,
) -> &mut EntityCommands<'_>
fn insert_reflect( &mut self, component: Box<dyn PartialReflect>, ) -> &mut EntityCommands<'_>
AppTypeRegistry
. Read moreSource§fn insert_reflect_with_registry<T>(
&mut self,
component: Box<dyn PartialReflect>,
) -> &mut EntityCommands<'_>
fn insert_reflect_with_registry<T>( &mut self, component: Box<dyn PartialReflect>, ) -> &mut EntityCommands<'_>
insert_reflect
, but using the T
resource as type registry instead of
AppTypeRegistry
. Read moreSource§fn remove_reflect(
&mut self,
component_type_path: impl Into<Cow<'static, str>>,
) -> &mut EntityCommands<'_>
fn remove_reflect( &mut self, component_type_path: impl Into<Cow<'static, str>>, ) -> &mut EntityCommands<'_>
AppTypeRegistry
. Read moreSource§fn remove_reflect_with_registry<T>(
&mut self,
component_type_path: impl Into<Cow<'static, str>>,
) -> &mut EntityCommands<'_>
fn remove_reflect_with_registry<T>( &mut self, component_type_path: impl Into<Cow<'static, str>>, ) -> &mut EntityCommands<'_>
Auto Trait Implementations§
impl<'a> Freeze for EntityCommands<'a>
impl<'a> RefUnwindSafe for EntityCommands<'a>
impl<'a> Send for EntityCommands<'a>
impl<'a> Sync for EntityCommands<'a>
impl<'a> Unpin for EntityCommands<'a>
impl<'a> !UnwindSafe for EntityCommands<'a>
Blanket Implementations§
Source§impl<T, U> AsBindGroupShaderType<U> for T
impl<T, U> AsBindGroupShaderType<U> for T
Source§fn as_bind_group_shader_type(&self, _images: &RenderAssets<GpuImage>) -> U
fn as_bind_group_shader_type(&self, _images: &RenderAssets<GpuImage>) -> U
T
ShaderType
for self
. When used in AsBindGroup
derives, it is safe to assume that all images in self
exist.Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait>
(where Trait: Downcast
) to Box<dyn Any>
, which can then be
downcast
into Box<dyn ConcreteType>
where ConcreteType
implements Trait
.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait>
(where Trait: Downcast
) to Rc<Any>
, which can then be further
downcast
into Rc<ConcreteType>
where ConcreteType
implements Trait
.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait
(where Trait: Downcast
) to &Any
. This is needed since Rust cannot
generate &Any
’s vtable from &Trait
’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait
(where Trait: Downcast
) to &Any
. This is needed since Rust cannot
generate &mut Any
’s vtable from &mut Trait
’s.Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait>
(where Trait: Downcast
) to Box<dyn Any>
. Box<dyn Any>
can
then be further downcast
into Box<ConcreteType>
where ConcreteType
implements Trait
.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait>
(where Trait: Downcast
) to Rc<Any>
. Rc<Any>
can then be
further downcast
into Rc<ConcreteType>
where ConcreteType
implements Trait
.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait
(where Trait: Downcast
) to &Any
. This is needed since Rust cannot
generate &Any
’s vtable from &Trait
’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait
(where Trait: Downcast
) to &Any
. This is needed since Rust cannot
generate &mut Any
’s vtable from &mut Trait
’s.Source§impl<T> DowncastSend for T
impl<T> DowncastSend for T
Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
Source§impl<T> FmtForward for T
impl<T> FmtForward for T
Source§fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
self
to use its Binary
implementation when Debug
-formatted.Source§fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
self
to use its Display
implementation when
Debug
-formatted.Source§fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
self
to use its LowerExp
implementation when
Debug
-formatted.Source§fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
self
to use its LowerHex
implementation when
Debug
-formatted.Source§fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
self
to use its Octal
implementation when Debug
-formatted.Source§fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
self
to use its Pointer
implementation when
Debug
-formatted.Source§fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
self
to use its UpperExp
implementation when
Debug
-formatted.Source§fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
self
to use its UpperHex
implementation when
Debug
-formatted.Source§impl<S> FromSample<S> for S
impl<S> FromSample<S> for S
fn from_sample_(s: S) -> S
Source§impl<T, W> HasTypeWitness<W> for Twhere
W: MakeTypeWitness<Arg = T>,
T: ?Sized,
impl<T, W> HasTypeWitness<W> for Twhere
W: MakeTypeWitness<Arg = T>,
T: ?Sized,
Source§impl<T> Identity for Twhere
T: ?Sized,
impl<T> Identity for Twhere
T: ?Sized,
Source§impl<T> InitializeFromFunction<T> for T
impl<T> InitializeFromFunction<T> for T
Source§fn initialize_from_function(f: fn() -> T) -> T
fn initialize_from_function(f: fn() -> T) -> T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self
into a Left
variant of Either<Self, Self>
if into_left
is true
.
Converts self
into a Right
variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self
into a Left
variant of Either<Self, Self>
if into_left(&self)
returns true
.
Converts self
into a Right
variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> IntoResult<T> for T
impl<T> IntoResult<T> for T
Source§fn into_result(self) -> Result<T, RunSystemError>
fn into_result(self) -> Result<T, RunSystemError>
Source§impl<F, T> IntoSample<T> for Fwhere
T: FromSample<F>,
impl<F, T> IntoSample<T> for Fwhere
T: FromSample<F>,
fn into_sample(self) -> T
Source§impl<T> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
Source§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
Source§fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
self
and passes that borrow into the pipe function. Read moreSource§fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
self
and passes that borrow into the pipe function. Read moreSource§fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
Source§fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R,
) -> R
fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
Source§fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
self
, then passes self.as_ref()
into the pipe function.Source§fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
self
, then passes self.as_mut()
into the pipe
function.Source§fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
self
, then passes self.deref()
into the pipe function.Source§impl<T> Pointable for T
impl<T> Pointable for T
Source§impl<Ret> SpawnIfAsync<(), Ret> for Ret
impl<Ret> SpawnIfAsync<(), Ret> for Ret
Source§impl<T, O> SuperFrom<T> for Owhere
O: From<T>,
impl<T, O> SuperFrom<T> for Owhere
O: From<T>,
Source§fn super_from(input: T) -> O
fn super_from(input: T) -> O
Source§impl<T, O, M> SuperInto<O, M> for Twhere
O: SuperFrom<T, M>,
impl<T, O, M> SuperInto<O, M> for Twhere
O: SuperFrom<T, M>,
Source§fn super_into(self) -> O
fn super_into(self) -> O
Source§impl<T> Tap for T
impl<T> Tap for T
Source§fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
Borrow<B>
of a value. Read moreSource§fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
BorrowMut<B>
of a value. Read moreSource§fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
AsRef<R>
view of a value. Read moreSource§fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
AsMut<R>
view of a value. Read moreSource§fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
Deref::Target
of a value. Read moreSource§fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
Deref::Target
of a value. Read moreSource§fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
.tap()
only in debug builds, and is erased in release builds.Source§fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
.tap_mut()
only in debug builds, and is erased in release
builds.Source§fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
.tap_borrow()
only in debug builds, and is erased in release
builds.Source§fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
.tap_borrow_mut()
only in debug builds, and is erased in release
builds.Source§fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
.tap_ref()
only in debug builds, and is erased in release
builds.Source§fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
.tap_ref_mut()
only in debug builds, and is erased in release
builds.Source§fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
.tap_deref()
only in debug builds, and is erased in release
builds.