pub struct Commands<'w, 's> { /* private fields */ }
Expand description
A Command
queue to perform structural changes to the World
.
Since each command requires exclusive access to the World
,
all queued commands are automatically applied in sequence
when the apply_deferred
system runs (see apply_deferred
documentation for more details).
Each command can be used to modify the World
in arbitrary ways:
- spawning or despawning entities
- inserting components on new or existing entities
- inserting resources
- etc.
For a version of Commands
that works in parallel contexts (such as
within Query::par_iter
) see
ParallelCommands
§Usage
Add mut commands: Commands
as a function argument to your system to get a copy of this struct that will be applied the next time a copy of apply_deferred
runs.
Commands are almost always used as a SystemParam
.
fn my_system(mut commands: Commands) {
// ...
}
§Implementing
Each built-in command is implemented as a separate method, e.g. Commands::spawn
.
In addition to the pre-defined command methods, you can add commands with any arbitrary
behavior using Commands::add
, which accepts any type implementing Command
.
Since closures and other functions implement this trait automatically, this allows one-shot, anonymous custom commands.
// NOTE: type inference fails here, so annotations are required on the closure.
commands.add(|w: &mut World| {
// Mutate the world however you want...
});
Implementations§
source§impl<'w, 's> Commands<'w, 's>
impl<'w, 's> Commands<'w, 's>
sourcepub fn new(queue: &'s mut CommandQueue, world: &'w World) -> Commands<'w, 's>
pub fn new(queue: &'s mut CommandQueue, world: &'w World) -> Commands<'w, 's>
Returns a new Commands
instance from a CommandQueue
and a World
.
It is not required to call this constructor when using Commands
as a system parameter.
sourcepub fn new_from_entities(
queue: &'s mut CommandQueue,
entities: &'w Entities,
) -> Commands<'w, 's>
pub fn new_from_entities( queue: &'s mut CommandQueue, entities: &'w Entities, ) -> Commands<'w, 's>
Returns a new Commands
instance from a CommandQueue
and an Entities
reference.
It is not required to call this constructor when using Commands
as a system parameter.
sourcepub fn reborrow(&mut self) -> Commands<'w, '_>
pub fn reborrow(&mut self) -> Commands<'w, '_>
Returns a Commands
with a smaller lifetime.
This is useful if you have &mut Commands
but need Commands
.
§Examples
fn my_system(mut commands: Commands) {
// We do our initialization in a separate function,
// which expects an owned `Commands`.
do_initialization(commands.reborrow());
// Since we only reborrowed the commands instead of moving them, we can still use them.
commands.spawn_empty();
}
sourcepub fn append(&mut self, other: &mut CommandQueue)
pub fn append(&mut self, other: &mut CommandQueue)
Take all commands from other
and append them to self
, leaving other
empty
sourcepub fn spawn_empty(&mut self) -> EntityCommands<'_>
pub fn spawn_empty(&mut self) -> EntityCommands<'_>
Pushes a Command
to the queue for creating a new empty Entity
,
and returns its corresponding EntityCommands
.
See World::spawn_empty
for more details.
§Example
#[derive(Component)]
struct Label(&'static str);
#[derive(Component)]
struct Strength(u32);
#[derive(Component)]
struct Agility(u32);
fn example_system(mut commands: Commands) {
// Create a new empty entity and retrieve its id.
let empty_entity = commands.spawn_empty().id();
// Create another empty entity, then add some component to it
commands.spawn_empty()
// adds a new component bundle to the entity
.insert((Strength(1), Agility(2)))
// adds a single component to the entity
.insert(Label("hello world"));
}
§See also
spawn
to spawn an entity with a bundle.spawn_batch
to spawn entities with a bundle each.
Examples found in repository?
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
fn spawn_tasks(mut commands: Commands) {
let thread_pool = AsyncComputeTaskPool::get();
for x in 0..NUM_CUBES {
for y in 0..NUM_CUBES {
for z in 0..NUM_CUBES {
// Spawn new task on the AsyncComputeTaskPool; the task will be
// executed in the background, and the Task future returned by
// spawn() can be used to poll for the result
let entity = commands.spawn_empty().id();
let task = thread_pool.spawn(async move {
let duration = Duration::from_secs_f32(rand::thread_rng().gen_range(0.05..5.0));
// Pretend this is a time-intensive function. :)
async_std::task::sleep(duration).await;
// Such hard work, all done!
let transform = Transform::from_xyz(x as f32, y as f32, z as f32);
let mut command_queue = CommandQueue::default();
// we use a raw command queue to pass a FnOne(&mut World) back to be
// applied in a deferred manner.
command_queue.push(move |world: &mut World| {
let (box_mesh_handle, box_material_handle) = {
let mut system_state = SystemState::<(
Res<BoxMeshHandle>,
Res<BoxMaterialHandle>,
)>::new(world);
let (box_mesh_handle, box_material_handle) =
system_state.get_mut(world);
(box_mesh_handle.clone(), box_material_handle.clone())
};
world
.entity_mut(entity)
// Add our new PbrBundle of components to our tagged entity
.insert(PbrBundle {
mesh: box_mesh_handle,
material: box_material_handle,
transform,
..default()
})
// Task is complete, so remove task component from entity
.remove::<ComputeTransform>();
});
command_queue
});
// Spawn new entity and add our new task as a component
commands.entity(entity).insert(ComputeTransform(task));
}
}
}
}
More examples
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431
fn spawn_tree(
parent_map: &[usize],
commands: &mut Commands,
update_filter: &UpdateFilter,
root_transform: Transform,
) -> InsertResult {
// total count (# of nodes + root)
let count = parent_map.len() + 1;
#[derive(Default, Clone, Copy)]
struct NodeInfo {
child_count: u32,
depth: u32,
}
// node index -> entity lookup list
let mut ents: Vec<Entity> = Vec::with_capacity(count);
let mut node_info: Vec<NodeInfo> = vec![default(); count];
for (i, &parent_idx) in parent_map.iter().enumerate() {
// assert spawn order (parent must be processed before child)
assert!(parent_idx <= i, "invalid spawn order");
node_info[parent_idx].child_count += 1;
}
// insert root
ents.push(commands.spawn(TransformBundle::from(root_transform)).id());
let mut result = InsertResult::default();
let mut rng = rand::thread_rng();
// used to count through the number of children (used only for visual layout)
let mut child_idx: Vec<u16> = vec![0; count];
// insert children
for (current_idx, &parent_idx) in parent_map.iter().enumerate() {
let current_idx = current_idx + 1;
// separation factor to visually separate children (0..1)
let sep = child_idx[parent_idx] as f32 / node_info[parent_idx].child_count as f32;
child_idx[parent_idx] += 1;
// calculate and set depth
// this works because it's guaranteed that we have already iterated over the parent
let depth = node_info[parent_idx].depth + 1;
let info = &mut node_info[current_idx];
info.depth = depth;
// update max depth of tree
result.maximum_depth = result.maximum_depth.max(depth.try_into().unwrap());
// insert child
let child_entity = {
let mut cmd = commands.spawn_empty();
// check whether or not to update this node
let update = (rng.gen::<f32>() <= update_filter.probability)
&& (depth >= update_filter.min_depth && depth <= update_filter.max_depth);
if update {
cmd.insert(UpdateValue(sep));
result.active_nodes += 1;
}
let transform = {
let mut translation = Vec3::ZERO;
// use the same placement fn as the `update` system
// this way the entities won't be all at (0, 0, 0) when they don't have an `Update` component
set_translation(&mut translation, sep);
Transform::from_translation(translation)
};
// only insert the components necessary for the transform propagation
cmd.insert(TransformBundle::from(transform));
cmd.id()
};
commands
.get_or_spawn(ents[parent_idx])
.add_child(child_entity);
ents.push(child_entity);
}
result.inserted_nodes = ents.len();
result
}
sourcepub fn get_or_spawn(&mut self, entity: Entity) -> EntityCommands<'_>
pub fn get_or_spawn(&mut self, entity: Entity) -> EntityCommands<'_>
Pushes a Command
to the queue for creating a new Entity
if the given one does not exists,
and returns its corresponding EntityCommands
.
This method silently fails by returning EntityCommands
even if the given Entity
cannot be spawned.
See World::get_or_spawn
for more details.
§Note
Spawning a specific entity
value is rarely the right choice. Most apps should favor
Commands::spawn
. This method should generally only be used for sharing entities across
apps, and only when they have a scheme worked out to share an ID space (which doesn’t happen
by default).
Examples found in repository?
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431
fn spawn_tree(
parent_map: &[usize],
commands: &mut Commands,
update_filter: &UpdateFilter,
root_transform: Transform,
) -> InsertResult {
// total count (# of nodes + root)
let count = parent_map.len() + 1;
#[derive(Default, Clone, Copy)]
struct NodeInfo {
child_count: u32,
depth: u32,
}
// node index -> entity lookup list
let mut ents: Vec<Entity> = Vec::with_capacity(count);
let mut node_info: Vec<NodeInfo> = vec![default(); count];
for (i, &parent_idx) in parent_map.iter().enumerate() {
// assert spawn order (parent must be processed before child)
assert!(parent_idx <= i, "invalid spawn order");
node_info[parent_idx].child_count += 1;
}
// insert root
ents.push(commands.spawn(TransformBundle::from(root_transform)).id());
let mut result = InsertResult::default();
let mut rng = rand::thread_rng();
// used to count through the number of children (used only for visual layout)
let mut child_idx: Vec<u16> = vec![0; count];
// insert children
for (current_idx, &parent_idx) in parent_map.iter().enumerate() {
let current_idx = current_idx + 1;
// separation factor to visually separate children (0..1)
let sep = child_idx[parent_idx] as f32 / node_info[parent_idx].child_count as f32;
child_idx[parent_idx] += 1;
// calculate and set depth
// this works because it's guaranteed that we have already iterated over the parent
let depth = node_info[parent_idx].depth + 1;
let info = &mut node_info[current_idx];
info.depth = depth;
// update max depth of tree
result.maximum_depth = result.maximum_depth.max(depth.try_into().unwrap());
// insert child
let child_entity = {
let mut cmd = commands.spawn_empty();
// check whether or not to update this node
let update = (rng.gen::<f32>() <= update_filter.probability)
&& (depth >= update_filter.min_depth && depth <= update_filter.max_depth);
if update {
cmd.insert(UpdateValue(sep));
result.active_nodes += 1;
}
let transform = {
let mut translation = Vec3::ZERO;
// use the same placement fn as the `update` system
// this way the entities won't be all at (0, 0, 0) when they don't have an `Update` component
set_translation(&mut translation, sep);
Transform::from_translation(translation)
};
// only insert the components necessary for the transform propagation
cmd.insert(TransformBundle::from(transform));
cmd.id()
};
commands
.get_or_spawn(ents[parent_idx])
.add_child(child_entity);
ents.push(child_entity);
}
result.inserted_nodes = ents.len();
result
}
sourcepub fn spawn<T>(&mut self, bundle: T) -> EntityCommands<'_>where
T: Bundle,
pub fn spawn<T>(&mut self, bundle: T) -> EntityCommands<'_>where
T: Bundle,
Pushes a Command
to the queue for creating a new entity with the given Bundle
’s components,
and returns its corresponding EntityCommands
.
§Example
use bevy_ecs::prelude::*;
#[derive(Component)]
struct Component1;
#[derive(Component)]
struct Component2;
#[derive(Component)]
struct Label(&'static str);
#[derive(Component)]
struct Strength(u32);
#[derive(Component)]
struct Agility(u32);
#[derive(Bundle)]
struct ExampleBundle {
a: Component1,
b: Component2,
}
fn example_system(mut commands: Commands) {
// Create a new entity with a single component.
commands.spawn(Component1);
// Create a new entity with a component bundle.
commands.spawn(ExampleBundle {
a: Component1,
b: Component2,
});
commands
// Create a new entity with two components using a "tuple bundle".
.spawn((Component1, Component2))
// `spawn returns a builder, so you can insert more bundles like this:
.insert((Strength(1), Agility(2)))
// or insert single components like this:
.insert(Label("hello world"));
}
§See also
spawn_empty
to spawn an entity without any components.spawn_batch
to spawn entities with a bundle each.
Examples found in repository?
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
fn setup_camera(mut cmd: Commands) {
cmd.spawn(Camera2dBundle::default());
}
// Spawns the UI
fn setup_ui(mut cmd: Commands) {
// Node that fills entire background
cmd.spawn(NodeBundle {
style: Style {
width: Val::Percent(100.),
..default()
},
..default()
})
.with_children(|root| {
// Text where we display current resolution
root.spawn((
TextBundle::from_section(
"Resolution",
TextStyle {
font_size: 50.0,
..default()
},
),
ResolutionText,
));
});
}
More examples
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777
fn setup(mut commands: Commands) {
commands.spawn(Camera2dBundle::default());
}
mod splash {
use bevy::prelude::*;
use super::{despawn_screen, GameState};
// This plugin will display a splash screen with Bevy logo for 1 second before switching to the menu
pub fn splash_plugin(app: &mut App) {
// As this plugin is managing the splash screen, it will focus on the state `GameState::Splash`
app
// When entering the state, spawn everything needed for this screen
.add_systems(OnEnter(GameState::Splash), splash_setup)
// While in this state, run the `countdown` system
.add_systems(Update, countdown.run_if(in_state(GameState::Splash)))
// When exiting the state, despawn everything that was spawned for this screen
.add_systems(OnExit(GameState::Splash), despawn_screen::<OnSplashScreen>);
}
// Tag component used to tag entities added on the splash screen
#[derive(Component)]
struct OnSplashScreen;
// Newtype to use a `Timer` for this screen as a resource
#[derive(Resource, Deref, DerefMut)]
struct SplashTimer(Timer);
fn splash_setup(mut commands: Commands, asset_server: Res<AssetServer>) {
let icon = asset_server.load("branding/icon.png");
// Display the logo
commands
.spawn((
NodeBundle {
style: Style {
align_items: AlignItems::Center,
justify_content: JustifyContent::Center,
width: Val::Percent(100.0),
height: Val::Percent(100.0),
..default()
},
..default()
},
OnSplashScreen,
))
.with_children(|parent| {
parent.spawn(ImageBundle {
style: Style {
// This will set the logo to be 200px wide, and auto adjust its height
width: Val::Px(200.0),
..default()
},
image: UiImage::new(icon),
..default()
});
});
// Insert the timer as a resource
commands.insert_resource(SplashTimer(Timer::from_seconds(1.0, TimerMode::Once)));
}
// Tick the timer, and change state when finished
fn countdown(
mut game_state: ResMut<NextState<GameState>>,
time: Res<Time>,
mut timer: ResMut<SplashTimer>,
) {
if timer.tick(time.delta()).finished() {
game_state.set(GameState::Menu);
}
}
}
mod game {
use bevy::{
color::palettes::basic::{BLUE, LIME},
prelude::*,
};
use super::{despawn_screen, DisplayQuality, GameState, Volume, TEXT_COLOR};
// This plugin will contain the game. In this case, it's just be a screen that will
// display the current settings for 5 seconds before returning to the menu
pub fn game_plugin(app: &mut App) {
app.add_systems(OnEnter(GameState::Game), game_setup)
.add_systems(Update, game.run_if(in_state(GameState::Game)))
.add_systems(OnExit(GameState::Game), despawn_screen::<OnGameScreen>);
}
// Tag component used to tag entities added on the game screen
#[derive(Component)]
struct OnGameScreen;
#[derive(Resource, Deref, DerefMut)]
struct GameTimer(Timer);
fn game_setup(
mut commands: Commands,
display_quality: Res<DisplayQuality>,
volume: Res<Volume>,
) {
commands
.spawn((
NodeBundle {
style: Style {
width: Val::Percent(100.0),
height: Val::Percent(100.0),
// center children
align_items: AlignItems::Center,
justify_content: JustifyContent::Center,
..default()
},
..default()
},
OnGameScreen,
))
.with_children(|parent| {
// First create a `NodeBundle` for centering what we want to display
parent
.spawn(NodeBundle {
style: Style {
// This will display its children in a column, from top to bottom
flex_direction: FlexDirection::Column,
// `align_items` will align children on the cross axis. Here the main axis is
// vertical (column), so the cross axis is horizontal. This will center the
// children
align_items: AlignItems::Center,
..default()
},
background_color: Color::BLACK.into(),
..default()
})
.with_children(|parent| {
// Display two lines of text, the second one with the current settings
parent.spawn(
TextBundle::from_section(
"Will be back to the menu shortly...",
TextStyle {
font_size: 80.0,
color: TEXT_COLOR,
..default()
},
)
.with_style(Style {
margin: UiRect::all(Val::Px(50.0)),
..default()
}),
);
parent.spawn(
TextBundle::from_sections([
TextSection::new(
format!("quality: {:?}", *display_quality),
TextStyle {
font_size: 60.0,
color: BLUE.into(),
..default()
},
),
TextSection::new(
" - ",
TextStyle {
font_size: 60.0,
color: TEXT_COLOR,
..default()
},
),
TextSection::new(
format!("volume: {:?}", *volume),
TextStyle {
font_size: 60.0,
color: LIME.into(),
..default()
},
),
])
.with_style(Style {
margin: UiRect::all(Val::Px(50.0)),
..default()
}),
);
});
});
// Spawn a 5 seconds timer to trigger going back to the menu
commands.insert_resource(GameTimer(Timer::from_seconds(5.0, TimerMode::Once)));
}
// Tick the timer, and change state when finished
fn game(
time: Res<Time>,
mut game_state: ResMut<NextState<GameState>>,
mut timer: ResMut<GameTimer>,
) {
if timer.tick(time.delta()).finished() {
game_state.set(GameState::Menu);
}
}
}
mod menu {
use bevy::{app::AppExit, color::palettes::css::CRIMSON, prelude::*};
use super::{despawn_screen, DisplayQuality, GameState, Volume, TEXT_COLOR};
// This plugin manages the menu, with 5 different screens:
// - a main menu with "New Game", "Settings", "Quit"
// - a settings menu with two submenus and a back button
// - two settings screen with a setting that can be set and a back button
pub fn menu_plugin(app: &mut App) {
app
// At start, the menu is not enabled. This will be changed in `menu_setup` when
// entering the `GameState::Menu` state.
// Current screen in the menu is handled by an independent state from `GameState`
.init_state::<MenuState>()
.add_systems(OnEnter(GameState::Menu), menu_setup)
// Systems to handle the main menu screen
.add_systems(OnEnter(MenuState::Main), main_menu_setup)
.add_systems(OnExit(MenuState::Main), despawn_screen::<OnMainMenuScreen>)
// Systems to handle the settings menu screen
.add_systems(OnEnter(MenuState::Settings), settings_menu_setup)
.add_systems(
OnExit(MenuState::Settings),
despawn_screen::<OnSettingsMenuScreen>,
)
// Systems to handle the display settings screen
.add_systems(
OnEnter(MenuState::SettingsDisplay),
display_settings_menu_setup,
)
.add_systems(
Update,
(setting_button::<DisplayQuality>.run_if(in_state(MenuState::SettingsDisplay)),),
)
.add_systems(
OnExit(MenuState::SettingsDisplay),
despawn_screen::<OnDisplaySettingsMenuScreen>,
)
// Systems to handle the sound settings screen
.add_systems(OnEnter(MenuState::SettingsSound), sound_settings_menu_setup)
.add_systems(
Update,
setting_button::<Volume>.run_if(in_state(MenuState::SettingsSound)),
)
.add_systems(
OnExit(MenuState::SettingsSound),
despawn_screen::<OnSoundSettingsMenuScreen>,
)
// Common systems to all screens that handles buttons behavior
.add_systems(
Update,
(menu_action, button_system).run_if(in_state(GameState::Menu)),
);
}
// State used for the current menu screen
#[derive(Clone, Copy, Default, Eq, PartialEq, Debug, Hash, States)]
enum MenuState {
Main,
Settings,
SettingsDisplay,
SettingsSound,
#[default]
Disabled,
}
// Tag component used to tag entities added on the main menu screen
#[derive(Component)]
struct OnMainMenuScreen;
// Tag component used to tag entities added on the settings menu screen
#[derive(Component)]
struct OnSettingsMenuScreen;
// Tag component used to tag entities added on the display settings menu screen
#[derive(Component)]
struct OnDisplaySettingsMenuScreen;
// Tag component used to tag entities added on the sound settings menu screen
#[derive(Component)]
struct OnSoundSettingsMenuScreen;
const NORMAL_BUTTON: Color = Color::srgb(0.15, 0.15, 0.15);
const HOVERED_BUTTON: Color = Color::srgb(0.25, 0.25, 0.25);
const HOVERED_PRESSED_BUTTON: Color = Color::srgb(0.25, 0.65, 0.25);
const PRESSED_BUTTON: Color = Color::srgb(0.35, 0.75, 0.35);
// Tag component used to mark which setting is currently selected
#[derive(Component)]
struct SelectedOption;
// All actions that can be triggered from a button click
#[derive(Component)]
enum MenuButtonAction {
Play,
Settings,
SettingsDisplay,
SettingsSound,
BackToMainMenu,
BackToSettings,
Quit,
}
// This system handles changing all buttons color based on mouse interaction
fn button_system(
mut interaction_query: Query<
(&Interaction, &mut UiImage, Option<&SelectedOption>),
(Changed<Interaction>, With<Button>),
>,
) {
for (interaction, mut image, selected) in &mut interaction_query {
image.color = match (*interaction, selected) {
(Interaction::Pressed, _) | (Interaction::None, Some(_)) => PRESSED_BUTTON,
(Interaction::Hovered, Some(_)) => HOVERED_PRESSED_BUTTON,
(Interaction::Hovered, None) => HOVERED_BUTTON,
(Interaction::None, None) => NORMAL_BUTTON,
}
}
}
// This system updates the settings when a new value for a setting is selected, and marks
// the button as the one currently selected
fn setting_button<T: Resource + Component + PartialEq + Copy>(
interaction_query: Query<(&Interaction, &T, Entity), (Changed<Interaction>, With<Button>)>,
mut selected_query: Query<(Entity, &mut UiImage), With<SelectedOption>>,
mut commands: Commands,
mut setting: ResMut<T>,
) {
for (interaction, button_setting, entity) in &interaction_query {
if *interaction == Interaction::Pressed && *setting != *button_setting {
let (previous_button, mut previous_image) = selected_query.single_mut();
previous_image.color = NORMAL_BUTTON;
commands.entity(previous_button).remove::<SelectedOption>();
commands.entity(entity).insert(SelectedOption);
*setting = *button_setting;
}
}
}
fn menu_setup(mut menu_state: ResMut<NextState<MenuState>>) {
menu_state.set(MenuState::Main);
}
fn main_menu_setup(mut commands: Commands, asset_server: Res<AssetServer>) {
// Common style for all buttons on the screen
let button_style = Style {
width: Val::Px(250.0),
height: Val::Px(65.0),
margin: UiRect::all(Val::Px(20.0)),
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
..default()
};
let button_icon_style = Style {
width: Val::Px(30.0),
// This takes the icons out of the flexbox flow, to be positioned exactly
position_type: PositionType::Absolute,
// The icon will be close to the left border of the button
left: Val::Px(10.0),
..default()
};
let button_text_style = TextStyle {
font_size: 40.0,
color: TEXT_COLOR,
..default()
};
commands
.spawn((
NodeBundle {
style: Style {
width: Val::Percent(100.0),
height: Val::Percent(100.0),
align_items: AlignItems::Center,
justify_content: JustifyContent::Center,
..default()
},
..default()
},
OnMainMenuScreen,
))
.with_children(|parent| {
parent
.spawn(NodeBundle {
style: Style {
flex_direction: FlexDirection::Column,
align_items: AlignItems::Center,
..default()
},
background_color: CRIMSON.into(),
..default()
})
.with_children(|parent| {
// Display the game name
parent.spawn(
TextBundle::from_section(
"Bevy Game Menu UI",
TextStyle {
font_size: 80.0,
color: TEXT_COLOR,
..default()
},
)
.with_style(Style {
margin: UiRect::all(Val::Px(50.0)),
..default()
}),
);
// Display three buttons for each action available from the main menu:
// - new game
// - settings
// - quit
parent
.spawn((
ButtonBundle {
style: button_style.clone(),
background_color: NORMAL_BUTTON.into(),
..default()
},
MenuButtonAction::Play,
))
.with_children(|parent| {
let icon = asset_server.load("textures/Game Icons/right.png");
parent.spawn(ImageBundle {
style: button_icon_style.clone(),
image: UiImage::new(icon),
..default()
});
parent.spawn(TextBundle::from_section(
"New Game",
button_text_style.clone(),
));
});
parent
.spawn((
ButtonBundle {
style: button_style.clone(),
background_color: NORMAL_BUTTON.into(),
..default()
},
MenuButtonAction::Settings,
))
.with_children(|parent| {
let icon = asset_server.load("textures/Game Icons/wrench.png");
parent.spawn(ImageBundle {
style: button_icon_style.clone(),
image: UiImage::new(icon),
..default()
});
parent.spawn(TextBundle::from_section(
"Settings",
button_text_style.clone(),
));
});
parent
.spawn((
ButtonBundle {
style: button_style,
background_color: NORMAL_BUTTON.into(),
..default()
},
MenuButtonAction::Quit,
))
.with_children(|parent| {
let icon = asset_server.load("textures/Game Icons/exitRight.png");
parent.spawn(ImageBundle {
style: button_icon_style,
image: UiImage::new(icon),
..default()
});
parent.spawn(TextBundle::from_section("Quit", button_text_style));
});
});
});
}
fn settings_menu_setup(mut commands: Commands) {
let button_style = Style {
width: Val::Px(200.0),
height: Val::Px(65.0),
margin: UiRect::all(Val::Px(20.0)),
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
..default()
};
let button_text_style = TextStyle {
font_size: 40.0,
color: TEXT_COLOR,
..default()
};
commands
.spawn((
NodeBundle {
style: Style {
width: Val::Percent(100.0),
height: Val::Percent(100.0),
align_items: AlignItems::Center,
justify_content: JustifyContent::Center,
..default()
},
..default()
},
OnSettingsMenuScreen,
))
.with_children(|parent| {
parent
.spawn(NodeBundle {
style: Style {
flex_direction: FlexDirection::Column,
align_items: AlignItems::Center,
..default()
},
background_color: CRIMSON.into(),
..default()
})
.with_children(|parent| {
for (action, text) in [
(MenuButtonAction::SettingsDisplay, "Display"),
(MenuButtonAction::SettingsSound, "Sound"),
(MenuButtonAction::BackToMainMenu, "Back"),
] {
parent
.spawn((
ButtonBundle {
style: button_style.clone(),
background_color: NORMAL_BUTTON.into(),
..default()
},
action,
))
.with_children(|parent| {
parent.spawn(TextBundle::from_section(
text,
button_text_style.clone(),
));
});
}
});
});
}
fn display_settings_menu_setup(mut commands: Commands, display_quality: Res<DisplayQuality>) {
let button_style = Style {
width: Val::Px(200.0),
height: Val::Px(65.0),
margin: UiRect::all(Val::Px(20.0)),
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
..default()
};
let button_text_style = TextStyle {
font_size: 40.0,
color: TEXT_COLOR,
..default()
};
commands
.spawn((
NodeBundle {
style: Style {
width: Val::Percent(100.0),
height: Val::Percent(100.0),
align_items: AlignItems::Center,
justify_content: JustifyContent::Center,
..default()
},
..default()
},
OnDisplaySettingsMenuScreen,
))
.with_children(|parent| {
parent
.spawn(NodeBundle {
style: Style {
flex_direction: FlexDirection::Column,
align_items: AlignItems::Center,
..default()
},
background_color: CRIMSON.into(),
..default()
})
.with_children(|parent| {
// Create a new `NodeBundle`, this time not setting its `flex_direction`. It will
// use the default value, `FlexDirection::Row`, from left to right.
parent
.spawn(NodeBundle {
style: Style {
align_items: AlignItems::Center,
..default()
},
background_color: CRIMSON.into(),
..default()
})
.with_children(|parent| {
// Display a label for the current setting
parent.spawn(TextBundle::from_section(
"Display Quality",
button_text_style.clone(),
));
// Display a button for each possible value
for quality_setting in [
DisplayQuality::Low,
DisplayQuality::Medium,
DisplayQuality::High,
] {
let mut entity = parent.spawn((
ButtonBundle {
style: Style {
width: Val::Px(150.0),
height: Val::Px(65.0),
..button_style.clone()
},
background_color: NORMAL_BUTTON.into(),
..default()
},
quality_setting,
));
entity.with_children(|parent| {
parent.spawn(TextBundle::from_section(
format!("{quality_setting:?}"),
button_text_style.clone(),
));
});
if *display_quality == quality_setting {
entity.insert(SelectedOption);
}
}
});
// Display the back button to return to the settings screen
parent
.spawn((
ButtonBundle {
style: button_style,
background_color: NORMAL_BUTTON.into(),
..default()
},
MenuButtonAction::BackToSettings,
))
.with_children(|parent| {
parent.spawn(TextBundle::from_section("Back", button_text_style));
});
});
});
}
fn sound_settings_menu_setup(mut commands: Commands, volume: Res<Volume>) {
let button_style = Style {
width: Val::Px(200.0),
height: Val::Px(65.0),
margin: UiRect::all(Val::Px(20.0)),
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
..default()
};
let button_text_style = TextStyle {
font_size: 40.0,
color: TEXT_COLOR,
..default()
};
commands
.spawn((
NodeBundle {
style: Style {
width: Val::Percent(100.0),
height: Val::Percent(100.0),
align_items: AlignItems::Center,
justify_content: JustifyContent::Center,
..default()
},
..default()
},
OnSoundSettingsMenuScreen,
))
.with_children(|parent| {
parent
.spawn(NodeBundle {
style: Style {
flex_direction: FlexDirection::Column,
align_items: AlignItems::Center,
..default()
},
background_color: CRIMSON.into(),
..default()
})
.with_children(|parent| {
parent
.spawn(NodeBundle {
style: Style {
align_items: AlignItems::Center,
..default()
},
background_color: CRIMSON.into(),
..default()
})
.with_children(|parent| {
parent.spawn(TextBundle::from_section(
"Volume",
button_text_style.clone(),
));
for volume_setting in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] {
let mut entity = parent.spawn((
ButtonBundle {
style: Style {
width: Val::Px(30.0),
height: Val::Px(65.0),
..button_style.clone()
},
background_color: NORMAL_BUTTON.into(),
..default()
},
Volume(volume_setting),
));
if *volume == Volume(volume_setting) {
entity.insert(SelectedOption);
}
}
});
parent
.spawn((
ButtonBundle {
style: button_style,
background_color: NORMAL_BUTTON.into(),
..default()
},
MenuButtonAction::BackToSettings,
))
.with_children(|parent| {
parent.spawn(TextBundle::from_section("Back", button_text_style));
});
});
});
}
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
fn setup(mut commands: Commands) {
commands.spawn(Camera2dBundle::default());
}
fn setup_game(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn(SpriteBundle {
texture: asset_server.load("branding/icon.png"),
..default()
});
info!("Setup game");
}
fn teardown_game(mut commands: Commands, player: Query<Entity, With<Sprite>>) {
commands.entity(player.single()).despawn();
info!("Teardown game");
}
#[derive(Resource)]
struct MenuData {
pub button_entity: Entity,
}
const NORMAL_BUTTON: Color = Color::srgb(0.15, 0.15, 0.15);
const HOVERED_BUTTON: Color = Color::srgb(0.25, 0.25, 0.25);
const PRESSED_BUTTON: Color = Color::srgb(0.35, 0.75, 0.35);
fn setup_menu(mut commands: Commands) {
let button_entity = commands
.spawn(NodeBundle {
style: Style {
// center button
width: Val::Percent(100.),
height: Val::Percent(100.),
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
..default()
},
..default()
})
.with_children(|parent| {
parent
.spawn(ButtonBundle {
style: Style {
width: Val::Px(150.),
height: Val::Px(65.),
// horizontally center child text
justify_content: JustifyContent::Center,
// vertically center child text
align_items: AlignItems::Center,
..default()
},
background_color: NORMAL_BUTTON.into(),
..default()
})
.with_children(|parent| {
parent.spawn(TextBundle::from_section(
"Play",
TextStyle {
font_size: 40.0,
color: Color::srgb(0.9, 0.9, 0.9),
..default()
},
));
});
})
.id();
commands.insert_resource(MenuData { button_entity });
}
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
fn setup(mut commands: Commands) {
commands.spawn(Camera2dBundle::default());
}
fn setup_menu(mut commands: Commands) {
let button_entity = commands
.spawn(NodeBundle {
style: Style {
// center button
width: Val::Percent(100.),
height: Val::Percent(100.),
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
..default()
},
..default()
})
.with_children(|parent| {
parent
.spawn(ButtonBundle {
style: Style {
width: Val::Px(150.),
height: Val::Px(65.),
// horizontally center child text
justify_content: JustifyContent::Center,
// vertically center child text
align_items: AlignItems::Center,
..default()
},
background_color: NORMAL_BUTTON.into(),
..default()
})
.with_children(|parent| {
parent.spawn(TextBundle::from_section(
"Play",
TextStyle {
font_size: 40.0,
color: Color::srgb(0.9, 0.9, 0.9),
..default()
},
));
});
})
.id();
commands.insert_resource(MenuData { button_entity });
}
fn menu(
mut next_state: ResMut<NextState<AppState>>,
mut interaction_query: Query<
(&Interaction, &mut BackgroundColor),
(Changed<Interaction>, With<Button>),
>,
) {
for (interaction, mut color) in &mut interaction_query {
match *interaction {
Interaction::Pressed => {
*color = PRESSED_BUTTON.into();
next_state.set(AppState::InGame);
}
Interaction::Hovered => {
*color = HOVERED_BUTTON.into();
}
Interaction::None => {
*color = NORMAL_BUTTON.into();
}
}
}
}
fn cleanup_menu(mut commands: Commands, menu_data: Res<MenuData>) {
commands.entity(menu_data.button_entity).despawn_recursive();
}
fn setup_game(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn(SpriteBundle {
texture: asset_server.load("branding/icon.png"),
..default()
});
}
- examples/window/custom_user_event.rs
- examples/app/without_winit.rs
- examples/state/computed_states.rs
- examples/state/sub_states.rs
- examples/ecs/custom_query_param.rs
- examples/ecs/component_change_detection.rs
- examples/ecs/system_param.rs
- examples/ecs/one_shot_systems.rs
- examples/ui/viewport_debug.rs
- examples/3d/irradiance_volumes.rs
- examples/time/timers.rs
- examples/audio/audio.rs
- examples/asset/custom_asset_reader.rs
- examples/window/transparent_window.rs
- examples/audio/audio_control.rs
- examples/3d/reflection_probes.rs
- examples/2d/sprite.rs
- examples/ecs/removal_detection.rs
- examples/3d/fog.rs
- examples/2d/move_sprite.rs
- examples/audio/decodable.rs
- examples/ecs/component_hooks.rs
- examples/3d/lightmaps.rs
- examples/asset/asset_decompression.rs
- examples/app/logs.rs
- examples/app/log_layers_ecs.rs
- examples/scene/scene.rs
- examples/ecs/generic_system.rs
- examples/2d/mesh2d.rs
- examples/2d/sprite_flipping.rs
- examples/games/alien_cake_addict.rs
- examples/stress_tests/many_gizmos.rs
- examples/math/render_primitives.rs
- examples/audio/pitch.rs
- examples/animation/gltf_skinned_mesh.rs
- examples/shader/animate_shader.rs
- examples/async_tasks/async_compute.rs
- examples/2d/sprite_tile.rs
- examples/ecs/ecs_guide.rs
- examples/3d/anisotropy.rs
- examples/shader/shader_material_2d.rs
- examples/shader/array_texture.rs
- examples/3d/ssr.rs
- examples/shader/fallback_image.rs
- examples/3d/motion_blur.rs
- examples/2d/custom_gltf_vertex_attribute.rs
- examples/gizmos/2d_gizmos.rs
- examples/shader/shader_material.rs
- examples/shader/shader_material_glsl.rs
- examples/3d/3d_viewport_to_world.rs
- examples/shader/custom_phase_item.rs
- examples/shader/texture_binding_array.rs
- examples/asset/extra_source.rs
- examples/2d/pixel_grid_snap.rs
- examples/ecs/parallel_query.rs
- examples/camera/2d_top_down_camera.rs
- examples/asset/hot_asset_reloading.rs
- examples/ui/window_fallthrough.rs
- examples/2d/transparency_2d.rs
- examples/shader/custom_vertex_attribute.rs
- examples/transforms/3d_rotation.rs
- examples/ui/font_atlas_debug.rs
- examples/dev_tools/fps_overlay.rs
- examples/transforms/scale.rs
- examples/3d/atmospheric_fog.rs
- examples/3d/clearcoat.rs
- examples/async_tasks/external_source_external_thread.rs
- examples/animation/morph_targets.rs
- examples/transforms/translation.rs
- examples/2d/sprite_sheet.rs
- examples/asset/embedded_asset.rs
- tests/window/minimising.rs
- tests/window/resizing.rs
- examples/games/loading_screen.rs
- examples/shader/shader_defs.rs
- examples/ui/ui_material.rs
- examples/3d/load_gltf_extras.rs
- examples/shader/compute_shader_game_of_life.rs
- examples/3d/3d_scene.rs
- examples/3d/color_grading.rs
- examples/asset/multi_asset_sync.rs
- examples/animation/animation_graph.rs
- examples/3d/tonemapping.rs
- examples/shader/shader_material_screenspace_texture.rs
- examples/3d/animated_material.rs
- examples/stress_tests/text_pipeline.rs
- examples/shader/post_processing.rs
- examples/3d/skybox.rs
- examples/3d/parenting.rs
- examples/3d/lines.rs
- examples/3d/depth_of_field.rs
- examples/window/screenshot.rs
- examples/3d/load_gltf.rs
- examples/3d/two_passes.rs
- examples/stress_tests/many_glyphs.rs
- examples/shader/shader_instancing.rs
- examples/3d/update_gltf_scene.rs
- examples/animation/color_animation.rs
- examples/stress_tests/many_sprites.rs
- examples/window/scale_factor_override.rs
- examples/ecs/hierarchy.rs
- examples/animation/cubic_curve.rs
- examples/2d/mesh2d_vertex_color_texture.rs
- examples/3d/volumetric_fog.rs
- examples/shader/extended_material.rs
- examples/audio/soundtrack.rs
- examples/3d/vertex_colors.rs
- examples/3d/generate_custom_mesh.rs
- examples/ui/button.rs
- examples/3d/orthographic.rs
- examples/2d/wireframe_2d.rs
- examples/ui/relative_cursor_position.rs
- examples/ecs/observers.rs
- examples/stress_tests/many_animated_sprites.rs
- examples/window/multiple_windows.rs
- examples/transforms/transform.rs
- examples/gizmos/3d_gizmos.rs
- examples/3d/spherical_area_lights.rs
- examples/ui/ui_scaling.rs
- examples/2d/2d_shapes.rs
- examples/2d/bloom_2d.rs
- examples/gizmos/axes.rs
- examples/stress_tests/transform_hierarchy.rs
- examples/games/contributors.rs
- examples/2d/rotation.rs
- examples/app/headless_renderer.rs
- examples/ui/ui_texture_atlas.rs
- examples/audio/spatial_audio_2d.rs
- examples/window/low_power.rs
- examples/stress_tests/many_buttons.rs
- examples/ui/overflow_debug.rs
- examples/camera/first_person_view_model.rs
- examples/2d/bounding_2d.rs
- examples/math/custom_primitives.rs
- examples/input/text_input.rs
- examples/2d/sprite_animation.rs
- examples/ui/ui_texture_slice.rs
- examples/3d/wireframe.rs
- examples/audio/spatial_audio_3d.rs
- examples/3d/ssao.rs
- examples/3d/texture.rs
- examples/3d/visibility_range.rs
- examples/animation/animated_fox.rs
- examples/stress_tests/many_lights.rs
- examples/ui/ui_texture_slice_flip_and_tile.rs
- examples/3d/bloom_3d.rs
- examples/games/stepping.rs
- examples/ui/transparency_ui.rs
- examples/ui/size_constraints.rs
- examples/ui/ui_texture_atlas_slice.rs
- examples/math/random_sampling.rs
- examples/3d/shadow_caster_receiver.rs
- examples/transforms/align.rs
- examples/3d/anti_aliasing.rs
- examples/time/virtual_time.rs
- examples/asset/repeated_texture.rs
- examples/2d/mesh2d_manual.rs
- examples/2d/mesh2d_arcs.rs
- examples/ui/render_ui_to_texture.rs
- examples/ecs/iter_combinations.rs
- examples/2d/sprite_slice.rs
- examples/ui/text.rs
- examples/asset/asset_settings.rs
- examples/3d/transparency_3d.rs
- examples/stress_tests/bevymark.rs
- examples/3d/meshlet.rs
- examples/3d/3d_shapes.rs
- examples/ui/text_wrap_debug.rs
- examples/3d/pbr.rs
- examples/gizmos/light_gizmos.rs
- examples/3d/render_to_texture.rs
- examples/3d/auto_exposure.rs
- examples/3d/spotlight.rs
- examples/asset/asset_loading.rs
- examples/shader/shader_prepass.rs
- examples/ui/flex_layout.rs
- examples/ui/overflow.rs
- examples/ui/display_and_visibility.rs
- examples/stress_tests/many_foxes.rs
- examples/3d/split_screen.rs
- examples/games/breakout.rs
- examples/2d/texture_atlas.rs
- examples/animation/custom_skinned_mesh.rs
- examples/2d/text2d.rs
- examples/ui/borders.rs
- examples/ui/z_index.rs
- examples/math/sampling_primitives.rs
- examples/games/desk_toy.rs
- examples/3d/parallax_mapping.rs
- examples/ui/rounded_borders.rs
- examples/3d/shadow_biases.rs
- examples/ui/text_debug.rs
- examples/3d/deferred_rendering.rs
- examples/3d/blend_modes.rs
- examples/animation/animated_transform.rs
- examples/stress_tests/many_cubes.rs
- examples/3d/lighting.rs
- examples/ui/grid.rs
- examples/3d/transmission.rs
- examples/ui/ui.rs
sourcepub fn entity(&mut self, entity: Entity) -> EntityCommands<'_>
pub fn entity(&mut self, entity: Entity) -> EntityCommands<'_>
Returns the EntityCommands
for the requested Entity
.
§Panics
This method panics if the requested entity does not exist.
§Example
use bevy_ecs::prelude::*;
#[derive(Component)]
struct Label(&'static str);
#[derive(Component)]
struct Strength(u32);
#[derive(Component)]
struct Agility(u32);
fn example_system(mut commands: Commands) {
// Create a new, empty entity
let entity = commands.spawn_empty().id();
commands.entity(entity)
// adds a new component bundle to the entity
.insert((Strength(1), Agility(2)))
// adds a single component to the entity
.insert(Label("hello world"));
}
§See also
get_entity
for the fallible version.
Examples found in repository?
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
fn cleanup_menu(mut commands: Commands, menu_data: Res<MenuData>) {
commands.entity(menu_data.button_entity).despawn_recursive();
}
const SPEED: f32 = 100.0;
fn movement(
time: Res<Time>,
input: Res<ButtonInput<KeyCode>>,
mut query: Query<&mut Transform, With<Sprite>>,
) {
for mut transform in &mut query {
let mut direction = Vec3::ZERO;
if input.pressed(KeyCode::ArrowLeft) {
direction.x -= 1.0;
}
if input.pressed(KeyCode::ArrowRight) {
direction.x += 1.0;
}
if input.pressed(KeyCode::ArrowUp) {
direction.y += 1.0;
}
if input.pressed(KeyCode::ArrowDown) {
direction.y -= 1.0;
}
if direction != Vec3::ZERO {
transform.translation += direction.normalize() * SPEED * time.delta_seconds();
}
}
}
fn change_color(time: Res<Time>, mut query: Query<&mut Sprite>) {
for mut sprite in &mut query {
let new_color = LinearRgba {
blue: (time.elapsed_seconds() * 0.5).sin() + 2.0,
..LinearRgba::from(sprite.color)
};
sprite.color = new_color.into();
}
}
// We can restart the game by pressing "R".
// This will trigger an [`AppState::InGame`] -> [`AppState::InGame`]
// transition, which will run our custom schedules.
fn trigger_game_restart(
input: Res<ButtonInput<KeyCode>>,
mut next_state: ResMut<NextState<AppState>>,
) {
if input.just_pressed(KeyCode::KeyR) {
// Although we are already in this state setting it again will generate an identity transition.
// While default schedules ignore those kinds of transitions, our custom schedules will react to them.
next_state.set(AppState::InGame);
}
}
fn setup(mut commands: Commands) {
commands.spawn(Camera2dBundle::default());
}
fn setup_game(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn(SpriteBundle {
texture: asset_server.load("branding/icon.png"),
..default()
});
info!("Setup game");
}
fn teardown_game(mut commands: Commands, player: Query<Entity, With<Sprite>>) {
commands.entity(player.single()).despawn();
info!("Teardown game");
}
More examples
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372
fn teardown(mut commands: Commands, entities: Query<Entity, (Without<Camera>, Without<Window>)>) {
for entity in &entities {
commands.entity(entity).despawn();
}
}
// control the game character
fn move_player(
mut commands: Commands,
keyboard_input: Res<ButtonInput<KeyCode>>,
mut game: ResMut<Game>,
mut transforms: Query<&mut Transform>,
time: Res<Time>,
) {
if game.player.move_cooldown.tick(time.delta()).finished() {
let mut moved = false;
let mut rotation = 0.0;
if keyboard_input.pressed(KeyCode::ArrowUp) {
if game.player.i < BOARD_SIZE_I - 1 {
game.player.i += 1;
}
rotation = -PI / 2.;
moved = true;
}
if keyboard_input.pressed(KeyCode::ArrowDown) {
if game.player.i > 0 {
game.player.i -= 1;
}
rotation = PI / 2.;
moved = true;
}
if keyboard_input.pressed(KeyCode::ArrowRight) {
if game.player.j < BOARD_SIZE_J - 1 {
game.player.j += 1;
}
rotation = PI;
moved = true;
}
if keyboard_input.pressed(KeyCode::ArrowLeft) {
if game.player.j > 0 {
game.player.j -= 1;
}
rotation = 0.0;
moved = true;
}
// move on the board
if moved {
game.player.move_cooldown.reset();
*transforms.get_mut(game.player.entity.unwrap()).unwrap() = Transform {
translation: Vec3::new(
game.player.i as f32,
game.board[game.player.j][game.player.i].height,
game.player.j as f32,
),
rotation: Quat::from_rotation_y(rotation),
..default()
};
}
}
// eat the cake!
if let Some(entity) = game.bonus.entity {
if game.player.i == game.bonus.i && game.player.j == game.bonus.j {
game.score += 2;
game.cake_eaten += 1;
commands.entity(entity).despawn_recursive();
game.bonus.entity = None;
}
}
}
// change the focus of the camera
fn focus_camera(
time: Res<Time>,
mut game: ResMut<Game>,
mut transforms: ParamSet<(Query<&mut Transform, With<Camera3d>>, Query<&Transform>)>,
) {
const SPEED: f32 = 2.0;
// if there is both a player and a bonus, target the mid-point of them
if let (Some(player_entity), Some(bonus_entity)) = (game.player.entity, game.bonus.entity) {
let transform_query = transforms.p1();
if let (Ok(player_transform), Ok(bonus_transform)) = (
transform_query.get(player_entity),
transform_query.get(bonus_entity),
) {
game.camera_should_focus = player_transform
.translation
.lerp(bonus_transform.translation, 0.5);
}
// otherwise, if there is only a player, target the player
} else if let Some(player_entity) = game.player.entity {
if let Ok(player_transform) = transforms.p1().get(player_entity) {
game.camera_should_focus = player_transform.translation;
}
// otherwise, target the middle
} else {
game.camera_should_focus = Vec3::from(RESET_FOCUS);
}
// calculate the camera motion based on the difference between where the camera is looking
// and where it should be looking; the greater the distance, the faster the motion;
// smooth out the camera movement using the frame time
let mut camera_motion = game.camera_should_focus - game.camera_is_focus;
if camera_motion.length() > 0.2 {
camera_motion *= SPEED * time.delta_seconds();
// set the new camera's actual focus
game.camera_is_focus += camera_motion;
}
// look at that new camera's actual focus
for mut transform in transforms.p0().iter_mut() {
*transform = transform.looking_at(game.camera_is_focus, Vec3::Y);
}
}
// despawn the bonus if there is one, then spawn a new one at a random location
fn spawn_bonus(
time: Res<Time>,
mut timer: ResMut<BonusSpawnTimer>,
mut next_state: ResMut<NextState<GameState>>,
mut commands: Commands,
mut game: ResMut<Game>,
mut rng: ResMut<Random>,
) {
// make sure we wait enough time before spawning the next cake
if !timer.0.tick(time.delta()).finished() {
return;
}
if let Some(entity) = game.bonus.entity {
game.score -= 3;
commands.entity(entity).despawn_recursive();
game.bonus.entity = None;
if game.score <= -5 {
next_state.set(GameState::GameOver);
return;
}
}
// ensure bonus doesn't spawn on the player
loop {
game.bonus.i = rng.gen_range(0..BOARD_SIZE_I);
game.bonus.j = rng.gen_range(0..BOARD_SIZE_J);
if game.bonus.i != game.player.i || game.bonus.j != game.player.j {
break;
}
}
game.bonus.entity = Some(
commands
.spawn(SceneBundle {
transform: Transform::from_xyz(
game.bonus.i as f32,
game.board[game.bonus.j][game.bonus.i].height + 0.2,
game.bonus.j as f32,
),
scene: game.bonus.handle.clone(),
..default()
})
.with_children(|children| {
children.spawn(PointLightBundle {
point_light: PointLight {
color: Color::srgb(1.0, 1.0, 0.0),
intensity: 500_000.0,
range: 10.0,
..default()
},
transform: Transform::from_xyz(0.0, 2.0, 0.0),
..default()
});
})
.id(),
);
}
- examples/games/loading_screen.rs
- examples/ecs/removal_detection.rs
- examples/async_tasks/external_source_external_thread.rs
- examples/3d/volumetric_fog.rs
- examples/input/text_input.rs
- examples/asset/multi_asset_sync.rs
- examples/3d/reflection_probes.rs
- examples/app/log_layers_ecs.rs
- examples/ecs/one_shot_systems.rs
- examples/3d/depth_of_field.rs
- examples/asset/asset_decompression.rs
- examples/shader/shader_instancing.rs
- examples/3d/pbr.rs
- examples/animation/morph_targets.rs
- examples/animation/animated_fox.rs
- examples/games/game_menu.rs
- examples/3d/anisotropy.rs
- examples/3d/clearcoat.rs
- examples/3d/tonemapping.rs
- examples/3d/irradiance_volumes.rs
- examples/3d/shadow_caster_receiver.rs
- examples/games/stepping.rs
- examples/3d/visibility_range.rs
- examples/3d/lightmaps.rs
- examples/ecs/hierarchy.rs
- examples/audio/soundtrack.rs
- examples/2d/bounding_2d.rs
- examples/games/breakout.rs
- examples/3d/ssr.rs
- examples/ecs/component_hooks.rs
- examples/async_tasks/async_compute.rs
- examples/animation/animation_graph.rs
- examples/3d/ssao.rs
- examples/math/random_sampling.rs
- examples/3d/deferred_rendering.rs
- examples/math/sampling_primitives.rs
- examples/3d/anti_aliasing.rs
- examples/ui/text_wrap_debug.rs
- examples/stress_tests/many_foxes.rs
- examples/animation/custom_skinned_mesh.rs
- examples/ui/borders.rs
- examples/3d/bloom_3d.rs
- examples/2d/bloom_2d.rs
- examples/ui/rounded_borders.rs
- examples/ui/text_debug.rs
- examples/animation/animated_transform.rs
- examples/3d/transmission.rs
sourcepub fn get_entity(&mut self, entity: Entity) -> Option<EntityCommands<'_>>
pub fn get_entity(&mut self, entity: Entity) -> Option<EntityCommands<'_>>
Returns the EntityCommands
for the requested Entity
, if it exists.
Returns None
if the entity does not exist.
This method does not guarantee that EntityCommands
will be successfully applied,
since another command in the queue may delete the entity before them.
§Example
use bevy_ecs::prelude::*;
#[derive(Component)]
struct Label(&'static str);
fn example_system(mut commands: Commands) {
// Create a new, empty entity
let entity = commands.spawn_empty().id();
// Get the entity if it still exists, which it will in this case
if let Some(mut entity_commands) = commands.get_entity(entity) {
// adds a single component to the entity
entity_commands.insert(Label("hello world"));
}
}
§See also
entity
for the panicking version.
Examples found in repository?
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
fn explode_mine(trigger: Trigger<Explode>, query: Query<&Mine>, mut commands: Commands) {
// If a triggered event is targeting a specific entity you can access it with `.entity()`
let id = trigger.entity();
let Some(mut entity) = commands.get_entity(id) else {
return;
};
info!("Boom! {:?} exploded.", id.index());
entity.despawn();
let mine = query.get(id).unwrap();
// Trigger another explosion cascade.
commands.trigger(ExplodeMines {
pos: mine.pos,
radius: mine.size,
});
}
sourcepub fn spawn_batch<I>(&mut self, bundles_iter: I)
pub fn spawn_batch<I>(&mut self, bundles_iter: I)
Pushes a Command
to the queue for creating entities with a particular Bundle
type.
bundles_iter
is a type that can be converted into a Bundle
iterator
(it can also be a collection).
This method is equivalent to iterating bundles_iter
and calling spawn
on each bundle,
but it is faster due to memory pre-allocation.
§Example
commands.spawn_batch(vec![
(
Name("Alice".to_string()),
Score(0),
),
(
Name("Bob".to_string()),
Score(0),
),
]);
§See also
spawn
to spawn an entity with a bundle.spawn_empty
to spawn an entity without any components.
Examples found in repository?
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
fn startup_system(mut commands: Commands, mut game_state: ResMut<GameState>) {
// Create our game rules resource
commands.insert_resource(GameRules {
max_rounds: 10,
winning_score: 4,
max_players: 4,
});
// Add some players to our world. Players start with a score of 0 ... we want our game to be
// fair!
commands.spawn_batch(vec![
(
Player {
name: "Alice".to_string(),
},
Score { value: 0 },
),
(
Player {
name: "Bob".to_string(),
},
Score { value: 0 },
),
]);
// set the total players to "2"
game_state.total_players = 2;
}
More examples
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
fn setup(mut commands: Commands, assets: Res<AssetServer>, color_tint: Res<ColorTint>) {
warn!(include_str!("warning_string.txt"));
let mut rng = rand::thread_rng();
let tile_size = Vec2::splat(64.0);
let map_size = Vec2::splat(320.0);
let half_x = (map_size.x / 2.0) as i32;
let half_y = (map_size.y / 2.0) as i32;
let sprite_handle = assets.load("branding/icon.png");
// Spawns the camera
commands.spawn(Camera2dBundle::default());
// Builds and spawns the sprites
let mut sprites = vec![];
for y in -half_y..half_y {
for x in -half_x..half_x {
let position = Vec2::new(x as f32, y as f32);
let translation = (position * tile_size).extend(rng.gen::<f32>());
let rotation = Quat::from_rotation_z(rng.gen::<f32>());
let scale = Vec3::splat(rng.gen::<f32>() * 2.0);
sprites.push(SpriteBundle {
texture: sprite_handle.clone(),
transform: Transform {
translation,
rotation,
scale,
},
sprite: Sprite {
custom_size: Some(tile_size),
color: if color_tint.0 {
COLORS[rng.gen_range(0..3)]
} else {
Color::WHITE
},
..default()
},
..default()
});
}
}
commands.spawn_batch(sprites);
}
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
warn!(include_str!("warning_string.txt"));
const LIGHT_RADIUS: f32 = 0.3;
const LIGHT_INTENSITY: f32 = 1000.0;
const RADIUS: f32 = 50.0;
const N_LIGHTS: usize = 100_000;
commands.spawn(PbrBundle {
mesh: meshes.add(Sphere::new(RADIUS).mesh().ico(9).unwrap()),
material: materials.add(Color::WHITE),
transform: Transform::from_scale(Vec3::NEG_ONE),
..default()
});
let mesh = meshes.add(Cuboid::default());
let material = materials.add(StandardMaterial {
base_color: DEEP_PINK.into(),
..default()
});
// NOTE: This pattern is good for testing performance of culling as it provides roughly
// the same number of visible meshes regardless of the viewing angle.
// NOTE: f64 is used to avoid precision issues that produce visual artifacts in the distribution
let golden_ratio = 0.5f64 * (1.0f64 + 5.0f64.sqrt());
// Spawn N_LIGHTS many lights
commands.spawn_batch((0..N_LIGHTS).map(move |i| {
let mut rng = thread_rng();
let spherical_polar_theta_phi = fibonacci_spiral_on_sphere(golden_ratio, i, N_LIGHTS);
let unit_sphere_p = spherical_polar_to_cartesian(spherical_polar_theta_phi);
PointLightBundle {
point_light: PointLight {
range: LIGHT_RADIUS,
intensity: LIGHT_INTENSITY,
color: Color::hsl(rng.gen_range(0.0..360.0), 1.0, 0.5),
..default()
},
transform: Transform::from_translation((RADIUS as f64 * unit_sphere_p).as_vec3()),
..default()
}
}));
// camera
match std::env::args().nth(1).as_deref() {
Some("orthographic") => commands.spawn(Camera3dBundle {
projection: OrthographicProjection {
scale: 20.0,
scaling_mode: ScalingMode::FixedHorizontal(1.0),
..default()
}
.into(),
..default()
}),
_ => commands.spawn(Camera3dBundle::default()),
};
// add one cube, the only one with strong handles
// also serves as a reference point during rotation
commands.spawn(PbrBundle {
mesh,
material,
transform: Transform {
translation: Vec3::new(0.0, RADIUS, 0.0),
scale: Vec3::splat(5.0),
..default()
},
..default()
});
}
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
// ground plane
commands.spawn((
PbrBundle {
mesh: meshes.add(Plane3d::default().mesh().size(100.0, 100.0)),
material: materials.add(Color::WHITE),
..default()
},
Movable,
));
// cubes
// We're seeding the PRNG here to make this example deterministic for testing purposes.
// This isn't strictly required in practical use unless you need your app to be deterministic.
let mut rng = ChaCha8Rng::seed_from_u64(19878367467713);
let cube_mesh = meshes.add(Cuboid::new(0.5, 0.5, 0.5));
let blue = materials.add(Color::srgb_u8(124, 144, 255));
commands.spawn_batch(
std::iter::repeat_with(move || {
let x = rng.gen_range(-5.0..5.0);
let y = rng.gen_range(0.0..3.0);
let z = rng.gen_range(-5.0..5.0);
(
PbrBundle {
mesh: cube_mesh.clone(),
material: blue.clone(),
transform: Transform::from_xyz(x, y, z),
..default()
},
Movable,
)
})
.take(40),
);
let sphere_mesh = meshes.add(Sphere::new(0.05).mesh().uv(32, 18));
let sphere_mesh_direction = meshes.add(Sphere::new(0.1).mesh().uv(32, 18));
let red_emissive = materials.add(StandardMaterial {
base_color: RED.into(),
emissive: LinearRgba::new(1.0, 0.0, 0.0, 0.0),
..default()
});
let maroon_emissive = materials.add(StandardMaterial {
base_color: MAROON.into(),
emissive: LinearRgba::new(0.369, 0.0, 0.0, 0.0),
..default()
});
for x in 0..4 {
for z in 0..4 {
let x = x as f32 - 2.0;
let z = z as f32 - 2.0;
// red spot_light
commands
.spawn(SpotLightBundle {
transform: Transform::from_xyz(1.0 + x, 2.0, z)
.looking_at(Vec3::new(1.0 + x, 0.0, z), Vec3::X),
spot_light: SpotLight {
intensity: 40_000.0, // lumens
color: Color::WHITE,
shadows_enabled: true,
inner_angle: PI / 4.0 * 0.85,
outer_angle: PI / 4.0,
..default()
},
..default()
})
.with_children(|builder| {
builder.spawn(PbrBundle {
mesh: sphere_mesh.clone(),
material: red_emissive.clone(),
..default()
});
builder.spawn((
PbrBundle {
transform: Transform::from_translation(Vec3::Z * -0.1),
mesh: sphere_mesh_direction.clone(),
material: maroon_emissive.clone(),
..default()
},
NotShadowCaster,
));
});
}
}
// camera
commands.spawn(Camera3dBundle {
camera: Camera {
hdr: true,
..default()
},
transform: Transform::from_xyz(-4.0, 5.0, 10.0).looking_at(Vec3::ZERO, Vec3::Y),
..default()
});
commands.spawn(
TextBundle::from_section(INSTRUCTIONS, TextStyle::default()).with_style(Style {
position_type: PositionType::Absolute,
top: Val::Px(12.0),
left: Val::Px(12.0),
..default()
}),
);
}
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473
fn spawn_birds(
commands: &mut Commands,
args: &Args,
primary_window_resolution: &WindowResolution,
counter: &mut BevyCounter,
spawn_count: usize,
bird_resources: &mut BirdResources,
waves_to_simulate: Option<usize>,
wave: usize,
) {
let bird_x = (primary_window_resolution.width() / -2.) + HALF_BIRD_SIZE;
let bird_y = (primary_window_resolution.height() / 2.) - HALF_BIRD_SIZE;
let half_extents = 0.5 * primary_window_resolution.size();
let color = counter.color;
let current_count = counter.count;
match args.mode {
Mode::Sprite => {
let batch = (0..spawn_count)
.map(|count| {
let bird_z = if args.ordered_z {
(current_count + count) as f32 * 0.00001
} else {
bird_resources.transform_rng.gen::<f32>()
};
let (transform, velocity) = bird_velocity_transform(
half_extents,
Vec3::new(bird_x, bird_y, bird_z),
&mut bird_resources.velocity_rng,
waves_to_simulate,
FIXED_DELTA_TIME,
);
let color = if args.vary_per_instance {
Color::linear_rgb(
bird_resources.color_rng.gen(),
bird_resources.color_rng.gen(),
bird_resources.color_rng.gen(),
)
} else {
color
};
(
SpriteBundle {
texture: bird_resources
.textures
.choose(&mut bird_resources.material_rng)
.unwrap()
.clone(),
transform,
sprite: Sprite { color, ..default() },
..default()
},
Bird { velocity },
)
})
.collect::<Vec<_>>();
commands.spawn_batch(batch);
}
Mode::Mesh2d => {
let batch = (0..spawn_count)
.map(|count| {
let bird_z = if args.ordered_z {
(current_count + count) as f32 * 0.00001
} else {
bird_resources.transform_rng.gen::<f32>()
};
let (transform, velocity) = bird_velocity_transform(
half_extents,
Vec3::new(bird_x, bird_y, bird_z),
&mut bird_resources.velocity_rng,
waves_to_simulate,
FIXED_DELTA_TIME,
);
let material =
if args.vary_per_instance || args.material_texture_count > args.waves {
bird_resources
.materials
.choose(&mut bird_resources.material_rng)
.unwrap()
.clone()
} else {
bird_resources.materials[wave % bird_resources.materials.len()].clone()
};
(
MaterialMesh2dBundle {
mesh: bird_resources.quad.clone(),
material,
transform,
..default()
},
Bird { velocity },
)
})
.collect::<Vec<_>>();
commands.spawn_batch(batch);
}
}
counter.count += spawn_count;
counter.color = Color::linear_rgb(
bird_resources.color_rng.gen(),
bird_resources.color_rng.gen(),
bird_resources.color_rng.gen(),
);
}
sourcepub fn insert_or_spawn_batch<I, B>(&mut self, bundles_iter: I)
pub fn insert_or_spawn_batch<I, B>(&mut self, bundles_iter: I)
Pushes a Command
to the queue for creating entities, if needed,
and for adding a bundle to each entity.
bundles_iter
is a type that can be converted into an (Entity
, Bundle
) iterator
(it can also be a collection).
When the command is applied,
for each (Entity
, Bundle
) pair in the given bundles_iter
,
the Entity
is spawned, if it does not exist already.
Then, the Bundle
is added to the entity.
This method is equivalent to iterating bundles_iter
,
calling get_or_spawn
for each bundle,
and passing it to insert
,
but it is faster due to memory pre-allocation.
§Note
Spawning a specific entity
value is rarely the right choice. Most apps should use Commands::spawn_batch
.
This method should generally only be used for sharing entities across apps, and only when they have a scheme
worked out to share an ID space (which doesn’t happen by default).
Examples found in repository?
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345
pub fn extract_colored_mesh2d(
mut commands: Commands,
mut previous_len: Local<usize>,
// When extracting, you must use `Extract` to mark the `SystemParam`s
// which should be taken from the main world.
query: Extract<
Query<(Entity, &ViewVisibility, &GlobalTransform, &Mesh2dHandle), With<ColoredMesh2d>>,
>,
mut render_mesh_instances: ResMut<RenderColoredMesh2dInstances>,
) {
let mut values = Vec::with_capacity(*previous_len);
for (entity, view_visibility, transform, handle) in &query {
if !view_visibility.get() {
continue;
}
let transforms = Mesh2dTransforms {
world_from_local: (&transform.affine()).into(),
flags: MeshFlags::empty().bits(),
};
values.push((entity, ColoredMesh2d));
render_mesh_instances.insert(
entity,
RenderMesh2dInstance {
mesh_asset_id: handle.0.id(),
transforms,
material_bind_group_id: Material2dBindGroupId::default(),
automatic_batching: false,
},
);
}
*previous_len = values.len();
commands.insert_or_spawn_batch(values);
}
sourcepub fn init_resource<R>(&mut self)
pub fn init_resource<R>(&mut self)
Pushes a Command
to the queue for inserting a Resource
in the World
with an inferred value.
The inferred value is determined by the FromWorld
trait of the resource.
When the command is applied,
if the resource already exists, nothing happens.
See World::init_resource
for more details.
§Example
commands.init_resource::<Scoreboard>();
sourcepub fn insert_resource<R>(&mut self, resource: R)where
R: Resource,
pub fn insert_resource<R>(&mut self, resource: R)where
R: Resource,
Pushes a Command
to the queue for inserting a Resource
in the World
with a specific value.
This will overwrite any previous value of the same resource type.
See World::insert_resource
for more details.
§Example
commands.insert_resource(Scoreboard {
current_score: 0,
high_score: 0,
});
Examples found in repository?
More examples
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn(Camera2dBundle::default());
commands.insert_resource(AnimationState {
min: 128.0,
max: 512.0,
current: 128.0,
speed: 50.0,
});
commands.spawn((
SpriteBundle {
texture: asset_server.load("branding/icon.png"),
..default()
},
ImageScaleMode::Tiled {
tile_x: true,
tile_y: true,
stretch_value: 0.5, // The image will tile every 128px
},
));
}
- examples/audio/soundtrack.rs
- examples/shader/gpu_readback.rs
- examples/shader/array_texture.rs
- examples/ecs/ecs_guide.rs
- examples/games/desk_toy.rs
- examples/async_tasks/external_source_external_thread.rs
- examples/animation/morph_targets.rs
- examples/games/loading_screen.rs
- examples/shader/compute_shader_game_of_life.rs
- examples/ui/font_atlas_debug.rs
- examples/asset/multi_asset_sync.rs
- examples/games/game_menu.rs
- examples/3d/skybox.rs
- examples/state/custom_transitions.rs
- examples/state/states.rs
- examples/state/sub_states.rs
- examples/animation/animation_graph.rs
- examples/3d/motion_blur.rs
- examples/gizmos/axes.rs
- examples/games/contributors.rs
- examples/animation/animated_fox.rs
- examples/math/random_sampling.rs
- examples/transforms/align.rs
- examples/games/alien_cake_addict.rs
- examples/stress_tests/bevymark.rs
- examples/state/computed_states.rs
- examples/3d/auto_exposure.rs
- examples/stress_tests/many_foxes.rs
- examples/games/breakout.rs
- examples/math/sampling_primitives.rs
- examples/3d/lighting.rs
sourcepub fn remove_resource<R>(&mut self)where
R: Resource,
pub fn remove_resource<R>(&mut self)where
R: Resource,
Pushes a Command
to the queue for removing a Resource
from the World
.
See World::remove_resource
for more details.
§Example
commands.remove_resource::<Scoreboard>();
Examples found in repository?
More examples
sourcepub fn run_system(&mut self, id: SystemId)
pub fn run_system(&mut self, id: SystemId)
Runs the system corresponding to the given SystemId
.
Systems are ran in an exclusive and single threaded way.
Running slow systems can become a bottleneck.
Calls World::run_system
.
There is no way to get the output of a system when run as a command, because the
execution of the system happens later. To get the output of a system, use
World::run_system
or World::run_system_with_input
instead of running the system as a command.
Examples found in repository?
More examples
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
fn level_selection(
mut commands: Commands,
keyboard: Res<ButtonInput<KeyCode>>,
level_data: Res<LevelData>,
loading_state: Res<LoadingState>,
) {
// Only trigger a load if the current level is fully loaded.
if let LoadingState::LevelReady = loading_state.as_ref() {
if keyboard.just_pressed(KeyCode::Digit1) {
commands.run_system(level_data.unload_level_id);
commands.run_system(level_data.level_1_id);
} else if keyboard.just_pressed(KeyCode::Digit2) {
commands.run_system(level_data.unload_level_id);
commands.run_system(level_data.level_2_id);
}
}
}
sourcepub fn run_system_with_input<I>(&mut self, id: SystemId<I>, input: I)where
I: 'static + Send,
pub fn run_system_with_input<I>(&mut self, id: SystemId<I>, input: I)where
I: 'static + Send,
Runs the system corresponding to the given SystemId
.
Systems are ran in an exclusive and single threaded way.
Running slow systems can become a bottleneck.
Calls World::run_system_with_input
.
There is no way to get the output of a system when run as a command, because the
execution of the system happens later. To get the output of a system, use
World::run_system
or World::run_system_with_input
instead of running the system as a command.
sourcepub fn register_one_shot_system<I, O, M, S>(
&mut self,
system: S,
) -> SystemId<I, O>
pub fn register_one_shot_system<I, O, M, S>( &mut self, system: S, ) -> SystemId<I, O>
Registers a system and returns a SystemId
so it can later be called by World::run_system
.
It’s possible to register the same systems more than once, they’ll be stored separately.
This is different from adding systems to a Schedule
,
because the SystemId
that is returned can be used anywhere in the World
to run the associated system.
This allows for running systems in a push-based fashion.
Using a Schedule
is still preferred for most cases
due to its better performance and ability to run non-conflicting systems simultaneously.
If you want to prevent Commands from registering the same system multiple times, consider using Local
§Example
#[derive(Resource)]
struct Counter(i32);
fn register_system(mut local_system: Local<Option<SystemId>>, mut commands: Commands) {
if let Some(system) = *local_system {
commands.run_system(system);
} else {
*local_system = Some(commands.register_one_shot_system(increment_counter));
}
}
fn increment_counter(mut value: ResMut<Counter>) {
value.0 += 1;
}
Examples found in repository?
More examples
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
fn setup(mut commands: Commands) {
let level_data = LevelData {
unload_level_id: commands.register_one_shot_system(unload_current_level),
level_1_id: commands.register_one_shot_system(load_level_1),
level_2_id: commands.register_one_shot_system(load_level_2),
};
commands.insert_resource(level_data);
// Spawns the UI that will show the user prompts.
let text_style = TextStyle {
font_size: 50.0,
..default()
};
commands
.spawn(NodeBundle {
background_color: BackgroundColor(Color::NONE),
style: Style {
justify_self: JustifySelf::Center,
align_self: AlignSelf::FlexEnd,
..default()
},
..default()
})
.with_children(|parent| {
parent.spawn(TextBundle::from_section(
"Press 1 or 2 to load a new scene.",
text_style,
));
});
}
sourcepub fn add<C>(&mut self, command: C)where
C: Command,
pub fn add<C>(&mut self, command: C)where
C: Command,
Pushes a generic Command
to the command queue.
command
can be a built-in command, custom struct that implements Command
or a closure
that takes &mut World
as an argument.
§Example
#[derive(Resource, Default)]
struct Counter(u64);
struct AddToCounter(u64);
impl Command for AddToCounter {
fn apply(self, world: &mut World) {
let mut counter = world.get_resource_or_insert_with(Counter::default);
counter.0 += self.0;
}
}
fn add_three_to_counter_system(mut commands: Commands) {
commands.add(AddToCounter(3));
}
fn add_twenty_five_to_counter_system(mut commands: Commands) {
commands.add(|world: &mut World| {
let mut counter = world.get_resource_or_insert_with(Counter::default);
counter.0 += 25;
});
}
sourcepub fn trigger(&mut self, event: impl Event)
pub fn trigger(&mut self, event: impl Event)
Sends a “global” [Trigger
] without any targets. This will run any Observer
of the event
that
isn’t scoped to specific targets.
Examples found in repository?
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
fn explode_mine(trigger: Trigger<Explode>, query: Query<&Mine>, mut commands: Commands) {
// If a triggered event is targeting a specific entity you can access it with `.entity()`
let id = trigger.entity();
let Some(mut entity) = commands.get_entity(id) else {
return;
};
info!("Boom! {:?} exploded.", id.index());
entity.despawn();
let mine = query.get(id).unwrap();
// Trigger another explosion cascade.
commands.trigger(ExplodeMines {
pos: mine.pos,
radius: mine.size,
});
}
// Draw a circle for each mine using `Gizmos`
fn draw_shapes(mut gizmos: Gizmos, mines: Query<&Mine>) {
for mine in &mines {
gizmos.circle_2d(
mine.pos,
mine.size,
Color::hsl((mine.size - 4.0) / 16.0 * 360.0, 1.0, 0.8),
);
}
}
// Trigger `ExplodeMines` at the position of a given click
fn handle_click(
mouse_button_input: Res<ButtonInput<MouseButton>>,
camera: Query<(&Camera, &GlobalTransform)>,
windows: Query<&Window>,
mut commands: Commands,
) {
let (camera, camera_transform) = camera.single();
if let Some(pos) = windows
.single()
.cursor_position()
.and_then(|cursor| camera.viewport_to_world(camera_transform, cursor))
.map(|ray| ray.origin.truncate())
{
if mouse_button_input.just_pressed(MouseButton::Left) {
commands.trigger(ExplodeMines { pos, radius: 1.0 });
}
}
}
sourcepub fn trigger_targets(
&mut self,
event: impl Event,
targets: impl TriggerTargets,
)
pub fn trigger_targets( &mut self, event: impl Event, targets: impl TriggerTargets, )
Sends a [Trigger
] for the given targets. This will run any Observer
of the event
that
watches those targets.
Examples found in repository?
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.init_resource::<SpatialIndex>()
.add_systems(Startup, setup)
.add_systems(Update, (draw_shapes, handle_click))
// Observers are systems that run when an event is "triggered". This observer runs whenever
// `ExplodeMines` is triggered.
.observe(
|trigger: Trigger<ExplodeMines>,
mines: Query<&Mine>,
index: Res<SpatialIndex>,
mut commands: Commands| {
// You can access the trigger data via the `Observer`
let event = trigger.event();
// Access resources
for e in index.get_nearby(event.pos) {
// Run queries
let mine = mines.get(e).unwrap();
if mine.pos.distance(event.pos) < mine.size + event.radius {
// And queue commands, including triggering additional events
// Here we trigger the `Explode` event for entity `e`
commands.trigger_targets(Explode, e);
}
}
},
)
// This observer runs whenever the `Mine` component is added to an entity, and places it in a simple spatial index.
.observe(on_add_mine)
// This observer runs whenever the `Mine` component is removed from an entity (including despawning it)
// and removes it from the spatial index.
.observe(on_remove_mine)
.run();
}
sourcepub fn observe<E, B, M>(
&mut self,
observer: impl IntoObserverSystem<E, B, M>,
) -> EntityCommands<'_>
pub fn observe<E, B, M>( &mut self, observer: impl IntoObserverSystem<E, B, M>, ) -> EntityCommands<'_>
Spawn an Observer
and returns the EntityCommands
associated with the entity that stores the observer.
Trait Implementations§
source§impl SystemParam for Commands<'_, '_>
impl SystemParam for Commands<'_, '_>
source§type Item<'w, 's> = Commands<'w, 's>
type Item<'w, 's> = Commands<'w, 's>
Self
, instantiated with new lifetimes. Read moresource§fn init_state(
world: &mut World,
system_meta: &mut SystemMeta,
) -> <Commands<'_, '_> as SystemParam>::State
fn init_state( world: &mut World, system_meta: &mut SystemMeta, ) -> <Commands<'_, '_> as SystemParam>::State
World
access used by this SystemParam
and creates a new instance of this param’s State
.source§unsafe fn new_archetype(
state: &mut <Commands<'_, '_> as SystemParam>::State,
archetype: &Archetype,
system_meta: &mut SystemMeta,
)
unsafe fn new_archetype( state: &mut <Commands<'_, '_> as SystemParam>::State, archetype: &Archetype, system_meta: &mut SystemMeta, )
Archetype
, registers the components accessed by this SystemParam
(if applicable).a Read moresource§fn apply(
state: &mut <Commands<'_, '_> as SystemParam>::State,
system_meta: &SystemMeta,
world: &mut World,
)
fn apply( state: &mut <Commands<'_, '_> as SystemParam>::State, system_meta: &SystemMeta, world: &mut World, )
SystemParam
’s state.
This is used to apply Commands
during apply_deferred
.source§fn queue(
state: &mut <Commands<'_, '_> as SystemParam>::State,
system_meta: &SystemMeta,
world: DeferredWorld<'_>,
)
fn queue( state: &mut <Commands<'_, '_> as SystemParam>::State, system_meta: &SystemMeta, world: DeferredWorld<'_>, )
apply_deferred
.source§unsafe fn get_param<'w, 's>(
state: &'s mut <Commands<'_, '_> as SystemParam>::State,
system_meta: &SystemMeta,
world: UnsafeWorldCell<'w>,
change_tick: Tick,
) -> <Commands<'_, '_> as SystemParam>::Item<'w, 's>
unsafe fn get_param<'w, 's>( state: &'s mut <Commands<'_, '_> as SystemParam>::State, system_meta: &SystemMeta, world: UnsafeWorldCell<'w>, change_tick: Tick, ) -> <Commands<'_, '_> as SystemParam>::Item<'w, 's>
SystemParamFunction
. Read moreimpl<'w, 's> ReadOnlySystemParam for Commands<'w, 's>
impl Send for Commands<'_, '_>
impl Sync for Commands<'_, '_>
Auto Trait Implementations§
impl<'w, 's> Freeze for Commands<'w, 's>
impl<'w, 's> RefUnwindSafe for Commands<'w, 's>
impl<'w, 's> Unpin for Commands<'w, 's>
impl<'w, 's> !UnwindSafe for Commands<'w, 's>
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>
. 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> DowncastSync for T
impl<T> DowncastSync for T
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 more