-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Show off different methods for spawning related entities in examples/ecs/hierarchy.rs
#20904
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
db28e1e
c685c9a
8ce671a
9ecf0d6
ec5078f
11f7fc6
62edb85
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,27 +4,102 @@ | |
//! [`Transform`] and [`Visibility`] from parents to children down the hierarchy, | ||
//! resulting in a final [`GlobalTransform`] and [`InheritedVisibility`] component for each entity. | ||
|
||
use std::f32::consts::*; | ||
use std::{f32::consts::*, time::Duration}; | ||
|
||
use bevy::{color::palettes::css::*, prelude::*}; | ||
use bevy::{color::palettes::css::*, ecs::relationship::RelatedSpawner, prelude::*}; | ||
|
||
fn main() { | ||
App::new() | ||
.add_plugins(DefaultPlugins) | ||
.add_systems(Startup, setup) | ||
.add_systems(Update, rotate) | ||
.init_state::<Showcase>() | ||
.insert_resource(Delta(Duration::ZERO)) | ||
.add_systems(OnEnter(Showcase::WithChildren), setup_with_children) | ||
.add_systems(OnEnter(Showcase::ChildrenSpawn), setup_children_spawn) | ||
.add_systems(OnEnter(Showcase::ChildrenMacro), spawn_children_macro) | ||
.add_systems(OnEnter(Showcase::ChildrenIter), setup_children_iter) | ||
.add_systems(OnEnter(Showcase::Related), setup_children_related) | ||
.add_systems(Update, (rotate, switch_scene)) | ||
.run(); | ||
} | ||
|
||
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) { | ||
#[derive(Debug, Clone, Eq, PartialEq, Hash, States, Default)] | ||
enum Showcase { | ||
#[default] | ||
WithChildren, | ||
ChildrenSpawn, | ||
ChildrenMacro, | ||
ChildrenIter, | ||
Related, | ||
} | ||
|
||
impl Showcase { | ||
fn next(&self) -> Self { | ||
match self { | ||
Showcase::WithChildren => Showcase::ChildrenSpawn, | ||
Showcase::ChildrenSpawn => Showcase::ChildrenMacro, | ||
Showcase::ChildrenMacro => Showcase::ChildrenIter, | ||
Showcase::ChildrenIter => Showcase::Related, | ||
Showcase::Related => Showcase::WithChildren, | ||
} | ||
} | ||
} | ||
|
||
fn switch_scene( | ||
keyboard: Res<ButtonInput<KeyCode>>, | ||
scene: Res<State<Showcase>>, | ||
mut next_scene: ResMut<NextState<Showcase>>, | ||
) { | ||
if keyboard.just_pressed(KeyCode::Space) { | ||
info!("Switching scene"); | ||
next_scene.set(scene.get().next()); | ||
} | ||
} | ||
|
||
fn setup(mut commands: Commands) { | ||
commands.spawn(Camera2d); | ||
} | ||
|
||
#[derive(Resource)] | ||
struct Delta(Duration); | ||
|
||
fn setup_common( | ||
commands: &mut Commands, | ||
time: &Res<Time>, | ||
delta: &mut ResMut<Delta>, | ||
title: &str, | ||
stage: Showcase, | ||
) { | ||
delta.0 = time.elapsed(); | ||
commands.spawn(( | ||
Text::new(title), | ||
TextFont::from_font_size(36.), | ||
DespawnOnExit(stage), | ||
)); | ||
} | ||
|
||
fn setup_with_children( | ||
mut commands: Commands, | ||
asset_server: Res<AssetServer>, | ||
time: Res<Time>, | ||
mut delta: ResMut<Delta>, | ||
) { | ||
let texture = asset_server.load("branding/icon.png"); | ||
|
||
setup_common( | ||
&mut commands, | ||
&time, | ||
&mut delta, | ||
"with_children()\nPress Space to continue", | ||
Showcase::WithChildren, | ||
); | ||
|
||
// Spawn a root entity with no parent | ||
let parent = commands | ||
.spawn(( | ||
Sprite::from_image(texture.clone()), | ||
Transform::from_scale(Vec3::splat(0.75)), | ||
DespawnOnExit(Showcase::WithChildren), | ||
)) | ||
// With that entity as a parent, run a lambda that spawns its children | ||
.with_children(|parent| { | ||
|
@@ -46,7 +121,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) { | |
let child = commands | ||
.spawn(( | ||
Sprite { | ||
image: texture, | ||
image: texture.clone(), | ||
color: LIME.into(), | ||
..default() | ||
}, | ||
|
@@ -58,10 +133,189 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) { | |
commands.entity(parent).add_child(child); | ||
} | ||
|
||
fn setup_children_spawn( | ||
mut commands: Commands, | ||
asset_server: Res<AssetServer>, | ||
time: Res<Time>, | ||
mut delta: ResMut<Delta>, | ||
) { | ||
let texture = asset_server.load("branding/icon.png"); | ||
|
||
setup_common( | ||
&mut commands, | ||
&time, | ||
&mut delta, | ||
"Children::spawn() \nPress Space to continue", | ||
Showcase::ChildrenSpawn, | ||
); | ||
|
||
// Children can also be spawned using the `Children` component as part of the parent's bundle. | ||
commands.spawn(( | ||
Sprite::from_image(texture.clone()), | ||
Transform::from_scale(Vec3::splat(0.75)), | ||
DespawnOnExit(Showcase::ChildrenSpawn), | ||
Children::spawn(( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure I would showcase the use of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh yeah I forgot about that. That's a good point. |
||
Spawn(( | ||
Transform::from_xyz(250.0, 0.0, 0.0).with_scale(Vec3::splat(0.75)), | ||
Sprite { | ||
image: texture.clone(), | ||
color: BLUE.into(), | ||
..default() | ||
}, | ||
)), | ||
// since they have to be explicitly created, `Children::spawn` can | ||
// mix implementers of `SpawnableList` while `children!` cannot. | ||
SpawnWith(|spawner: &mut RelatedSpawner<'_, ChildOf>| { | ||
spawner.spawn(( | ||
Transform::from_xyz(0.0, 250.0, 0.0).with_scale(Vec3::splat(0.75)), | ||
Sprite { | ||
image: texture, | ||
color: LIME.into(), | ||
..default() | ||
}, | ||
)); | ||
}), | ||
)), | ||
)); | ||
} | ||
|
||
fn spawn_children_macro( | ||
mut commands: Commands, | ||
asset_server: Res<AssetServer>, | ||
time: Res<Time>, | ||
mut delta: ResMut<Delta>, | ||
) { | ||
let texture = asset_server.load("branding/icon.png"); | ||
|
||
setup_common( | ||
&mut commands, | ||
&time, | ||
&mut delta, | ||
"children!() \nPress Space to continue", | ||
Showcase::ChildrenMacro, | ||
); | ||
|
||
// The `children!` macro provides a convenient way to define children inline with their parent. | ||
commands.spawn(( | ||
Sprite::from_image(texture.clone()), | ||
Transform::from_scale(Vec3::splat(0.75)), | ||
DespawnOnExit(Showcase::ChildrenMacro), | ||
children![ | ||
( | ||
Transform::from_xyz(250.0, 0.0, 0.0).with_scale(Vec3::splat(0.75)), | ||
Sprite { | ||
image: texture.clone(), | ||
color: BLUE.into(), | ||
..default() | ||
}, | ||
), | ||
( | ||
Transform::from_xyz(0.0, 250.0, 0.0).with_scale(Vec3::splat(0.75)), | ||
Sprite { | ||
image: texture, | ||
color: LIME.into(), | ||
..default() | ||
}, | ||
) | ||
], | ||
)); | ||
} | ||
|
||
fn setup_children_iter( | ||
mut commands: Commands, | ||
asset_server: Res<AssetServer>, | ||
time: Res<Time>, | ||
mut delta: ResMut<Delta>, | ||
) { | ||
let texture = asset_server.load("branding/icon.png"); | ||
|
||
setup_common( | ||
&mut commands, | ||
&time, | ||
&mut delta, | ||
"SpawnIter() \nPress Space to continue", | ||
Showcase::ChildrenIter, | ||
); | ||
|
||
// You can also spawn children from an iterator yielding bundles. | ||
let child_components = [ | ||
( | ||
Transform::from_xyz(250.0, 0.0, 0.0).with_scale(Vec3::splat(0.75)), | ||
BLUE, | ||
), | ||
( | ||
Transform::from_xyz(0.0, 250.0, 0.0).with_scale(Vec3::splat(0.75)), | ||
LIME, | ||
), | ||
]; | ||
|
||
commands.spawn(( | ||
Sprite::from_image(texture.clone()), | ||
Transform::from_scale(Vec3::splat(0.75)), | ||
DespawnOnExit(Showcase::ChildrenIter), | ||
Children::spawn(SpawnIter(child_components.into_iter().map( | ||
move |(transform, color)| { | ||
( | ||
transform, | ||
Sprite { | ||
image: texture.clone(), | ||
color: color.into(), | ||
..default() | ||
}, | ||
) | ||
}, | ||
))), | ||
)); | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe showcase |
||
fn setup_children_related( | ||
mut commands: Commands, | ||
asset_server: Res<AssetServer>, | ||
time: Res<Time>, | ||
mut delta: ResMut<Delta>, | ||
) { | ||
let texture = asset_server.load("branding/icon.png"); | ||
|
||
setup_common( | ||
&mut commands, | ||
&time, | ||
&mut delta, | ||
"related!() \nPress Space to continue", | ||
Showcase::Related, | ||
); | ||
|
||
// You can also spawn entities with relationships other than parent/child. | ||
commands.spawn(( | ||
Sprite::from_image(texture.clone()), | ||
Transform::from_scale(Vec3::splat(0.75)), | ||
DespawnOnExit(Showcase::Related), | ||
// the `related!` macro will spawn entities according to the `Children: RelationshipTarget` trait, but other types implementing `RelationshipTarget` can be used as well. | ||
related!(Children[ | ||
( | ||
Transform::from_xyz(250.0, 0.0, 0.0).with_scale(Vec3::splat(0.75)), | ||
Sprite { | ||
image: texture.clone(), | ||
color: BLUE.into(), | ||
..default() | ||
}, | ||
), | ||
( | ||
Transform::from_xyz(0.0, 250.0, 0.0).with_scale(Vec3::splat(0.75)), | ||
Sprite { | ||
image: texture, | ||
color: LIME.into(), | ||
..default() | ||
}, | ||
) | ||
]), | ||
)); | ||
} | ||
|
||
// A simple system to rotate the root entity, and rotate all its children separately | ||
fn rotate( | ||
mut commands: Commands, | ||
time: Res<Time>, | ||
delta: Res<Delta>, | ||
mut parents_query: Query<(Entity, &Children), With<Sprite>>, | ||
mut transform_query: Query<&mut Transform, With<Sprite>>, | ||
) { | ||
|
@@ -79,12 +333,13 @@ fn rotate( | |
} | ||
|
||
// To demonstrate removing children, we'll remove a child after a couple of seconds. | ||
if time.elapsed_secs() >= 2.0 && children.len() == 2 { | ||
let elapsed = time.elapsed_secs() - delta.0.as_secs_f32(); | ||
if elapsed >= 2.0 && children.len() == 2 { | ||
let child = children.last().unwrap(); | ||
commands.entity(*child).despawn(); | ||
} | ||
|
||
if time.elapsed_secs() >= 4.0 { | ||
if elapsed >= 4.0 { | ||
// This will remove the entity from its parent's list of children, as well as despawn | ||
// any children the entity has. | ||
commands.entity(parent).despawn(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a fan of this function. I would rather copy paste its body.