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 by setting the GLOBAL_ERROR_HANDLER
.
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
184fn spawn_buttons(commands: &mut Commands) {
185 commands
186 .spawn(widgets::main_ui_node())
187 .with_children(|parent| {
188 widgets::spawn_option_buttons(
189 parent,
190 "Lighting",
191 &[
192 (LightingMode::Baked, "Baked"),
193 (LightingMode::MixedDirect, "Mixed (Direct)"),
194 (LightingMode::MixedIndirect, "Mixed (Indirect)"),
195 (LightingMode::RealTime, "Real-Time"),
196 ],
197 );
198 });
199}
142fn print_logs(
143 mut events: EventReader<LogEvent>,
144 mut commands: Commands,
145 log_viewer_root: Single<Entity, With<LogViewerRoot>>,
146) {
147 let root_entity = *log_viewer_root;
148
149 commands.entity(root_entity).with_children(|child| {
150 for event in events.read() {
151 child.spawn(Text::default()).with_children(|child| {
152 child.spawn((
153 TextSpan::new(format!("{:5} ", event.level)),
154 TextColor(level_color(&event.level)),
155 ));
156 child.spawn(TextSpan::new(&event.message));
157 });
158 }
159 });
160}
92fn setup_ui(mut commands: Commands) {
93 commands.spawn(Camera2d);
94 commands
95 .spawn((
96 Text::default(),
97 TextLayout::new_with_justify(JustifyText::Center),
98 Node {
99 align_self: AlignSelf::Center,
100 justify_self: JustifySelf::Center,
101 ..default()
102 },
103 ))
104 .with_children(|p| {
105 p.spawn(TextSpan::new("Press A or B to trigger a one-shot system\n"));
106 p.spawn(TextSpan::new("Last Triggered: "));
107 p.spawn((
108 TextSpan::new("-"),
109 TextColor(bevy::color::palettes::css::ORANGE.into()),
110 ));
111 });
112}
138fn add_buttons(commands: &mut Commands, font: &Handle<Font>, color_grading: &ColorGrading) {
139 // Spawn the parent node that contains all the buttons.
140 commands
141 .spawn(Node {
142 flex_direction: FlexDirection::Column,
143 position_type: PositionType::Absolute,
144 row_gap: Val::Px(6.0),
145 left: Val::Px(12.0),
146 bottom: Val::Px(12.0),
147 ..default()
148 })
149 .with_children(|parent| {
150 // Create the first row, which contains the global controls.
151 add_buttons_for_global_controls(parent, color_grading, font);
152
153 // Create the rows for individual controls.
154 for section in [
155 SelectedColorGradingSection::Highlights,
156 SelectedColorGradingSection::Midtones,
157 SelectedColorGradingSection::Shadows,
158 ] {
159 add_buttons_for_section(parent, section, color_grading, font);
160 }
161 });
162}
163
164/// Adds the buttons for the global controls (those that control the scene as a
165/// whole as opposed to shadows, midtones, or highlights).
166fn add_buttons_for_global_controls(
167 parent: &mut ChildSpawnerCommands,
168 color_grading: &ColorGrading,
169 font: &Handle<Font>,
170) {
171 // Add the parent node for the row.
172 parent.spawn(Node::default()).with_children(|parent| {
173 // Add some placeholder text to fill this column.
174 parent.spawn(Node {
175 width: Val::Px(125.0),
176 ..default()
177 });
178
179 // Add each global color grading option button.
180 for option in [
181 SelectedGlobalColorGradingOption::Exposure,
182 SelectedGlobalColorGradingOption::Temperature,
183 SelectedGlobalColorGradingOption::Tint,
184 SelectedGlobalColorGradingOption::Hue,
185 ] {
186 add_button_for_value(
187 parent,
188 SelectedColorGradingOption::Global(option),
189 color_grading,
190 font,
191 );
192 }
193 });
194}
195
196/// Adds the buttons that control color grading for individual sections
197/// (highlights, midtones, shadows).
198fn add_buttons_for_section(
199 parent: &mut ChildSpawnerCommands,
200 section: SelectedColorGradingSection,
201 color_grading: &ColorGrading,
202 font: &Handle<Font>,
203) {
204 // Spawn the row container.
205 parent
206 .spawn(Node {
207 align_items: AlignItems::Center,
208 ..default()
209 })
210 .with_children(|parent| {
211 // Spawn the label ("Highlights", etc.)
212 add_text(parent, §ion.to_string(), font, Color::WHITE).insert(Node {
213 width: Val::Px(125.0),
214 ..default()
215 });
216
217 // Spawn the buttons.
218 for option in [
219 SelectedSectionColorGradingOption::Saturation,
220 SelectedSectionColorGradingOption::Contrast,
221 SelectedSectionColorGradingOption::Gamma,
222 SelectedSectionColorGradingOption::Gain,
223 SelectedSectionColorGradingOption::Lift,
224 ] {
225 add_button_for_value(
226 parent,
227 SelectedColorGradingOption::Section(section, option),
228 color_grading,
229 font,
230 );
231 }
232 });
233}
234
235/// Adds a button that controls one of the color grading values.
236fn add_button_for_value(
237 parent: &mut ChildSpawnerCommands,
238 option: SelectedColorGradingOption,
239 color_grading: &ColorGrading,
240 font: &Handle<Font>,
241) {
242 // Add the button node.
243 parent
244 .spawn((
245 Button,
246 Node {
247 border: UiRect::all(Val::Px(1.0)),
248 width: Val::Px(200.0),
249 justify_content: JustifyContent::Center,
250 align_items: AlignItems::Center,
251 padding: UiRect::axes(Val::Px(12.0), Val::Px(6.0)),
252 margin: UiRect::right(Val::Px(12.0)),
253 ..default()
254 },
255 BorderColor(Color::WHITE),
256 BorderRadius::MAX,
257 BackgroundColor(Color::BLACK),
258 ))
259 .insert(ColorGradingOptionWidget {
260 widget_type: ColorGradingOptionWidgetType::Button,
261 option,
262 })
263 .with_children(|parent| {
264 // Add the button label.
265 let label = match option {
266 SelectedColorGradingOption::Global(option) => option.to_string(),
267 SelectedColorGradingOption::Section(_, option) => option.to_string(),
268 };
269 add_text(parent, &label, font, Color::WHITE).insert(ColorGradingOptionWidget {
270 widget_type: ColorGradingOptionWidgetType::Label,
271 option,
272 });
273
274 // Add a spacer.
275 parent.spawn(Node {
276 flex_grow: 1.0,
277 ..default()
278 });
279
280 // Add the value text.
281 add_text(
282 parent,
283 &format!("{:.3}", option.get(color_grading)),
284 font,
285 Color::WHITE,
286 )
287 .insert(ColorGradingOptionWidget {
288 widget_type: ColorGradingOptionWidgetType::Value,
289 option,
290 });
291 });
292}
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}
- examples/3d/parenting.rs
- examples/window/window_drag_move.rs
- examples/3d/clustered_decals.rs
- examples/window/scale_factor_override.rs
- examples/3d/pcss.rs
- examples/stress_tests/many_glyphs.rs
- examples/stress_tests/text_pipeline.rs
- examples/3d/order_independent_transparency.rs
- examples/ecs/hierarchy.rs
- examples/window/low_power.rs
- examples/3d/../helpers/widgets.rs
- examples/ui/relative_cursor_position.rs
- examples/ui/ui_material.rs
- examples/ui/ui_scaling.rs
- examples/ui/ui_texture_atlas.rs
- examples/camera/first_person_view_model.rs
- examples/ui/ghost_nodes.rs
- examples/ui/overflow_debug.rs
- examples/stress_tests/many_buttons.rs
- examples/testbed/2d.rs
- examples/ui/ui_texture_slice.rs
- examples/math/cubic_splines.rs
- examples/animation/animation_masks.rs
- examples/animation/animated_ui.rs
- examples/ui/ui_texture_slice_flip_and_tile.rs
- examples/3d/motion_blur.rs
- examples/ui/viewport_debug.rs
- examples/ui/size_constraints.rs
- examples/ui/transparency_ui.rs
- examples/ui/render_ui_to_texture.rs
- examples/ui/ui_texture_atlas_slice.rs
- examples/testbed/ui.rs
- examples/picking/sprite_picking.rs
- examples/3d/spotlight.rs
- examples/ui/overflow.rs
- examples/ui/flex_layout.rs
- examples/ui/overflow_clip_margin.rs
- examples/animation/easing_functions.rs
- examples/stress_tests/bevymark.rs
- examples/shader/shader_prepass.rs
- examples/ui/display_and_visibility.rs
- examples/ui/tab_navigation.rs
- examples/3d/shadow_biases.rs
- examples/stress_tests/many_foxes.rs
- examples/3d/split_screen.rs
- examples/ui/z_index.rs
- examples/games/desk_toy.rs
- examples/2d/text2d.rs
- examples/2d/sprite_scale.rs
- examples/3d/parallax_mapping.rs
- examples/3d/blend_modes.rs
- examples/animation/animated_transform.rs
- examples/ui/box_shadow.rs
- examples/ui/text_debug.rs
- examples/ui/grid.rs
- examples/ui/borders.rs
- examples/ui/scroll.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: Val::Percent(100.0),
118 height: Val::Percent(100.0),
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: Val::Px(300.0),
130 justify_content: JustifyContent::Center,
131 align_items: AlignItems::Center,
132 margin: UiRect::all(Val::Px(12.0)),
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: Val::Percent(100.),
144 height: Val::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: Val::Px(200.0),
168 height: Val::Px(120.0),
169 // Add a border so we can show which element is focused
170 border: UiRect::all(Val::Px(4.0)),
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(Val::Px(16.0)),
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: JustifyText::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
39fn setup(
40 mut commands: Commands,
41 asset_server: Res<AssetServer>,
42 mut meshes: ResMut<Assets<Mesh>>,
43 mut materials: ResMut<Assets<StandardMaterial>>,
44 mut skinned_mesh_inverse_bindposes_assets: ResMut<Assets<SkinnedMeshInverseBindposes>>,
45) {
46 // Create a camera
47 commands.spawn((
48 Camera3d::default(),
49 Transform::from_xyz(2.5, 2.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
50 ));
51
52 // Create inverse bindpose matrices for a skeleton consists of 2 joints
53 let inverse_bindposes = skinned_mesh_inverse_bindposes_assets.add(vec![
54 Mat4::from_translation(Vec3::new(-0.5, -1.0, 0.0)),
55 Mat4::from_translation(Vec3::new(-0.5, -1.0, 0.0)),
56 ]);
57
58 // Create a mesh
59 let mesh = Mesh::new(
60 PrimitiveTopology::TriangleList,
61 RenderAssetUsages::RENDER_WORLD,
62 )
63 // Set mesh vertex positions
64 .with_inserted_attribute(
65 Mesh::ATTRIBUTE_POSITION,
66 vec![
67 [0.0, 0.0, 0.0],
68 [1.0, 0.0, 0.0],
69 [0.0, 0.5, 0.0],
70 [1.0, 0.5, 0.0],
71 [0.0, 1.0, 0.0],
72 [1.0, 1.0, 0.0],
73 [0.0, 1.5, 0.0],
74 [1.0, 1.5, 0.0],
75 [0.0, 2.0, 0.0],
76 [1.0, 2.0, 0.0],
77 ],
78 )
79 // Add UV coordinates that map the left half of the texture since its a 1 x
80 // 2 rectangle.
81 .with_inserted_attribute(
82 Mesh::ATTRIBUTE_UV_0,
83 vec![
84 [0.0, 0.00],
85 [0.5, 0.00],
86 [0.0, 0.25],
87 [0.5, 0.25],
88 [0.0, 0.50],
89 [0.5, 0.50],
90 [0.0, 0.75],
91 [0.5, 0.75],
92 [0.0, 1.00],
93 [0.5, 1.00],
94 ],
95 )
96 // Set mesh vertex normals
97 .with_inserted_attribute(Mesh::ATTRIBUTE_NORMAL, vec![[0.0, 0.0, 1.0]; 10])
98 // Set mesh vertex joint indices for mesh skinning.
99 // Each vertex gets 4 indices used to address the `JointTransforms` array in the vertex shader
100 // as well as `SkinnedMeshJoint` array in the `SkinnedMesh` component.
101 // This means that a maximum of 4 joints can affect a single vertex.
102 .with_inserted_attribute(
103 Mesh::ATTRIBUTE_JOINT_INDEX,
104 // Need to be explicit here as [u16; 4] could be either Uint16x4 or Unorm16x4.
105 VertexAttributeValues::Uint16x4(vec![
106 [0, 0, 0, 0],
107 [0, 0, 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 [0, 1, 0, 0],
115 [0, 1, 0, 0],
116 ]),
117 )
118 // Set mesh vertex joint weights for mesh skinning.
119 // Each vertex gets 4 joint weights corresponding to the 4 joint indices assigned to it.
120 // The sum of these weights should equal to 1.
121 .with_inserted_attribute(
122 Mesh::ATTRIBUTE_JOINT_WEIGHT,
123 vec![
124 [1.00, 0.00, 0.0, 0.0],
125 [1.00, 0.00, 0.0, 0.0],
126 [0.75, 0.25, 0.0, 0.0],
127 [0.75, 0.25, 0.0, 0.0],
128 [0.50, 0.50, 0.0, 0.0],
129 [0.50, 0.50, 0.0, 0.0],
130 [0.25, 0.75, 0.0, 0.0],
131 [0.25, 0.75, 0.0, 0.0],
132 [0.00, 1.00, 0.0, 0.0],
133 [0.00, 1.00, 0.0, 0.0],
134 ],
135 )
136 // Tell bevy to construct triangles from a list of vertex indices,
137 // where each 3 vertex indices form a triangle.
138 .with_inserted_indices(Indices::U16(vec![
139 0, 1, 3, 0, 3, 2, 2, 3, 5, 2, 5, 4, 4, 5, 7, 4, 7, 6, 6, 7, 9, 6, 9, 8,
140 ]));
141
142 let mesh = meshes.add(mesh);
143
144 // We're seeding the PRNG here to make this example deterministic for testing purposes.
145 // This isn't strictly required in practical use unless you need your app to be deterministic.
146 let mut rng = ChaCha8Rng::seed_from_u64(42);
147
148 for i in -5..5 {
149 // Create joint entities
150 let joint_0 = commands
151 .spawn(Transform::from_xyz(
152 i as f32 * 1.5,
153 0.0,
154 // Move quads back a small amount to avoid Z-fighting and not
155 // obscure the transform gizmos.
156 -(i as f32 * 0.01).abs(),
157 ))
158 .id();
159 let joint_1 = commands.spawn((AnimatedJoint(i), Transform::IDENTITY)).id();
160
161 // Set joint_1 as a child of joint_0.
162 commands.entity(joint_0).add_children(&[joint_1]);
163
164 // Each joint in this vector corresponds to each inverse bindpose matrix in `SkinnedMeshInverseBindposes`.
165 let joint_entities = vec![joint_0, joint_1];
166
167 // Create skinned mesh renderer. Note that its transform doesn't affect the position of the mesh.
168 commands.spawn((
169 Mesh3d(mesh.clone()),
170 MeshMaterial3d(materials.add(StandardMaterial {
171 base_color: Color::srgb(
172 rng.gen_range(0.0..1.0),
173 rng.gen_range(0.0..1.0),
174 rng.gen_range(0.0..1.0),
175 ),
176 base_color_texture: Some(asset_server.load("textures/uv_checker_bw.png")),
177 ..default()
178 })),
179 SkinnedMesh {
180 inverse_bindposes: inverse_bindposes.clone(),
181 joints: joint_entities,
182 },
183 ));
184 }
185}
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: Val::Percent(100.),
41 height: Val::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(Val::Px(15.), Val::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 `JustifyText` 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(JustifyText::Right),
76 Node {
77 max_width: Val::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: Val::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(Val::Px(15.), Val::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(JustifyText::Center),
118 Node {
119 max_width: Val::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(JustifyText::Left),
134 Node {
135 max_width: Val::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(JustifyText::Justified),
149 TextColor(GREEN_YELLOW.into()),
150 Node {
151 max_width: Val::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}
12fn setup(mut commands: Commands) {
13 commands.spawn(Camera2d);
14 let root = commands
15 .spawn((
16 Node {
17 margin: UiRect::all(Val::Px(25.0)),
18 align_self: AlignSelf::Stretch,
19 justify_self: JustifySelf::Stretch,
20 flex_wrap: FlexWrap::Wrap,
21 justify_content: JustifyContent::FlexStart,
22 align_items: AlignItems::FlexStart,
23 align_content: AlignContent::FlexStart,
24 ..default()
25 },
26 BackgroundColor(Color::srgb(0.25, 0.25, 0.25)),
27 ))
28 .id();
29
30 let root_rounded = commands
31 .spawn((
32 Node {
33 margin: UiRect::all(Val::Px(25.0)),
34 align_self: AlignSelf::Stretch,
35 justify_self: JustifySelf::Stretch,
36 flex_wrap: FlexWrap::Wrap,
37 justify_content: JustifyContent::FlexStart,
38 align_items: AlignItems::FlexStart,
39 align_content: AlignContent::FlexStart,
40 ..default()
41 },
42 BackgroundColor(Color::srgb(0.25, 0.25, 0.25)),
43 ))
44 .id();
45
46 // labels for the different border edges
47 let border_labels = [
48 "None",
49 "All",
50 "Left",
51 "Right",
52 "Top",
53 "Bottom",
54 "Horizontal",
55 "Vertical",
56 "Top Left",
57 "Bottom Left",
58 "Top Right",
59 "Bottom Right",
60 "Top Bottom Right",
61 "Top Bottom Left",
62 "Top Left Right",
63 "Bottom Left Right",
64 ];
65
66 // all the different combinations of border edges
67 // these correspond to the labels above
68 let borders = [
69 UiRect::default(),
70 UiRect::all(Val::Px(10.)),
71 UiRect::left(Val::Px(10.)),
72 UiRect::right(Val::Px(10.)),
73 UiRect::top(Val::Px(10.)),
74 UiRect::bottom(Val::Px(10.)),
75 UiRect::horizontal(Val::Px(10.)),
76 UiRect::vertical(Val::Px(10.)),
77 UiRect {
78 left: Val::Px(20.),
79 top: Val::Px(10.),
80 ..Default::default()
81 },
82 UiRect {
83 left: Val::Px(10.),
84 bottom: Val::Px(20.),
85 ..Default::default()
86 },
87 UiRect {
88 right: Val::Px(20.),
89 top: Val::Px(10.),
90 ..Default::default()
91 },
92 UiRect {
93 right: Val::Px(10.),
94 bottom: Val::Px(10.),
95 ..Default::default()
96 },
97 UiRect {
98 right: Val::Px(10.),
99 top: Val::Px(20.),
100 bottom: Val::Px(10.),
101 ..Default::default()
102 },
103 UiRect {
104 left: Val::Px(10.),
105 top: Val::Px(10.),
106 bottom: Val::Px(10.),
107 ..Default::default()
108 },
109 UiRect {
110 left: Val::Px(20.),
111 right: Val::Px(10.),
112 top: Val::Px(10.),
113 ..Default::default()
114 },
115 UiRect {
116 left: Val::Px(10.),
117 right: Val::Px(10.),
118 bottom: Val::Px(20.),
119 ..Default::default()
120 },
121 ];
122
123 for (label, border) in border_labels.into_iter().zip(borders) {
124 let inner_spot = commands
125 .spawn((
126 Node {
127 width: Val::Px(10.),
128 height: Val::Px(10.),
129 ..default()
130 },
131 BackgroundColor(YELLOW.into()),
132 ))
133 .id();
134 let border_node = commands
135 .spawn((
136 Node {
137 width: Val::Px(50.),
138 height: Val::Px(50.),
139 border,
140 margin: UiRect::all(Val::Px(20.)),
141 align_items: AlignItems::Center,
142 justify_content: JustifyContent::Center,
143 ..default()
144 },
145 BackgroundColor(MAROON.into()),
146 BorderColor(RED.into()),
147 Outline {
148 width: Val::Px(6.),
149 offset: Val::Px(6.),
150 color: Color::WHITE,
151 },
152 ))
153 .add_child(inner_spot)
154 .id();
155 let label_node = commands
156 .spawn((
157 Text::new(label),
158 TextFont {
159 font_size: 9.0,
160 ..Default::default()
161 },
162 ))
163 .id();
164 let container = commands
165 .spawn(Node {
166 flex_direction: FlexDirection::Column,
167 align_items: AlignItems::Center,
168 ..default()
169 })
170 .add_children(&[border_node, label_node])
171 .id();
172 commands.entity(root).add_child(container);
173 }
174
175 for (label, border) in border_labels.into_iter().zip(borders) {
176 let inner_spot = commands
177 .spawn((
178 Node {
179 width: Val::Px(10.),
180 height: Val::Px(10.),
181 ..default()
182 },
183 BorderRadius::MAX,
184 BackgroundColor(YELLOW.into()),
185 ))
186 .id();
187 let non_zero = |x, y| x != Val::Px(0.) && y != Val::Px(0.);
188 let border_size = |x, y| if non_zero(x, y) { f32::MAX } else { 0. };
189 let border_radius = BorderRadius::px(
190 border_size(border.left, border.top),
191 border_size(border.right, border.top),
192 border_size(border.right, border.bottom),
193 border_size(border.left, border.bottom),
194 );
195 let border_node = commands
196 .spawn((
197 Node {
198 width: Val::Px(50.),
199 height: Val::Px(50.),
200 border,
201 margin: UiRect::all(Val::Px(20.)),
202 align_items: AlignItems::Center,
203 justify_content: JustifyContent::Center,
204 ..default()
205 },
206 BackgroundColor(MAROON.into()),
207 BorderColor(RED.into()),
208 border_radius,
209 Outline {
210 width: Val::Px(6.),
211 offset: Val::Px(6.),
212 color: Color::WHITE,
213 },
214 ))
215 .add_child(inner_spot)
216 .id();
217 let label_node = commands
218 .spawn((
219 Text::new(label),
220 TextFont {
221 font_size: 9.0,
222 ..Default::default()
223 },
224 ))
225 .id();
226 let container = commands
227 .spawn(Node {
228 flex_direction: FlexDirection::Column,
229 align_items: AlignItems::Center,
230 ..default()
231 })
232 .add_children(&[border_node, label_node])
233 .id();
234 commands.entity(root_rounded).add_child(container);
235 }
236
237 let border_label = commands
238 .spawn((
239 Node {
240 margin: UiRect {
241 left: Val::Px(25.0),
242 right: Val::Px(25.0),
243 top: Val::Px(25.0),
244 bottom: Val::Px(0.0),
245 },
246 ..default()
247 },
248 BackgroundColor(Color::srgb(0.25, 0.25, 0.25)),
249 ))
250 .with_children(|builder| {
251 builder.spawn((
252 Text::new("Borders"),
253 TextFont {
254 font_size: 20.0,
255 ..Default::default()
256 },
257 ));
258 })
259 .id();
260
261 let border_rounded_label = commands
262 .spawn((
263 Node {
264 margin: UiRect {
265 left: Val::Px(25.0),
266 right: Val::Px(25.0),
267 top: Val::Px(25.0),
268 bottom: Val::Px(0.0),
269 },
270 ..default()
271 },
272 BackgroundColor(Color::srgb(0.25, 0.25, 0.25)),
273 ))
274 .with_children(|builder| {
275 builder.spawn((
276 Text::new("Borders Rounded"),
277 TextFont {
278 font_size: 20.0,
279 ..Default::default()
280 },
281 ));
282 })
283 .id();
284
285 commands
286 .spawn((
287 Node {
288 margin: UiRect::all(Val::Px(25.0)),
289 flex_direction: FlexDirection::Column,
290 align_self: AlignSelf::Stretch,
291 justify_self: JustifySelf::Stretch,
292 flex_wrap: FlexWrap::Wrap,
293 justify_content: JustifyContent::FlexStart,
294 align_items: AlignItems::FlexStart,
295 align_content: AlignContent::FlexStart,
296 ..default()
297 },
298 BackgroundColor(Color::srgb(0.25, 0.25, 0.25)),
299 ))
300 .add_child(border_label)
301 .add_child(root)
302 .add_child(border_rounded_label)
303 .add_child(root_rounded);
304}
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 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
284fn spawn_small_cubes(
285 commands: &mut Commands,
286 meshes: &mut Assets<Mesh>,
287 materials: &mut Assets<StandardMaterial>,
288) {
289 // Add the cube mesh.
290 let small_cube = meshes.add(Cuboid::new(
291 SMALL_CUBE_SIZE,
292 SMALL_CUBE_SIZE,
293 SMALL_CUBE_SIZE,
294 ));
295
296 // Add the cube material.
297 let small_cube_material = materials.add(StandardMaterial {
298 base_color: SILVER.into(),
299 ..default()
300 });
301
302 // Create the entity that the small cubes will be parented to. This is the
303 // entity that we rotate.
304 let sphere_parent = commands
305 .spawn(Transform::from_translation(Vec3::ZERO))
306 .insert(Visibility::default())
307 .insert(SphereParent)
308 .id();
309
310 // Now we have to figure out where to place the cubes. To do that, we create
311 // a sphere mesh, but we don't add it to the scene. Instead, we inspect the
312 // sphere mesh to find the positions of its vertices, and spawn a small cube
313 // at each one. That way, we end up with a bunch of cubes arranged in a
314 // spherical shape.
315
316 // Create the sphere mesh, and extract the positions of its vertices.
317 let sphere = Sphere::new(OUTER_RADIUS)
318 .mesh()
319 .ico(OUTER_SUBDIVISION_COUNT)
320 .unwrap();
321 let sphere_positions = sphere.attribute(Mesh::ATTRIBUTE_POSITION).unwrap();
322
323 // At each vertex, create a small cube.
324 for sphere_position in sphere_positions.as_float3().unwrap() {
325 let sphere_position = Vec3::from_slice(sphere_position);
326 let small_cube = commands
327 .spawn(Mesh3d(small_cube.clone()))
328 .insert(MeshMaterial3d(small_cube_material.clone()))
329 .insert(Transform::from_translation(sphere_position))
330 .id();
331 commands.entity(sphere_parent).add_child(small_cube);
332 }
333}
265fn setup_node_rects(commands: &mut Commands) {
266 for (node_rect, node_type) in NODE_RECTS.iter().zip(NODE_TYPES.iter()) {
267 let node_string = match *node_type {
268 NodeType::Clip(ref clip) => clip.text,
269 NodeType::Blend(text) => text,
270 };
271
272 let text = commands
273 .spawn((
274 Text::new(node_string),
275 TextFont {
276 font_size: 16.0,
277 ..default()
278 },
279 TextColor(ANTIQUE_WHITE.into()),
280 TextLayout::new_with_justify(JustifyText::Center),
281 ))
282 .id();
283
284 let container = {
285 let mut container = commands.spawn((
286 Node {
287 position_type: PositionType::Absolute,
288 bottom: Val::Px(node_rect.bottom),
289 left: Val::Px(node_rect.left),
290 height: Val::Px(node_rect.height),
291 width: Val::Px(node_rect.width),
292 align_items: AlignItems::Center,
293 justify_items: JustifyItems::Center,
294 align_content: AlignContent::Center,
295 justify_content: JustifyContent::Center,
296 ..default()
297 },
298 BorderColor(WHITE.into()),
299 Outline::new(Val::Px(1.), Val::ZERO, Color::WHITE),
300 ));
301
302 if let NodeType::Clip(clip) = node_type {
303 container.insert((
304 Interaction::None,
305 RelativeCursorPosition::default(),
306 (*clip).clone(),
307 ));
308 }
309
310 container.id()
311 };
312
313 // Create the background color.
314 if let NodeType::Clip(_) = node_type {
315 let background = commands
316 .spawn((
317 Node {
318 position_type: PositionType::Absolute,
319 top: Val::Px(0.),
320 left: Val::Px(0.),
321 height: Val::Px(node_rect.height),
322 width: Val::Px(node_rect.width),
323 ..default()
324 },
325 BackgroundColor(DARK_GREEN.into()),
326 ))
327 .id();
328
329 commands.entity(container).add_child(background);
330 }
331
332 commands.entity(container).add_child(text);
333 }
334}
526fn create_cubes(
527 image_assets: Res<Assets<Image>>,
528 mut commands: Commands,
529 irradiance_volumes: Query<(&IrradianceVolume, &GlobalTransform)>,
530 voxel_cube_parents: Query<Entity, With<VoxelCubeParent>>,
531 voxel_cubes: Query<Entity, With<VoxelCube>>,
532 example_assets: Res<ExampleAssets>,
533 mut voxel_visualization_material_assets: ResMut<Assets<VoxelVisualizationMaterial>>,
534) {
535 // If voxel cubes have already been spawned, don't do anything.
536 if !voxel_cubes.is_empty() {
537 return;
538 }
539
540 let Some(voxel_cube_parent) = voxel_cube_parents.iter().next() else {
541 return;
542 };
543
544 for (irradiance_volume, global_transform) in irradiance_volumes.iter() {
545 let Some(image) = image_assets.get(&irradiance_volume.voxels) else {
546 continue;
547 };
548
549 let resolution = image.texture_descriptor.size;
550
551 let voxel_cube_material = voxel_visualization_material_assets.add(ExtendedMaterial {
552 base: StandardMaterial::from(Color::from(RED)),
553 extension: VoxelVisualizationExtension {
554 irradiance_volume_info: VoxelVisualizationIrradianceVolumeInfo {
555 world_from_voxel: VOXEL_FROM_WORLD.inverse(),
556 voxel_from_world: VOXEL_FROM_WORLD,
557 resolution: uvec3(
558 resolution.width,
559 resolution.height,
560 resolution.depth_or_array_layers,
561 ),
562 intensity: IRRADIANCE_VOLUME_INTENSITY,
563 },
564 },
565 });
566
567 let scale = vec3(
568 1.0 / resolution.width as f32,
569 1.0 / resolution.height as f32,
570 1.0 / resolution.depth_or_array_layers as f32,
571 );
572
573 // Spawn a cube for each voxel.
574 for z in 0..resolution.depth_or_array_layers {
575 for y in 0..resolution.height {
576 for x in 0..resolution.width {
577 let uvw = (uvec3(x, y, z).as_vec3() + 0.5) * scale - 0.5;
578 let pos = global_transform.transform_point(uvw);
579 let voxel_cube = commands
580 .spawn((
581 Mesh3d(example_assets.voxel_cube.clone()),
582 MeshMaterial3d(voxel_cube_material.clone()),
583 Transform::from_scale(Vec3::splat(VOXEL_CUBE_SCALE))
584 .with_translation(pos),
585 ))
586 .insert(VoxelCube)
587 .insert(NotShadowCaster)
588 .id();
589
590 commands.entity(voxel_cube_parent).add_child(voxel_cube);
591 }
592 }
593 }
594 }
595}
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::thread_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.r#gen::<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}
45fn spawn(mut commands: Commands, asset_server: Res<AssetServer>) {
46 commands.spawn(Camera2d);
47
48 let text_font = TextFont {
49 font: asset_server.load("fonts/FiraSans-Bold.ttf"),
50 font_size: 12.0,
51 ..default()
52 };
53
54 let root = commands
55 .spawn((
56 Node {
57 width: Val::Percent(100.),
58 height: Val::Percent(100.),
59 flex_direction: FlexDirection::Column,
60 ..default()
61 },
62 BackgroundColor(Color::BLACK),
63 ))
64 .id();
65
66 for linebreak in [
67 LineBreak::AnyCharacter,
68 LineBreak::WordBoundary,
69 LineBreak::WordOrCharacter,
70 LineBreak::NoWrap,
71 ] {
72 let row_id = commands
73 .spawn(Node {
74 flex_direction: FlexDirection::Row,
75 justify_content: JustifyContent::SpaceAround,
76 align_items: AlignItems::Center,
77 width: Val::Percent(100.),
78 height: Val::Percent(50.),
79 ..default()
80 })
81 .id();
82
83 let justifications = vec![
84 JustifyContent::Center,
85 JustifyContent::FlexStart,
86 JustifyContent::FlexEnd,
87 JustifyContent::SpaceAround,
88 JustifyContent::SpaceBetween,
89 JustifyContent::SpaceEvenly,
90 ];
91
92 for (i, justification) in justifications.into_iter().enumerate() {
93 let c = 0.3 + i as f32 * 0.1;
94 let column_id = commands
95 .spawn((
96 Node {
97 justify_content: justification,
98 flex_direction: FlexDirection::Column,
99 width: Val::Percent(16.),
100 height: Val::Percent(95.),
101 overflow: Overflow::clip_x(),
102 ..default()
103 },
104 BackgroundColor(Color::srgb(0.5, c, 1.0 - c)),
105 ))
106 .id();
107
108 let messages = [
109 format!("JustifyContent::{justification:?}"),
110 format!("LineBreakOn::{linebreak:?}"),
111 "Line 1\nLine 2".to_string(),
112 "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas auctor, nunc ac faucibus fringilla.".to_string(),
113 "pneumonoultramicroscopicsilicovolcanoconiosis".to_string()
114 ];
115
116 for (j, message) in messages.into_iter().enumerate() {
117 commands.entity(column_id).with_child((
118 Text(message.clone()),
119 text_font.clone(),
120 TextLayout::new(JustifyText::Left, linebreak),
121 BackgroundColor(Color::srgb(0.8 - j as f32 * 0.2, 0., 0.)),
122 ));
123 }
124 commands.entity(row_id).add_child(column_id);
125 }
126 commands.entity(root).add_child(row_id);
127 }
128}
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 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 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?
146fn spawn_text(mut commands: Commands) {
147 commands
148 .spawn(Node {
149 position_type: PositionType::Absolute,
150 bottom: Val::Px(12.0),
151 left: Val::Px(12.0),
152 ..default()
153 })
154 .with_child((
155 Text::new("Move the player with WASD"),
156 TextFont {
157 font_size: 25.0,
158 ..default()
159 },
160 ));
161}
More examples
35fn setup_ui(mut commands: Commands) {
36 // Node that fills entire background
37 commands
38 .spawn(Node {
39 width: Val::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}
193fn spawn_text(mut commands: Commands) {
194 commands
195 .spawn(Node {
196 position_type: PositionType::Absolute,
197 bottom: Val::Px(12.0),
198 left: Val::Px(12.0),
199 ..default()
200 })
201 .with_child(Text::new(concat!(
202 "Move the camera with your mouse.\n",
203 "Press arrow up to decrease the FOV of the world model.\n",
204 "Press arrow down to increase the FOV of the world model."
205 )));
206}
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: Val::Px(12.),
155 left: Val::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: Val::Percent(100.0),
251 width: Val::Percent(100.0),
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}
26fn setup(mut commands: Commands) {
27 // camera
28 commands.spawn(Camera2d);
29 // root node
30 commands
31 .spawn(Node {
32 width: Val::Percent(100.0),
33 height: Val::Percent(100.0),
34 justify_content: JustifyContent::SpaceBetween,
35 ..default()
36 })
37 .with_children(|parent| {
38 // left vertical fill (border)
39 parent
40 .spawn((
41 Node {
42 width: Val::Px(300.0),
43 height: Val::Percent(100.0),
44 border: UiRect::all(Val::Px(2.0)),
45 ..default()
46 },
47 BackgroundColor(Color::srgb(0.65, 0.65, 0.65)),
48 ))
49 .with_child((
50 CustomText,
51 Text::new("Example text"),
52 TextFont {
53 font_size: 25.0,
54 ..default()
55 },
56 Node {
57 align_self: AlignSelf::FlexEnd,
58 ..default()
59 },
60 ));
61 });
62}
- examples/ui/tab_navigation.rs
- examples/stress_tests/many_glyphs.rs
- examples/testbed/ui.rs
- examples/stress_tests/many_buttons.rs
- examples/3d/spherical_area_lights.rs
- examples/window/multiple_windows.rs
- examples/ui/ui_texture_atlas.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/2d/text2d.rs
- examples/animation/animation_masks.rs
- examples/ui/directional_navigation.rs
Sourcepub fn remove_parent(&mut self) -> &mut EntityCommands<'a>
👎Deprecated since 0.16.0: Use entity_commands.remove::<ChildOf>()
pub fn remove_parent(&mut self) -> &mut EntityCommands<'a>
Removes the ChildOf
component, if it exists.
Sourcepub fn set_parent(&mut self, parent: Entity) -> &mut EntityCommands<'a>
👎Deprecated since 0.16.0: Use entity_commands.insert(ChildOf(entity))
pub fn set_parent(&mut self, parent: Entity) -> &mut EntityCommands<'a>
Inserts the ChildOf
component with the given parent
entity, if it exists.
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_relationships` 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_relationships` 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_relationships` 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_relationships` 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.
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.
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?
244fn setup_menu(mut commands: Commands) {
245 let button_entity = commands
246 .spawn((
247 Node {
248 // center button
249 width: Val::Percent(100.),
250 height: Val::Percent(100.),
251 justify_content: JustifyContent::Center,
252 align_items: AlignItems::Center,
253 ..default()
254 },
255 children![(
256 Button,
257 Node {
258 width: Val::Px(150.),
259 height: Val::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}
More examples
52fn setup_menu(mut commands: Commands) {
53 let button_entity = commands
54 .spawn((
55 Node {
56 // center button
57 width: Val::Percent(100.),
58 height: Val::Percent(100.),
59 justify_content: JustifyContent::Center,
60 align_items: AlignItems::Center,
61 ..default()
62 },
63 children![(
64 Button,
65 Node {
66 width: Val::Px(150.),
67 height: Val::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: Val::Percent(100.),
163 height: Val::Percent(100.),
164 justify_content: JustifyContent::Center,
165 align_items: AlignItems::Center,
166 ..default()
167 },
168 children![(
169 Button,
170 Node {
171 width: Val::Px(150.),
172 height: Val::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.gen_range(-400.0..400.0),
100 rng.gen_range(0.0..400.0),
101 rng.r#gen(),
102 );
103 let dir = rng.gen_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.r#gen();
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}
72fn setup(mut commands: Commands) {
73 commands.spawn(Camera2d);
74 commands.spawn((
75 Text::new(
76 "Click on a \"Mine\" to trigger it.\n\
77 When it explodes it will trigger all overlapping mines.",
78 ),
79 Node {
80 position_type: PositionType::Absolute,
81 top: Val::Px(12.),
82 left: Val::Px(12.),
83 ..default()
84 },
85 ));
86
87 let mut rng = ChaCha8Rng::seed_from_u64(19878367467713);
88
89 commands
90 .spawn(Mine::random(&mut rng))
91 // Observers can watch for events targeting a specific entity.
92 // This will create a new observer that runs whenever the Explode event
93 // is triggered for this spawned entity.
94 .observe(explode_mine);
95
96 // We want to spawn a bunch of mines. We could just call the code above for each of them.
97 // That would create a new observer instance for every Mine entity. Having duplicate observers
98 // generally isn't worth worrying about as the overhead is low. But if you want to be maximally efficient,
99 // you can reuse observers across entities.
100 //
101 // First, observers are actually just entities with the Observer component! The `observe()` functions
102 // you've seen so far in this example are just shorthand for manually spawning an observer.
103 let mut observer = Observer::new(explode_mine);
104
105 // As we spawn entities, we can make this observer watch each of them:
106 for _ in 0..1000 {
107 let entity = commands.spawn(Mine::random(&mut rng)).id();
108 observer.watch_entity(entity);
109 }
110
111 // By spawning the Observer component, it becomes active!
112 commands.spawn(observer);
113}
- examples/window/multiple_windows.rs
- examples/ecs/relationships.rs
- examples/3d/occlusion_culling.rs
- examples/animation/eased_motion.rs
- examples/window/monitor_info.rs
- examples/animation/animation_graph.rs
- examples/async_tasks/async_compute.rs
- examples/ui/render_ui_to_texture.rs
- examples/3d/irradiance_volumes.rs
- examples/stress_tests/many_buttons.rs
- examples/state/computed_states.rs
- examples/stress_tests/transform_hierarchy.rs
- examples/ui/text_wrap_debug.rs
- examples/games/alien_cake_addict.rs
- examples/testbed/ui.rs
- examples/stress_tests/many_foxes.rs
- examples/3d/split_screen.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/borders.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);
fn level_up_system(mut commands: Commands, player: Res<PlayerEntity>) {
commands
.entity(player.entity)
.entry::<Level>()
// Modify the component if it exists.
.and_modify(|mut lvl| lvl.0 += 1)
// Otherwise, insert a default value.
.or_insert(Level(0));
}
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?
156fn spawn_camera(commands: &mut Commands) {
157 commands
158 .spawn(Camera3d::default())
159 .insert(Transform::from_xyz(-0.7, 0.7, 1.0).looking_at(vec3(0.0, 0.3, 0.0), Vec3::Y));
160}
161
162/// Spawns the scene.
163///
164/// The scene is loaded from a glTF file.
165fn spawn_scene(commands: &mut Commands, asset_server: &AssetServer) {
166 commands
167 .spawn(SceneRoot(
168 asset_server.load(
169 GltfAssetLabel::Scene(0)
170 .from_asset("models/MixedLightingExample/MixedLightingExample.gltf"),
171 ),
172 ))
173 .observe(
174 |_: Trigger<SceneInstanceReady>,
175 mut lighting_mode_change_event_writer: EventWriter<LightingModeChanged>| {
176 // When the scene loads, send a `LightingModeChanged` event so
177 // that we set up the lightmaps.
178 lighting_mode_change_event_writer.write(LightingModeChanged);
179 },
180 );
181}
182
183/// Spawns the buttons that allow the user to change the lighting mode.
184fn spawn_buttons(commands: &mut Commands) {
185 commands
186 .spawn(widgets::main_ui_node())
187 .with_children(|parent| {
188 widgets::spawn_option_buttons(
189 parent,
190 "Lighting",
191 &[
192 (LightingMode::Baked, "Baked"),
193 (LightingMode::MixedDirect, "Mixed (Direct)"),
194 (LightingMode::MixedIndirect, "Mixed (Indirect)"),
195 (LightingMode::RealTime, "Real-Time"),
196 ],
197 );
198 });
199}
200
201/// Spawns the help text at the top of the window.
202fn spawn_help_text(commands: &mut Commands, app_status: &AppStatus) {
203 commands.spawn((
204 create_help_text(app_status),
205 Node {
206 position_type: PositionType::Absolute,
207 top: Val::Px(12.0),
208 left: Val::Px(12.0),
209 ..default()
210 },
211 HelpText,
212 ));
213}
214
215/// Adds lightmaps to and/or removes lightmaps from objects in the scene when
216/// the lighting mode changes.
217///
218/// This is also called right after the scene loads in order to set up the
219/// lightmaps.
220fn update_lightmaps(
221 mut commands: Commands,
222 asset_server: Res<AssetServer>,
223 mut materials: ResMut<Assets<StandardMaterial>>,
224 meshes: Query<(Entity, &Name, &MeshMaterial3d<StandardMaterial>), With<Mesh3d>>,
225 mut lighting_mode_change_event_reader: EventReader<LightingModeChanged>,
226 app_status: Res<AppStatus>,
227) {
228 // Only run if the lighting mode changed. (Note that a change event is fired
229 // when the scene first loads.)
230 if lighting_mode_change_event_reader.read().next().is_none() {
231 return;
232 }
233
234 // Select the lightmap to use, based on the lighting mode.
235 let lightmap: Option<Handle<Image>> = match app_status.lighting_mode {
236 LightingMode::Baked => {
237 Some(asset_server.load("lightmaps/MixedLightingExample-Baked.zstd.ktx2"))
238 }
239 LightingMode::MixedDirect => {
240 Some(asset_server.load("lightmaps/MixedLightingExample-MixedDirect.zstd.ktx2"))
241 }
242 LightingMode::MixedIndirect => {
243 Some(asset_server.load("lightmaps/MixedLightingExample-MixedIndirect.zstd.ktx2"))
244 }
245 LightingMode::RealTime => None,
246 };
247
248 'outer: for (entity, name, material) in &meshes {
249 // Add lightmaps to or remove lightmaps from the scenery objects in the
250 // scene (all objects but the sphere).
251 //
252 // Note that doing a linear search through the `LIGHTMAPS` array is
253 // inefficient, but we do it anyway in this example to improve clarity.
254 for (lightmap_name, uv_rect) in LIGHTMAPS {
255 if &**name != lightmap_name {
256 continue;
257 }
258
259 // Lightmap exposure defaults to zero, so we need to set it.
260 if let Some(ref mut material) = materials.get_mut(material) {
261 material.lightmap_exposure = LIGHTMAP_EXPOSURE;
262 }
263
264 // Add or remove the lightmap.
265 match lightmap {
266 Some(ref lightmap) => {
267 commands.entity(entity).insert(Lightmap {
268 image: (*lightmap).clone(),
269 uv_rect,
270 bicubic_sampling: false,
271 });
272 }
273 None => {
274 commands.entity(entity).remove::<Lightmap>();
275 }
276 }
277 continue 'outer;
278 }
279
280 // Add lightmaps to or remove lightmaps from the sphere.
281 if &**name == "Sphere" {
282 // Lightmap exposure defaults to zero, so we need to set it.
283 if let Some(ref mut material) = materials.get_mut(material) {
284 material.lightmap_exposure = LIGHTMAP_EXPOSURE;
285 }
286
287 // Add or remove the lightmap from the sphere. We only apply the
288 // lightmap in fully-baked mode.
289 match (&lightmap, app_status.lighting_mode) {
290 (Some(lightmap), LightingMode::Baked) => {
291 commands.entity(entity).insert(Lightmap {
292 image: (*lightmap).clone(),
293 uv_rect: SPHERE_UV_RECT,
294 bicubic_sampling: false,
295 });
296 }
297 _ => {
298 commands.entity(entity).remove::<Lightmap>();
299 }
300 }
301 }
302 }
303}
304
305/// Converts a uv rectangle from the OpenGL coordinate system (origin in the
306/// lower left) to the Vulkan coordinate system (origin in the upper left) that
307/// Bevy uses.
308///
309/// For this particular example, the baking tool happened to use the OpenGL
310/// coordinate system, so it was more convenient to do the conversion at compile
311/// time than to pre-calculate and hard-code the values.
312const fn uv_rect_opengl(gl_min: Vec2, size: Vec2) -> Rect {
313 let min = vec2(gl_min.x, 1.0 - gl_min.y - size.y);
314 Rect {
315 min,
316 max: vec2(min.x + size.x, min.y + size.y),
317 }
318}
319
320/// Ensures that clicking on the scene to move the sphere doesn't result in a
321/// hit on the sphere itself.
322fn make_sphere_nonpickable(
323 mut commands: Commands,
324 mut query: Query<(Entity, &Name), (With<Mesh3d>, Without<Pickable>)>,
325) {
326 for (sphere, name) in &mut query {
327 if &**name == "Sphere" {
328 commands.entity(sphere).insert(Pickable::IGNORE);
329 }
330 }
331}
332
333/// Updates the directional light settings as necessary when the lighting mode
334/// changes.
335fn update_directional_light(
336 mut lights: Query<&mut DirectionalLight>,
337 mut lighting_mode_change_event_reader: EventReader<LightingModeChanged>,
338 app_status: Res<AppStatus>,
339) {
340 // Only run if the lighting mode changed. (Note that a change event is fired
341 // when the scene first loads.)
342 if lighting_mode_change_event_reader.read().next().is_none() {
343 return;
344 }
345
346 // Real-time direct light is used on the scenery if we're using mixed
347 // indirect or real-time mode.
348 let scenery_is_lit_in_real_time = matches!(
349 app_status.lighting_mode,
350 LightingMode::MixedIndirect | LightingMode::RealTime
351 );
352
353 for mut light in &mut lights {
354 light.affects_lightmapped_mesh_diffuse = scenery_is_lit_in_real_time;
355 // Don't bother enabling shadows if they won't show up on the scenery.
356 light.shadows_enabled = scenery_is_lit_in_real_time;
357 }
358}
359
360/// Updates the state of the selection widgets at the bottom of the window when
361/// the lighting mode changes.
362fn update_radio_buttons(
363 mut widgets: Query<
364 (
365 Entity,
366 Option<&mut BackgroundColor>,
367 Has<Text>,
368 &WidgetClickSender<LightingMode>,
369 ),
370 Or<(With<RadioButton>, With<RadioButtonText>)>,
371 >,
372 app_status: Res<AppStatus>,
373 mut writer: TextUiWriter,
374) {
375 for (entity, image, has_text, sender) in &mut widgets {
376 let selected = **sender == app_status.lighting_mode;
377
378 if let Some(mut bg_color) = image {
379 widgets::update_ui_radio_button(&mut bg_color, selected);
380 }
381 if has_text {
382 widgets::update_ui_radio_button_text(entity, &mut writer, selected);
383 }
384 }
385}
386
387/// Handles clicks on the widgets at the bottom of the screen and fires
388/// [`LightingModeChanged`] events.
389fn handle_lighting_mode_change(
390 mut widget_click_event_reader: EventReader<WidgetClickEvent<LightingMode>>,
391 mut lighting_mode_change_event_writer: EventWriter<LightingModeChanged>,
392 mut app_status: ResMut<AppStatus>,
393) {
394 for event in widget_click_event_reader.read() {
395 app_status.lighting_mode = **event;
396 lighting_mode_change_event_writer.write(LightingModeChanged);
397 }
398}
399
400/// Moves the sphere to its original position when the user selects the baked
401/// lighting mode.
402///
403/// As the light from the sphere is precomputed and depends on the sphere's
404/// original position, the sphere must be placed there in order for the lighting
405/// to be correct.
406fn reset_sphere_position(
407 mut objects: Query<(&Name, &mut Transform)>,
408 mut lighting_mode_change_event_reader: EventReader<LightingModeChanged>,
409 app_status: Res<AppStatus>,
410) {
411 // Only run if the lighting mode changed and if the lighting mode is
412 // `LightingMode::Baked`. (Note that a change event is fired when the scene
413 // first loads.)
414 if lighting_mode_change_event_reader.read().next().is_none()
415 || app_status.lighting_mode != LightingMode::Baked
416 {
417 return;
418 }
419
420 for (name, mut transform) in &mut objects {
421 if &**name == "Sphere" {
422 transform.translation = INITIAL_SPHERE_POSITION;
423 break;
424 }
425 }
426}
427
428/// Updates the position of the sphere when the user clicks on a spot in the
429/// scene.
430///
431/// Note that the position of the sphere is locked in baked lighting mode.
432fn move_sphere(
433 mouse_button_input: Res<ButtonInput<MouseButton>>,
434 pointers: Query<&PointerInteraction>,
435 mut meshes: Query<(&Name, &ChildOf), With<Mesh3d>>,
436 mut transforms: Query<&mut Transform>,
437 app_status: Res<AppStatus>,
438) {
439 // Only run when the left button is clicked and we're not in baked lighting
440 // mode.
441 if app_status.lighting_mode == LightingMode::Baked
442 || !mouse_button_input.pressed(MouseButton::Left)
443 {
444 return;
445 }
446
447 // Find the sphere.
448 let Some(child_of) = meshes
449 .iter_mut()
450 .filter_map(|(name, child_of)| {
451 if &**name == "Sphere" {
452 Some(child_of)
453 } else {
454 None
455 }
456 })
457 .next()
458 else {
459 return;
460 };
461
462 // Grab its transform.
463 let Ok(mut transform) = transforms.get_mut(child_of.parent()) else {
464 return;
465 };
466
467 // Set its transform to the appropriate position, as determined by the
468 // picking subsystem.
469 for interaction in pointers.iter() {
470 if let Some(&(
471 _,
472 HitData {
473 position: Some(position),
474 ..
475 },
476 )) = interaction.get_nearest_hit()
477 {
478 transform.translation = position + vec3(0.0, SPHERE_OFFSET, 0.0);
479 }
480 }
481}
482
483/// Changes the help text at the top of the screen when the lighting mode
484/// changes.
485fn adjust_help_text(
486 mut commands: Commands,
487 help_texts: Query<Entity, With<HelpText>>,
488 app_status: Res<AppStatus>,
489 mut lighting_mode_change_event_reader: EventReader<LightingModeChanged>,
490) {
491 if lighting_mode_change_event_reader.read().next().is_none() {
492 return;
493 }
494
495 for help_text in &help_texts {
496 commands
497 .entity(help_text)
498 .insert(create_help_text(&app_status));
499 }
500}
More examples
212fn spawn_camera(commands: &mut Commands) {
213 commands
214 .spawn(Camera3d::default())
215 .insert(Transform::from_xyz(0.0, 2.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y))
216 // Tag the camera with `Selection::Camera`.
217 .insert(Selection::Camera);
218}
219
220/// Spawns the actual clustered decals.
221fn spawn_decals(commands: &mut Commands, asset_server: &AssetServer) {
222 let image = asset_server.load("branding/icon.png");
223
224 commands.spawn((
225 ClusteredDecal {
226 image: image.clone(),
227 // Tint with red.
228 tag: 1,
229 },
230 calculate_initial_decal_transform(vec3(1.0, 3.0, 5.0), Vec3::ZERO, Vec2::splat(1.1)),
231 Selection::DecalA,
232 ));
233
234 commands.spawn((
235 ClusteredDecal {
236 image: image.clone(),
237 // Tint with blue.
238 tag: 2,
239 },
240 calculate_initial_decal_transform(vec3(-2.0, -1.0, 4.0), Vec3::ZERO, Vec2::splat(2.0)),
241 Selection::DecalB,
242 ));
243}
244
245/// Spawns the buttons at the bottom of the screen.
246fn spawn_buttons(commands: &mut Commands) {
247 // Spawn the radio buttons that allow the user to select an object to
248 // control.
249 commands
250 .spawn(widgets::main_ui_node())
251 .with_children(|parent| {
252 widgets::spawn_option_buttons(
253 parent,
254 "Drag to Move",
255 &[
256 (Selection::Camera, "Camera"),
257 (Selection::DecalA, "Decal A"),
258 (Selection::DecalB, "Decal B"),
259 ],
260 );
261 });
262
263 // Spawn the drag buttons that allow the user to control the scale and roll
264 // of the selected object.
265 commands
266 .spawn(Node {
267 flex_direction: FlexDirection::Row,
268 position_type: PositionType::Absolute,
269 right: Val::Px(10.0),
270 bottom: Val::Px(10.0),
271 column_gap: Val::Px(6.0),
272 ..default()
273 })
274 .with_children(|parent| {
275 spawn_drag_button(parent, "Scale").insert(DragMode::Scale);
276 spawn_drag_button(parent, "Roll").insert(DragMode::Roll);
277 });
278}
279
280/// Spawns a button that the user can drag to change a parameter.
281fn spawn_drag_button<'a>(
282 commands: &'a mut ChildSpawnerCommands,
283 label: &str,
284) -> EntityCommands<'a> {
285 let mut kid = commands.spawn(Node {
286 border: BUTTON_BORDER,
287 justify_content: JustifyContent::Center,
288 align_items: AlignItems::Center,
289 padding: BUTTON_PADDING,
290 ..default()
291 });
292 kid.insert((
293 Button,
294 BackgroundColor(Color::BLACK),
295 BorderRadius::all(BUTTON_BORDER_RADIUS_SIZE),
296 BUTTON_BORDER_COLOR,
297 ))
298 .with_children(|parent| {
299 widgets::spawn_ui_text(parent, label, Color::WHITE);
300 });
301 kid
302}
303
304/// Spawns the help text at the top of the screen.
305fn spawn_help_text(commands: &mut Commands, app_status: &AppStatus) {
306 commands.spawn((
307 Text::new(create_help_string(app_status)),
308 Node {
309 position_type: PositionType::Absolute,
310 top: Val::Px(12.0),
311 left: Val::Px(12.0),
312 ..default()
313 },
314 HelpText,
315 ));
316}
317
318/// Draws the outlines that show the bounds of the clustered decals.
319fn draw_gizmos(
320 mut gizmos: Gizmos,
321 decals: Query<(&GlobalTransform, &Selection), With<ClusteredDecal>>,
322) {
323 for (global_transform, selection) in &decals {
324 let color = match *selection {
325 Selection::Camera => continue,
326 Selection::DecalA => ORANGE_RED,
327 Selection::DecalB => LIME,
328 };
329
330 gizmos.primitive_3d(
331 &Cuboid {
332 // Since the clustered decal is a 1×1×1 cube in model space, its
333 // half-size is half of the scaling part of its transform.
334 half_size: global_transform.scale() * 0.5,
335 },
336 Isometry3d {
337 rotation: global_transform.rotation(),
338 translation: global_transform.translation_vec3a(),
339 },
340 color,
341 );
342 }
343}
344
345/// Calculates the initial transform of the clustered decal.
346fn calculate_initial_decal_transform(start: Vec3, looking_at: Vec3, size: Vec2) -> Transform {
347 let direction = looking_at - start;
348 let center = start + direction * 0.5;
349 Transform::from_translation(center)
350 .with_scale((size * 0.5).extend(direction.length()))
351 .looking_to(direction, Vec3::Y)
352}
353
354/// Rotates the cube a bit every frame.
355fn rotate_cube(mut meshes: Query<&mut Transform, With<Mesh3d>>) {
356 for mut transform in &mut meshes {
357 transform.rotate_y(CUBE_ROTATION_SPEED);
358 }
359}
360
361/// Updates the state of the radio buttons when the user clicks on one.
362fn update_radio_buttons(
363 mut widgets: Query<(
364 Entity,
365 Option<&mut BackgroundColor>,
366 Has<Text>,
367 &WidgetClickSender<Selection>,
368 )>,
369 app_status: Res<AppStatus>,
370 mut writer: TextUiWriter,
371) {
372 for (entity, maybe_bg_color, has_text, sender) in &mut widgets {
373 let selected = app_status.selection == **sender;
374 if let Some(mut bg_color) = maybe_bg_color {
375 widgets::update_ui_radio_button(&mut bg_color, selected);
376 }
377 if has_text {
378 widgets::update_ui_radio_button_text(entity, &mut writer, selected);
379 }
380 }
381}
382
383/// Changes the selection when the user clicks a radio button.
384fn handle_selection_change(
385 mut events: EventReader<WidgetClickEvent<Selection>>,
386 mut app_status: ResMut<AppStatus>,
387) {
388 for event in events.read() {
389 app_status.selection = **event;
390 }
391}
392
393/// Process a drag event that moves the selected object.
394fn process_move_input(
395 mut selections: Query<(&mut Transform, &Selection)>,
396 mouse_buttons: Res<ButtonInput<MouseButton>>,
397 mouse_motion: Res<AccumulatedMouseMotion>,
398 app_status: Res<AppStatus>,
399) {
400 // Only process drags when movement is selected.
401 if !mouse_buttons.pressed(MouseButton::Left) || app_status.drag_mode != DragMode::Move {
402 return;
403 }
404
405 for (mut transform, selection) in &mut selections {
406 if app_status.selection != *selection {
407 continue;
408 }
409
410 let position = transform.translation;
411
412 // Convert to spherical coordinates.
413 let radius = position.length();
414 let mut theta = acos(position.y / radius);
415 let mut phi = position.z.signum() * acos(position.x * position.xz().length_recip());
416
417 // Camera movement is the inverse of object movement.
418 let (phi_factor, theta_factor) = match *selection {
419 Selection::Camera => (1.0, -1.0),
420 Selection::DecalA | Selection::DecalB => (-1.0, 1.0),
421 };
422
423 // Adjust the spherical coordinates. Clamp the inclination to (0, π).
424 phi += phi_factor * mouse_motion.delta.x * MOVE_SPEED;
425 theta = f32::clamp(
426 theta + theta_factor * mouse_motion.delta.y * MOVE_SPEED,
427 0.001,
428 PI - 0.001,
429 );
430
431 // Convert spherical coordinates back to Cartesian coordinates.
432 transform.translation =
433 radius * vec3(sin(theta) * cos(phi), cos(theta), sin(theta) * sin(phi));
434
435 // Look at the center, but preserve the previous roll angle.
436 let roll = transform.rotation.to_euler(EulerRot::YXZ).2;
437 transform.look_at(Vec3::ZERO, Vec3::Y);
438 let (yaw, pitch, _) = transform.rotation.to_euler(EulerRot::YXZ);
439 transform.rotation = Quat::from_euler(EulerRot::YXZ, yaw, pitch, roll);
440 }
441}
442
443/// Processes a drag event that scales the selected target.
444fn process_scale_input(
445 mut selections: Query<(&mut Transform, &Selection)>,
446 mouse_buttons: Res<ButtonInput<MouseButton>>,
447 mouse_motion: Res<AccumulatedMouseMotion>,
448 app_status: Res<AppStatus>,
449) {
450 // Only process drags when the scaling operation is selected.
451 if !mouse_buttons.pressed(MouseButton::Left) || app_status.drag_mode != DragMode::Scale {
452 return;
453 }
454
455 for (mut transform, selection) in &mut selections {
456 if app_status.selection == *selection {
457 transform.scale *= 1.0 + mouse_motion.delta.x * SCALE_SPEED;
458 }
459 }
460}
461
462/// Processes a drag event that rotates the selected target along its local Z
463/// axis.
464fn process_roll_input(
465 mut selections: Query<(&mut Transform, &Selection)>,
466 mouse_buttons: Res<ButtonInput<MouseButton>>,
467 mouse_motion: Res<AccumulatedMouseMotion>,
468 app_status: Res<AppStatus>,
469) {
470 // Only process drags when the rolling operation is selected.
471 if !mouse_buttons.pressed(MouseButton::Left) || app_status.drag_mode != DragMode::Roll {
472 return;
473 }
474
475 for (mut transform, selection) in &mut selections {
476 if app_status.selection != *selection {
477 continue;
478 }
479
480 let (yaw, pitch, mut roll) = transform.rotation.to_euler(EulerRot::YXZ);
481 roll += mouse_motion.delta.x * ROLL_SPEED;
482 transform.rotation = Quat::from_euler(EulerRot::YXZ, yaw, pitch, roll);
483 }
484}
485
486/// Creates the help string at the top left of the screen.
487fn create_help_string(app_status: &AppStatus) -> String {
488 format!(
489 "Click and drag to {} {}",
490 app_status.drag_mode, app_status.selection
491 )
492}
493
494/// Changes the drag mode when the user hovers over the "Scale" and "Roll"
495/// buttons in the lower right.
496///
497/// If the user is hovering over no such button, this system changes the drag
498/// mode back to its default value of [`DragMode::Move`].
499fn switch_drag_mode(
500 mut commands: Commands,
501 mut interactions: Query<(&Interaction, &DragMode)>,
502 mut windows: Query<Entity, With<Window>>,
503 mouse_buttons: Res<ButtonInput<MouseButton>>,
504 mut app_status: ResMut<AppStatus>,
505) {
506 if mouse_buttons.pressed(MouseButton::Left) {
507 return;
508 }
509
510 for (interaction, drag_mode) in &mut interactions {
511 if *interaction != Interaction::Hovered {
512 continue;
513 }
514
515 app_status.drag_mode = *drag_mode;
516
517 // Set the cursor to provide the user with a nice visual hint.
518 for window in &mut windows {
519 commands
520 .entity(window)
521 .insert(CursorIcon::from(SystemCursorIcon::EwResize));
522 }
523 return;
524 }
525
526 app_status.drag_mode = DragMode::Move;
527
528 for window in &mut windows {
529 commands.entity(window).remove::<CursorIcon>();
530 }
531}
269fn spawn_sphere(commands: &mut Commands, assets: &ExampleAssets) {
270 commands
271 .spawn((
272 Mesh3d(assets.main_sphere.clone()),
273 MeshMaterial3d(assets.main_sphere_material.clone()),
274 Transform::from_xyz(0.0, SPHERE_SCALE, 0.0).with_scale(Vec3::splat(SPHERE_SCALE)),
275 ))
276 .insert(MainObject);
277}
278
279fn spawn_voxel_cube_parent(commands: &mut Commands) {
280 commands.spawn((Visibility::Hidden, Transform::default(), VoxelCubeParent));
281}
282
283fn spawn_fox(commands: &mut Commands, assets: &ExampleAssets) {
284 commands.spawn((
285 SceneRoot(assets.fox.clone()),
286 Visibility::Hidden,
287 Transform::from_scale(Vec3::splat(FOX_SCALE)),
288 MainObject,
289 ));
290}
291
292fn spawn_text(commands: &mut Commands, app_status: &AppStatus) {
293 commands.spawn((
294 app_status.create_text(),
295 Node {
296 position_type: PositionType::Absolute,
297 bottom: Val::Px(12.0),
298 left: Val::Px(12.0),
299 ..default()
300 },
301 ));
302}
303
304// A system that updates the help text.
305fn update_text(mut text_query: Query<&mut Text>, app_status: Res<AppStatus>) {
306 for mut text in text_query.iter_mut() {
307 *text = app_status.create_text();
308 }
309}
310
311impl AppStatus {
312 // Constructs the help text at the bottom of the screen based on the
313 // application status.
314 fn create_text(&self) -> Text {
315 let irradiance_volume_help_text = if self.irradiance_volume_present {
316 DISABLE_IRRADIANCE_VOLUME_HELP_TEXT
317 } else {
318 ENABLE_IRRADIANCE_VOLUME_HELP_TEXT
319 };
320
321 let voxels_help_text = if self.voxels_visible {
322 HIDE_VOXELS_HELP_TEXT
323 } else {
324 SHOW_VOXELS_HELP_TEXT
325 };
326
327 let rotation_help_text = if self.rotating {
328 STOP_ROTATION_HELP_TEXT
329 } else {
330 START_ROTATION_HELP_TEXT
331 };
332
333 let switch_mesh_help_text = match self.model {
334 ExampleModel::Sphere => SWITCH_TO_FOX_HELP_TEXT,
335 ExampleModel::Fox => SWITCH_TO_SPHERE_HELP_TEXT,
336 };
337
338 format!(
339 "{CLICK_TO_MOVE_HELP_TEXT}\n\
340 {voxels_help_text}\n\
341 {irradiance_volume_help_text}\n\
342 {rotation_help_text}\n\
343 {switch_mesh_help_text}"
344 )
345 .into()
346 }
347}
348
349// Rotates the camera a bit every frame.
350fn rotate_camera(
351 mut camera_query: Query<&mut Transform, With<Camera3d>>,
352 time: Res<Time>,
353 app_status: Res<AppStatus>,
354) {
355 if !app_status.rotating {
356 return;
357 }
358
359 for mut transform in camera_query.iter_mut() {
360 transform.translation = Vec2::from_angle(ROTATION_SPEED * time.delta_secs())
361 .rotate(transform.translation.xz())
362 .extend(transform.translation.y)
363 .xzy();
364 transform.look_at(Vec3::ZERO, Vec3::Y);
365 }
366}
367
368// Toggles between the unskinned sphere model and the skinned fox model if the
369// user requests it.
370fn change_main_object(
371 keyboard: Res<ButtonInput<KeyCode>>,
372 mut app_status: ResMut<AppStatus>,
373 mut sphere_query: Query<&mut Visibility, (With<MainObject>, With<Mesh3d>, Without<SceneRoot>)>,
374 mut fox_query: Query<&mut Visibility, (With<MainObject>, With<SceneRoot>)>,
375) {
376 if !keyboard.just_pressed(KeyCode::Tab) {
377 return;
378 }
379 let Some(mut sphere_visibility) = sphere_query.iter_mut().next() else {
380 return;
381 };
382 let Some(mut fox_visibility) = fox_query.iter_mut().next() else {
383 return;
384 };
385
386 match app_status.model {
387 ExampleModel::Sphere => {
388 *sphere_visibility = Visibility::Hidden;
389 *fox_visibility = Visibility::Visible;
390 app_status.model = ExampleModel::Fox;
391 }
392 ExampleModel::Fox => {
393 *sphere_visibility = Visibility::Visible;
394 *fox_visibility = Visibility::Hidden;
395 app_status.model = ExampleModel::Sphere;
396 }
397 }
398}
399
400impl Default for AppStatus {
401 fn default() -> Self {
402 Self {
403 irradiance_volume_present: true,
404 rotating: true,
405 model: ExampleModel::Sphere,
406 voxels_visible: false,
407 }
408 }
409}
410
411// Turns on and off the irradiance volume as requested by the user.
412fn toggle_irradiance_volumes(
413 mut commands: Commands,
414 keyboard: Res<ButtonInput<KeyCode>>,
415 light_probe_query: Query<Entity, With<LightProbe>>,
416 mut app_status: ResMut<AppStatus>,
417 assets: Res<ExampleAssets>,
418 mut ambient_light: ResMut<AmbientLight>,
419) {
420 if !keyboard.just_pressed(KeyCode::Space) {
421 return;
422 };
423
424 let Some(light_probe) = light_probe_query.iter().next() else {
425 return;
426 };
427
428 if app_status.irradiance_volume_present {
429 commands.entity(light_probe).remove::<IrradianceVolume>();
430 ambient_light.brightness = AMBIENT_LIGHT_BRIGHTNESS * IRRADIANCE_VOLUME_INTENSITY;
431 app_status.irradiance_volume_present = false;
432 } else {
433 commands.entity(light_probe).insert(IrradianceVolume {
434 voxels: assets.irradiance_volume.clone(),
435 intensity: IRRADIANCE_VOLUME_INTENSITY,
436 ..default()
437 });
438 ambient_light.brightness = 0.0;
439 app_status.irradiance_volume_present = true;
440 }
441}
442
443fn toggle_rotation(keyboard: Res<ButtonInput<KeyCode>>, mut app_status: ResMut<AppStatus>) {
444 if keyboard.just_pressed(KeyCode::Enter) {
445 app_status.rotating = !app_status.rotating;
446 }
447}
448
449// Handles clicks on the plane that reposition the object.
450fn handle_mouse_clicks(
451 buttons: Res<ButtonInput<MouseButton>>,
452 windows: Query<&Window, With<PrimaryWindow>>,
453 cameras: Query<(&Camera, &GlobalTransform)>,
454 mut main_objects: Query<&mut Transform, With<MainObject>>,
455) {
456 if !buttons.pressed(MouseButton::Left) {
457 return;
458 }
459 let Some(mouse_position) = windows.iter().next().and_then(Window::cursor_position) else {
460 return;
461 };
462 let Some((camera, camera_transform)) = cameras.iter().next() else {
463 return;
464 };
465
466 // Figure out where the user clicked on the plane.
467 let Ok(ray) = camera.viewport_to_world(camera_transform, mouse_position) else {
468 return;
469 };
470 let Some(ray_distance) = ray.intersect_plane(Vec3::ZERO, InfinitePlane3d::new(Vec3::Y)) else {
471 return;
472 };
473 let plane_intersection = ray.origin + ray.direction.normalize() * ray_distance;
474
475 // Move all the main objects.
476 for mut transform in main_objects.iter_mut() {
477 transform.translation = vec3(
478 plane_intersection.x,
479 transform.translation.y,
480 plane_intersection.z,
481 );
482 }
483}
484
485impl FromWorld for ExampleAssets {
486 fn from_world(world: &mut World) -> Self {
487 let fox_animation =
488 world.load_asset(GltfAssetLabel::Animation(1).from_asset("models/animated/Fox.glb"));
489 let (fox_animation_graph, fox_animation_node) =
490 AnimationGraph::from_clip(fox_animation.clone());
491
492 ExampleAssets {
493 main_sphere: world.add_asset(Sphere::default().mesh().uv(32, 18)),
494 fox: world.load_asset(GltfAssetLabel::Scene(0).from_asset("models/animated/Fox.glb")),
495 main_sphere_material: world.add_asset(Color::from(SILVER)),
496 main_scene: world.load_asset(
497 GltfAssetLabel::Scene(0)
498 .from_asset("models/IrradianceVolumeExample/IrradianceVolumeExample.glb"),
499 ),
500 irradiance_volume: world.load_asset("irradiance_volumes/Example.vxgi.ktx2"),
501 fox_animation_graph: world.add_asset(fox_animation_graph),
502 fox_animation_node,
503 voxel_cube: world.add_asset(Cuboid::default()),
504 // Just use a specular map for the skybox since it's not too blurry.
505 // In reality you wouldn't do this--you'd use a real skybox texture--but
506 // reusing the textures like this saves space in the Bevy repository.
507 skybox: world.load_asset("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
508 }
509 }
510}
511
512// Plays the animation on the fox.
513fn play_animations(
514 mut commands: Commands,
515 assets: Res<ExampleAssets>,
516 mut players: Query<(Entity, &mut AnimationPlayer), Without<AnimationGraphHandle>>,
517) {
518 for (entity, mut player) in players.iter_mut() {
519 commands
520 .entity(entity)
521 .insert(AnimationGraphHandle(assets.fox_animation_graph.clone()));
522 player.play(assets.fox_animation_node).repeat();
523 }
524}
525
526fn create_cubes(
527 image_assets: Res<Assets<Image>>,
528 mut commands: Commands,
529 irradiance_volumes: Query<(&IrradianceVolume, &GlobalTransform)>,
530 voxel_cube_parents: Query<Entity, With<VoxelCubeParent>>,
531 voxel_cubes: Query<Entity, With<VoxelCube>>,
532 example_assets: Res<ExampleAssets>,
533 mut voxel_visualization_material_assets: ResMut<Assets<VoxelVisualizationMaterial>>,
534) {
535 // If voxel cubes have already been spawned, don't do anything.
536 if !voxel_cubes.is_empty() {
537 return;
538 }
539
540 let Some(voxel_cube_parent) = voxel_cube_parents.iter().next() else {
541 return;
542 };
543
544 for (irradiance_volume, global_transform) in irradiance_volumes.iter() {
545 let Some(image) = image_assets.get(&irradiance_volume.voxels) else {
546 continue;
547 };
548
549 let resolution = image.texture_descriptor.size;
550
551 let voxel_cube_material = voxel_visualization_material_assets.add(ExtendedMaterial {
552 base: StandardMaterial::from(Color::from(RED)),
553 extension: VoxelVisualizationExtension {
554 irradiance_volume_info: VoxelVisualizationIrradianceVolumeInfo {
555 world_from_voxel: VOXEL_FROM_WORLD.inverse(),
556 voxel_from_world: VOXEL_FROM_WORLD,
557 resolution: uvec3(
558 resolution.width,
559 resolution.height,
560 resolution.depth_or_array_layers,
561 ),
562 intensity: IRRADIANCE_VOLUME_INTENSITY,
563 },
564 },
565 });
566
567 let scale = vec3(
568 1.0 / resolution.width as f32,
569 1.0 / resolution.height as f32,
570 1.0 / resolution.depth_or_array_layers as f32,
571 );
572
573 // Spawn a cube for each voxel.
574 for z in 0..resolution.depth_or_array_layers {
575 for y in 0..resolution.height {
576 for x in 0..resolution.width {
577 let uvw = (uvec3(x, y, z).as_vec3() + 0.5) * scale - 0.5;
578 let pos = global_transform.transform_point(uvw);
579 let voxel_cube = commands
580 .spawn((
581 Mesh3d(example_assets.voxel_cube.clone()),
582 MeshMaterial3d(voxel_cube_material.clone()),
583 Transform::from_scale(Vec3::splat(VOXEL_CUBE_SCALE))
584 .with_translation(pos),
585 ))
586 .insert(VoxelCube)
587 .insert(NotShadowCaster)
588 .id();
589
590 commands.entity(voxel_cube_parent).add_child(voxel_cube);
591 }
592 }
593 }
594 }
595}
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}
169fn add_environment_map_to_camera(
170 mut commands: Commands,
171 query: Query<Entity, Added<Camera3d>>,
172 cubemaps: Res<Cubemaps>,
173) {
174 for camera_entity in query.iter() {
175 commands
176 .entity(camera_entity)
177 .insert(create_camera_environment_map_light(&cubemaps))
178 .insert(Skybox {
179 image: cubemaps.skybox.clone(),
180 brightness: 5000.0,
181 ..default()
182 });
183 }
184}
185
186// A system that handles switching between different reflection modes.
187fn change_reflection_type(
188 mut commands: Commands,
189 light_probe_query: Query<Entity, With<LightProbe>>,
190 camera_query: Query<Entity, With<Camera3d>>,
191 keyboard: Res<ButtonInput<KeyCode>>,
192 mut app_status: ResMut<AppStatus>,
193 cubemaps: Res<Cubemaps>,
194) {
195 // Only do anything if space was pressed.
196 if !keyboard.just_pressed(KeyCode::Space) {
197 return;
198 }
199
200 // Switch reflection mode.
201 app_status.reflection_mode =
202 ReflectionMode::try_from((app_status.reflection_mode as u32 + 1) % 3).unwrap();
203
204 // Add or remove the light probe.
205 for light_probe in light_probe_query.iter() {
206 commands.entity(light_probe).despawn();
207 }
208 match app_status.reflection_mode {
209 ReflectionMode::None | ReflectionMode::EnvironmentMap => {}
210 ReflectionMode::ReflectionProbe => spawn_reflection_probe(&mut commands, &cubemaps),
211 }
212
213 // Add or remove the environment map from the camera.
214 for camera in camera_query.iter() {
215 match app_status.reflection_mode {
216 ReflectionMode::None => {
217 commands.entity(camera).remove::<EnvironmentMapLight>();
218 }
219 ReflectionMode::EnvironmentMap | ReflectionMode::ReflectionProbe => {
220 commands
221 .entity(camera)
222 .insert(create_camera_environment_map_light(&cubemaps));
223 }
224 }
225 }
226}
32fn screenshot_saving(
33 mut commands: Commands,
34 screenshot_saving: Query<Entity, With<Capturing>>,
35 window: Single<Entity, With<Window>>,
36) {
37 match screenshot_saving.iter().count() {
38 0 => {
39 commands.entity(*window).remove::<CursorIcon>();
40 }
41 x if x > 0 => {
42 commands
43 .entity(*window)
44 .insert(CursorIcon::from(SystemCursorIcon::Progress));
45 }
46 _ => {}
47 }
48}
- examples/3d/lightmaps.rs
- examples/ui/tab_navigation.rs
- examples/3d/ssr.rs
- examples/shader/custom_shader_instancing.rs
- examples/asset/asset_decompression.rs
- examples/ecs/entity_disabling.rs
- examples/window/window_settings.rs
- examples/stress_tests/many_foxes.rs
- examples/animation/morph_targets.rs
- examples/ecs/error_handling.rs
- examples/testbed/3d.rs
- examples/animation/animated_mesh_control.rs
- examples/games/game_menu.rs
- examples/3d/anisotropy.rs
- examples/3d/clearcoat.rs
- examples/3d/rotate_environment_map.rs
- examples/math/sampling_primitives.rs
- examples/3d/order_independent_transparency.rs
- examples/3d/pcss.rs
- examples/3d/shadow_caster_receiver.rs
- examples/3d/depth_of_field.rs
- examples/games/stepping.rs
- examples/3d/color_grading.rs
- examples/window/custom_cursor_image.rs
- examples/3d/edit_material_on_gltf.rs
- examples/audio/soundtrack.rs
- examples/3d/../helpers/widgets.rs
- examples/ui/relative_cursor_position.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/eased_motion.rs
- examples/testbed/2d.rs
- examples/3d/visibility_range.rs
- examples/animation/animated_mesh_events.rs
- examples/3d/volumetric_fog.rs
- examples/animation/animated_ui.rs
- examples/animation/animation_graph.rs
- examples/async_tasks/async_compute.rs
- examples/stress_tests/transform_hierarchy.rs
- examples/animation/animation_masks.rs
- examples/3d/deferred_rendering.rs
- examples/testbed/ui.rs
- examples/3d/anti_aliasing.rs
- examples/3d/ssao.rs
- examples/3d/bloom_3d.rs
- examples/2d/bloom_2d.rs
- examples/stress_tests/many_cubes.rs
- examples/animation/animated_transform.rs
- examples/3d/transmission.rs
- examples/ui/scroll.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 for i in 0..N_CARS {
140 let color = colors[i % colors.len()].clone();
141 commands
142 .spawn((
143 Mesh3d(box_mesh.clone()),
144 MeshMaterial3d(color.clone()),
145 Transform::from_scale(Vec3::splat(0.5)),
146 Moves(i as f32 * 2.0),
147 ))
148 .insert_if(CameraTracked, || i == 0)
149 .with_children(|parent| {
150 parent.spawn((
151 Mesh3d(box_mesh.clone()),
152 MeshMaterial3d(color),
153 Transform::from_xyz(0.0, 0.08, 0.03).with_scale(Vec3::new(1.0, 1.0, 0.5)),
154 ));
155 let mut spawn_wheel = |x: f32, z: f32| {
156 parent.spawn((
157 Mesh3d(cylinder.clone()),
158 MeshMaterial3d(wheel_matl.clone()),
159 Transform::from_xyz(0.14 * x, -0.045, 0.15 * z)
160 .with_scale(Vec3::new(0.15, 0.04, 0.15))
161 .with_rotation(Quat::from_rotation_z(std::f32::consts::FRAC_PI_2)),
162 Rotates,
163 ));
164 };
165 spawn_wheel(1.0, 1.0);
166 spawn_wheel(1.0, -1.0);
167 spawn_wheel(-1.0, 1.0);
168 spawn_wheel(-1.0, -1.0);
169 });
170 }
171}
More examples
92fn update(
93 camera: Single<
94 (
95 Entity,
96 Option<&ScreenSpaceAmbientOcclusion>,
97 Option<&TemporalJitter>,
98 ),
99 With<Camera>,
100 >,
101 mut text: Single<&mut Text>,
102 mut sphere: Single<&mut Transform, With<SphereMarker>>,
103 mut commands: Commands,
104 keycode: Res<ButtonInput<KeyCode>>,
105 time: Res<Time>,
106) {
107 sphere.translation.y = ops::sin(time.elapsed_secs() / 1.7) * 0.7;
108
109 let (camera_entity, ssao, temporal_jitter) = *camera;
110 let current_ssao = ssao.cloned().unwrap_or_default();
111
112 let mut commands = commands.entity(camera_entity);
113 commands
114 .insert_if(
115 ScreenSpaceAmbientOcclusion {
116 quality_level: ScreenSpaceAmbientOcclusionQualityLevel::Low,
117 ..current_ssao
118 },
119 || keycode.just_pressed(KeyCode::Digit2),
120 )
121 .insert_if(
122 ScreenSpaceAmbientOcclusion {
123 quality_level: ScreenSpaceAmbientOcclusionQualityLevel::Medium,
124 ..current_ssao
125 },
126 || keycode.just_pressed(KeyCode::Digit3),
127 )
128 .insert_if(
129 ScreenSpaceAmbientOcclusion {
130 quality_level: ScreenSpaceAmbientOcclusionQualityLevel::High,
131 ..current_ssao
132 },
133 || keycode.just_pressed(KeyCode::Digit4),
134 )
135 .insert_if(
136 ScreenSpaceAmbientOcclusion {
137 quality_level: ScreenSpaceAmbientOcclusionQualityLevel::Ultra,
138 ..current_ssao
139 },
140 || keycode.just_pressed(KeyCode::Digit5),
141 )
142 .insert_if(
143 ScreenSpaceAmbientOcclusion {
144 constant_object_thickness: (current_ssao.constant_object_thickness * 2.0).min(4.0),
145 ..current_ssao
146 },
147 || keycode.just_pressed(KeyCode::ArrowUp),
148 )
149 .insert_if(
150 ScreenSpaceAmbientOcclusion {
151 constant_object_thickness: (current_ssao.constant_object_thickness * 0.5)
152 .max(0.0625),
153 ..current_ssao
154 },
155 || keycode.just_pressed(KeyCode::ArrowDown),
156 );
157 if keycode.just_pressed(KeyCode::Digit1) {
158 commands.remove::<ScreenSpaceAmbientOcclusion>();
159 }
160 if keycode.just_pressed(KeyCode::Space) {
161 if temporal_jitter.is_some() {
162 commands.remove::<TemporalJitter>();
163 } else {
164 commands.insert(TemporalJitter::default());
165 }
166 }
167
168 text.clear();
169
170 let (o, l, m, h, u) = match ssao.map(|s| s.quality_level) {
171 None => ("*", "", "", "", ""),
172 Some(ScreenSpaceAmbientOcclusionQualityLevel::Low) => ("", "*", "", "", ""),
173 Some(ScreenSpaceAmbientOcclusionQualityLevel::Medium) => ("", "", "*", "", ""),
174 Some(ScreenSpaceAmbientOcclusionQualityLevel::High) => ("", "", "", "*", ""),
175 Some(ScreenSpaceAmbientOcclusionQualityLevel::Ultra) => ("", "", "", "", "*"),
176 _ => unreachable!(),
177 };
178
179 if let Some(thickness) = ssao.map(|s| s.constant_object_thickness) {
180 text.push_str(&format!(
181 "Constant object thickness: {} (Up/Down)\n\n",
182 thickness
183 ));
184 }
185
186 text.push_str("SSAO Quality:\n");
187 text.push_str(&format!("(1) {o}Off{o}\n"));
188 text.push_str(&format!("(2) {l}Low{l}\n"));
189 text.push_str(&format!("(3) {m}Medium{m}\n"));
190 text.push_str(&format!("(4) {h}High{h}\n"));
191 text.push_str(&format!("(5) {u}Ultra{u}\n\n"));
192
193 text.push_str("Temporal Antialiasing:\n");
194 text.push_str(match temporal_jitter {
195 Some(_) => "(Space) Enabled",
196 None => "(Space) Disabled",
197 });
198}
137fn setup(
138 mut commands: Commands,
139 args: Res<Args>,
140 mesh_assets: ResMut<Assets<Mesh>>,
141 material_assets: ResMut<Assets<StandardMaterial>>,
142 images: ResMut<Assets<Image>>,
143) {
144 warn!(include_str!("warning_string.txt"));
145
146 let args = args.into_inner();
147 let images = images.into_inner();
148 let material_assets = material_assets.into_inner();
149 let mesh_assets = mesh_assets.into_inner();
150
151 let meshes = init_meshes(args, mesh_assets);
152
153 let material_textures = init_textures(args, images);
154 let materials = init_materials(args, &material_textures, material_assets);
155
156 // We're seeding the PRNG here to make this example deterministic for testing purposes.
157 // This isn't strictly required in practical use unless you need your app to be deterministic.
158 let mut material_rng = ChaCha8Rng::seed_from_u64(42);
159 match args.layout {
160 Layout::Sphere => {
161 // NOTE: This pattern is good for testing performance of culling as it provides roughly
162 // the same number of visible meshes regardless of the viewing angle.
163 const N_POINTS: usize = WIDTH * HEIGHT * 4;
164 // NOTE: f64 is used to avoid precision issues that produce visual artifacts in the distribution
165 let radius = WIDTH as f64 * 2.5;
166 let golden_ratio = 0.5f64 * (1.0f64 + 5.0f64.sqrt());
167 for i in 0..N_POINTS {
168 let spherical_polar_theta_phi =
169 fibonacci_spiral_on_sphere(golden_ratio, i, N_POINTS);
170 let unit_sphere_p = spherical_polar_to_cartesian(spherical_polar_theta_phi);
171 let (mesh, transform) = meshes.choose(&mut material_rng).unwrap();
172 commands
173 .spawn((
174 Mesh3d(mesh.clone()),
175 MeshMaterial3d(materials.choose(&mut material_rng).unwrap().clone()),
176 Transform::from_translation((radius * unit_sphere_p).as_vec3())
177 .looking_at(Vec3::ZERO, Vec3::Y)
178 .mul_transform(*transform),
179 ))
180 .insert_if(NoFrustumCulling, || args.no_frustum_culling)
181 .insert_if(NoAutomaticBatching, || args.no_automatic_batching);
182 }
183
184 // camera
185 let mut camera = commands.spawn(Camera3d::default());
186 if args.no_indirect_drawing {
187 camera.insert(NoIndirectDrawing);
188 }
189 if args.no_cpu_culling {
190 camera.insert(NoCpuCulling);
191 }
192
193 // Inside-out box around the meshes onto which shadows are cast (though you cannot see them...)
194 commands.spawn((
195 Mesh3d(mesh_assets.add(Cuboid::from_size(Vec3::splat(radius as f32 * 2.2)))),
196 MeshMaterial3d(material_assets.add(StandardMaterial::from(Color::WHITE))),
197 Transform::from_scale(-Vec3::ONE),
198 NotShadowCaster,
199 ));
200 }
201 _ => {
202 // NOTE: This pattern is good for demonstrating that frustum culling is working correctly
203 // as the number of visible meshes rises and falls depending on the viewing angle.
204 let scale = 2.5;
205 for x in 0..WIDTH {
206 for y in 0..HEIGHT {
207 // introduce spaces to break any kind of moiré pattern
208 if x % 10 == 0 || y % 10 == 0 {
209 continue;
210 }
211 // cube
212 commands.spawn((
213 Mesh3d(meshes.choose(&mut material_rng).unwrap().0.clone()),
214 MeshMaterial3d(materials.choose(&mut material_rng).unwrap().clone()),
215 Transform::from_xyz((x as f32) * scale, (y as f32) * scale, 0.0),
216 ));
217 commands.spawn((
218 Mesh3d(meshes.choose(&mut material_rng).unwrap().0.clone()),
219 MeshMaterial3d(materials.choose(&mut material_rng).unwrap().clone()),
220 Transform::from_xyz(
221 (x as f32) * scale,
222 HEIGHT as f32 * scale,
223 (y as f32) * scale,
224 ),
225 ));
226 commands.spawn((
227 Mesh3d(meshes.choose(&mut material_rng).unwrap().0.clone()),
228 MeshMaterial3d(materials.choose(&mut material_rng).unwrap().clone()),
229 Transform::from_xyz((x as f32) * scale, 0.0, (y as f32) * scale),
230 ));
231 commands.spawn((
232 Mesh3d(meshes.choose(&mut material_rng).unwrap().0.clone()),
233 MeshMaterial3d(materials.choose(&mut material_rng).unwrap().clone()),
234 Transform::from_xyz(0.0, (x as f32) * scale, (y as f32) * scale),
235 ));
236 }
237 }
238 // camera
239 let center = 0.5 * scale * Vec3::new(WIDTH as f32, HEIGHT as f32, WIDTH as f32);
240 commands.spawn((Camera3d::default(), Transform::from_translation(center)));
241 // Inside-out box around the meshes onto which shadows are cast (though you cannot see them...)
242 commands.spawn((
243 Mesh3d(mesh_assets.add(Cuboid::from_size(2.0 * 1.1 * center))),
244 MeshMaterial3d(material_assets.add(StandardMaterial::from(Color::WHITE))),
245 Transform::from_scale(-Vec3::ONE).with_translation(center),
246 NotShadowCaster,
247 ));
248 }
249 }
250
251 commands.spawn((
252 DirectionalLight {
253 shadows_enabled: args.shadows,
254 ..default()
255 },
256 Transform::IDENTITY.looking_at(Vec3::new(0.0, -1.0, -1.0), Vec3::Y),
257 ));
258}
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 if let Some(entity) = query.iter().next() {
46 commands.entity(entity).remove::<MyComponent>();
47 }
48 }
49}
135fn trigger_hooks(
136 mut commands: Commands,
137 keys: Res<ButtonInput<KeyCode>>,
138 index: Res<MyComponentIndex>,
139) {
140 for (key, entity) in index.iter() {
141 if !keys.pressed(*key) {
142 commands.entity(*entity).remove::<MyComponent>();
143 }
144 }
145 for key in keys.get_just_pressed() {
146 commands.spawn(MyComponent(*key));
147 }
148}
32fn screenshot_saving(
33 mut commands: Commands,
34 screenshot_saving: Query<Entity, With<Capturing>>,
35 window: Single<Entity, With<Window>>,
36) {
37 match screenshot_saving.iter().count() {
38 0 => {
39 commands.entity(*window).remove::<CursorIcon>();
40 }
41 x if x > 0 => {
42 commands
43 .entity(*window)
44 .insert(CursorIcon::from(SystemCursorIcon::Progress));
45 }
46 _ => {}
47 }
48}
111fn fade_in(
112 mut commands: Commands,
113 mut audio_sink: Query<(&mut AudioSink, Entity), With<FadeIn>>,
114 time: Res<Time>,
115) {
116 for (mut audio, entity) in audio_sink.iter_mut() {
117 let current_volume = audio.volume();
118 audio.set_volume(current_volume + Volume::Linear(time.delta_secs() / FADE_TIME));
119 if audio.volume().to_linear() >= 1.0 {
120 audio.set_volume(Volume::Linear(1.0));
121 commands.entity(entity).remove::<FadeIn>();
122 }
123 }
124}
- examples/3d/depth_of_field.rs
- examples/ecs/entity_disabling.rs
- examples/ui/tab_navigation.rs
- examples/ecs/relationships.rs
- examples/asset/asset_decompression.rs
- examples/3d/visibility_range.rs
- examples/games/game_menu.rs
- examples/3d/clearcoat.rs
- examples/3d/clustered_decals.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
- examples/3d/reflection_probes.rs
- examples/3d/volumetric_fog.rs
- examples/3d/ssr.rs
- examples/animation/animation_masks.rs
- examples/3d/anisotropy.rs
- examples/3d/deferred_rendering.rs
- examples/3d/anti_aliasing.rs
- examples/3d/mixed_lighting.rs
- examples/3d/ssao.rs
- examples/3d/bloom_3d.rs
- examples/2d/bloom_2d.rs
- examples/3d/transmission.rs
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/games/game_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/reflection_probes.rs
- examples/games/alien_cake_addict.rs
- examples/games/breakout.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
Sourcepub fn despawn_recursive(&mut self)
👎Deprecated since 0.16.0: Use entity.despawn(), which now automatically despawns recursively.
pub fn despawn_recursive(&mut self)
Despawns the provided entity and its descendants.
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 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 trigger(&mut self, event: impl Event) -> &mut EntityCommands<'a>
pub fn trigger(&mut self, event: impl Event) -> &mut EntityCommands<'a>
Sourcepub fn observe<E, B, M>(
&mut self,
observer: impl IntoObserverSystem<E, B, M>,
) -> &mut EntityCommands<'a>
pub fn observe<E, B, M>( &mut self, observer: impl IntoObserverSystem<E, B, M>, ) -> &mut EntityCommands<'a>
Creates an Observer
listening for events of type E
targeting this entity.
Examples found in repository?
18fn screenshot_on_spacebar(
19 mut commands: Commands,
20 input: Res<ButtonInput<KeyCode>>,
21 mut counter: Local<u32>,
22) {
23 if input.just_pressed(KeyCode::Space) {
24 let path = format!("./screenshot-{}.png", *counter);
25 *counter += 1;
26 commands
27 .spawn(Screenshot::primary_window())
28 .observe(save_to_disk(path));
29 }
30}
More examples
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}
165fn spawn_scene(commands: &mut Commands, asset_server: &AssetServer) {
166 commands
167 .spawn(SceneRoot(
168 asset_server.load(
169 GltfAssetLabel::Scene(0)
170 .from_asset("models/MixedLightingExample/MixedLightingExample.gltf"),
171 ),
172 ))
173 .observe(
174 |_: Trigger<SceneInstanceReady>,
175 mut lighting_mode_change_event_writer: EventWriter<LightingModeChanged>| {
176 // When the scene loads, send a `LightingModeChanged` event so
177 // that we set up the lightmaps.
178 lighting_mode_change_event_writer.write(LightingModeChanged);
179 },
180 );
181}
244 pub fn setup(
245 mut commands: Commands,
246 asset_server: Res<AssetServer>,
247 mut graphs: ResMut<Assets<AnimationGraph>>,
248 ) {
249 let (graph, node) = AnimationGraph::from_clip(
250 asset_server.load(GltfAssetLabel::Animation(2).from_asset(FOX_PATH)),
251 );
252
253 let graph_handle = graphs.add(graph);
254 commands.insert_resource(Animation {
255 animation: node,
256 graph: graph_handle,
257 });
258
259 commands.spawn((
260 Camera3d::default(),
261 Transform::from_xyz(100.0, 100.0, 150.0).looking_at(Vec3::new(0.0, 20.0, 0.0), Vec3::Y),
262 StateScoped(CURRENT_SCENE),
263 ));
264
265 commands.spawn((
266 Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, 1.0, -PI / 4.)),
267 DirectionalLight {
268 shadows_enabled: true,
269 ..default()
270 },
271 StateScoped(CURRENT_SCENE),
272 ));
273
274 commands
275 .spawn((
276 SceneRoot(asset_server.load(GltfAssetLabel::Scene(0).from_asset(FOX_PATH))),
277 StateScoped(CURRENT_SCENE),
278 ))
279 .observe(pause_animation_frame);
280 }
32fn setup_mesh_and_animation(
33 mut commands: Commands,
34 asset_server: Res<AssetServer>,
35 mut graphs: ResMut<Assets<AnimationGraph>>,
36) {
37 // Create an animation graph containing a single animation. We want the "run"
38 // animation from our example asset, which has an index of two.
39 let (graph, index) = AnimationGraph::from_clip(
40 asset_server.load(GltfAssetLabel::Animation(2).from_asset(GLTF_PATH)),
41 );
42
43 // Store the animation graph as an asset.
44 let graph_handle = graphs.add(graph);
45
46 // Create a component that stores a reference to our animation.
47 let animation_to_play = AnimationToPlay {
48 graph_handle,
49 index,
50 };
51
52 // Start loading the asset as a scene and store a reference to it in a
53 // SceneRoot component. This component will automatically spawn a scene
54 // containing our mesh once it has loaded.
55 let mesh_scene = SceneRoot(asset_server.load(GltfAssetLabel::Scene(0).from_asset(GLTF_PATH)));
56
57 // Spawn an entity with our components, and connect it to an observer that
58 // will trigger when the scene is loaded and spawned.
59 commands
60 .spawn((animation_to_play, mesh_scene))
61 .observe(play_animation_when_ready);
62}
12fn setup_scene(
13 mut commands: Commands,
14 mut meshes: ResMut<Assets<Mesh>>,
15 mut materials: ResMut<Assets<StandardMaterial>>,
16) {
17 commands
18 .spawn((
19 Text::new("Click Me to get a box\nDrag cubes to rotate"),
20 Node {
21 position_type: PositionType::Absolute,
22 top: Val::Percent(12.0),
23 left: Val::Percent(12.0),
24 ..default()
25 },
26 ))
27 .observe(on_click_spawn_cube)
28 .observe(
29 |out: Trigger<Pointer<Out>>, mut texts: Query<&mut TextColor>| {
30 let mut text_color = texts.get_mut(out.target()).unwrap();
31 text_color.0 = Color::WHITE;
32 },
33 )
34 .observe(
35 |over: Trigger<Pointer<Over>>, mut texts: Query<&mut TextColor>| {
36 let mut color = texts.get_mut(over.target()).unwrap();
37 color.0 = bevy::color::palettes::tailwind::CYAN_400.into();
38 },
39 );
40
41 // Base
42 commands.spawn((
43 Mesh3d(meshes.add(Circle::new(4.0))),
44 MeshMaterial3d(materials.add(Color::WHITE)),
45 Transform::from_rotation(Quat::from_rotation_x(-std::f32::consts::FRAC_PI_2)),
46 ));
47
48 // Light
49 commands.spawn((
50 PointLight {
51 shadows_enabled: true,
52 ..default()
53 },
54 Transform::from_xyz(4.0, 8.0, 4.0),
55 ));
56
57 // Camera
58 commands.spawn((
59 Camera3d::default(),
60 Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
61 ));
62}
63
64fn on_click_spawn_cube(
65 _click: Trigger<Pointer<Click>>,
66 mut commands: Commands,
67 mut meshes: ResMut<Assets<Mesh>>,
68 mut materials: ResMut<Assets<StandardMaterial>>,
69 mut num: Local<usize>,
70) {
71 commands
72 .spawn((
73 Mesh3d(meshes.add(Cuboid::new(0.5, 0.5, 0.5))),
74 MeshMaterial3d(materials.add(Color::srgb_u8(124, 144, 255))),
75 Transform::from_xyz(0.0, 0.25 + 0.55 * *num as f32, 0.0),
76 ))
77 // With the MeshPickingPlugin added, you can add pointer event observers to meshes:
78 .observe(on_drag_rotate);
79 *num += 1;
80}
Sourcepub fn clone_with(
&mut self,
target: Entity,
config: impl FnOnce(&mut EntityClonerBuilder<'_>) + Send + Sync + 'static,
) -> &mut EntityCommands<'a>
pub fn clone_with( &mut self, target: Entity, config: impl FnOnce(&mut EntityClonerBuilder<'_>) + Send + Sync + 'static, ) -> &mut EntityCommands<'a>
Clones parts of an entity (components, observers, etc.) onto another entity,
configured through EntityClonerBuilder
.
By default, the other entity will receive all the components of the original that implement
Clone
or Reflect
.
§Panics
The command will panic when applied if the target entity does not exist.
§Example
Configure through EntityClonerBuilder
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 only ComponentA onto the target.
entity.clone_with(target, |builder| {
builder.deny::<ComponentB>();
});
}
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
.
§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 first entity.
let mut entity_clone = entity.clone_and_spawn();
}
Sourcepub fn clone_and_spawn_with(
&mut self,
config: impl FnOnce(&mut EntityClonerBuilder<'_>) + Send + Sync + 'static,
) -> EntityCommands<'_>
pub fn clone_and_spawn_with( &mut self, config: impl FnOnce(&mut EntityClonerBuilder<'_>) + Send + Sync + 'static, ) -> EntityCommands<'_>
Spawns a clone of this entity and allows configuring cloning behavior
using EntityClonerBuilder
, returning the EntityCommands
of the clone.
By default, the clone will receive all the components of the original that implement
Clone
or Reflect
.
To exclude specific components, use EntityClonerBuilder::deny
.
To only include specific components, use EntityClonerBuilder::deny_all
followed by EntityClonerBuilder::allow
.
See the methods on EntityClonerBuilder
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 first entity, but without ComponentB.
let mut entity_clone = entity.clone_and_spawn_with(|builder| {
builder.deny::<ComponentB>();
});
}
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,
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> 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<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<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.