add a SceneBundle to spawn a scene (#2424)

# Objective

- Spawning a scene is handled as a special case with a command `spawn_scene` that takes an handle but doesn't let you specify anything else. This is the only handle that works that way.
- Workaround for this have been to add the `spawn_scene` on `ChildBuilder` to be able to specify transform of parent, or to make the `SceneSpawner` available to be able to select entities from a scene by their instance id

## Solution

Add a bundle
```rust
pub struct SceneBundle {
    pub scene: Handle<Scene>,
    pub transform: Transform,
    pub global_transform: GlobalTransform,
    pub instance_id: Option<InstanceId>,
}
```

and instead of 
```rust
commands.spawn_scene(asset_server.load("models/FlightHelmet/FlightHelmet.gltf#Scene0"));
```
you can do
```rust
commands.spawn_bundle(SceneBundle {
    scene: asset_server.load("models/FlightHelmet/FlightHelmet.gltf#Scene0"),
    ..Default::default()
});
```

The scene will be spawned as a child of the entity with the `SceneBundle`

~I would like to remove the command `spawn_scene` in favor of this bundle but didn't do it yet to get feedback first~

Co-authored-by: François <8672791+mockersf@users.noreply.github.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
This commit is contained in:
François 2022-06-09 20:34:09 +00:00
parent cdb62af4bf
commit c6958b3056
18 changed files with 264 additions and 189 deletions

View file

@ -25,7 +25,11 @@ impl FromWorld for Parent {
impl MapEntities for Parent {
fn map_entities(&mut self, entity_map: &EntityMap) -> Result<(), MapEntitiesError> {
self.0 = entity_map.get(self.0)?;
// Parent of an entity in the new world can be in outside world, in which case it
// should not be mapped.
if let Ok(mapped_entity) = entity_map.get(self.0) {
self.0 = mapped_entity;
}
Ok(())
}
}
@ -51,7 +55,11 @@ pub struct PreviousParent(pub(crate) Entity);
impl MapEntities for PreviousParent {
fn map_entities(&mut self, entity_map: &EntityMap) -> Result<(), MapEntitiesError> {
self.0 = entity_map.get(self.0)?;
// PreviousParent of an entity in the new world can be in outside world, in which
// case it should not be mapped.
if let Ok(mapped_entity) = entity_map.get(self.0) {
self.0 = mapped_entity;
}
Ok(())
}
}

View file

@ -12,9 +12,11 @@ keywords = ["bevy"]
# bevy
bevy_app = { path = "../bevy_app", version = "0.8.0-dev" }
bevy_asset = { path = "../bevy_asset", version = "0.8.0-dev" }
bevy_derive = { path = "../bevy_derive", version = "0.8.0-dev" }
bevy_ecs = { path = "../bevy_ecs", version = "0.8.0-dev" }
bevy_reflect = { path = "../bevy_reflect", version = "0.8.0-dev", features = ["bevy"] }
bevy_hierarchy = { path = "../bevy_hierarchy", version = "0.8.0-dev" }
bevy_transform = { path = "../bevy_transform", version = "0.8.0-dev" }
bevy_utils = { path = "../bevy_utils", version = "0.8.0-dev" }
# other

View file

@ -0,0 +1,74 @@
use bevy_asset::Handle;
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{
bundle::Bundle,
change_detection::ResMut,
entity::Entity,
prelude::{Changed, Component, Without},
system::{Commands, Query},
};
use bevy_transform::components::{GlobalTransform, Transform};
use crate::{DynamicScene, InstanceId, Scene, SceneSpawner};
/// [`InstanceId`] of a spawned scene. It can be used with the [`SceneSpawner`] to
/// interact with the spawned scene.
#[derive(Component, Deref, DerefMut)]
pub struct SceneInstance(InstanceId);
/// A component bundle for a [`Scene`] root.
///
/// The scene from `scene` will be spawn as a child of the entity with this component.
/// Once it's spawned, the entity will have a [`SceneInstance`] component.
#[derive(Default, Bundle)]
pub struct SceneBundle {
/// Handle to the scene to spawn
pub scene: Handle<Scene>,
pub transform: Transform,
pub global_transform: GlobalTransform,
}
/// A component bundle for a [`DynamicScene`] root.
///
/// The dynamic scene from `scene` will be spawn as a child of the entity with this component.
/// Once it's spawned, the entity will have a [`SceneInstance`] component.
#[derive(Default, Bundle)]
pub struct DynamicSceneBundle {
/// Handle to the scene to spawn
pub scene: Handle<DynamicScene>,
pub transform: Transform,
pub global_transform: GlobalTransform,
}
/// System that will spawn scenes from [`SceneBundle`].
pub fn scene_spawner(
mut commands: Commands,
mut scene_to_spawn: Query<
(Entity, &Handle<Scene>, Option<&mut SceneInstance>),
(Changed<Handle<Scene>>, Without<Handle<DynamicScene>>),
>,
mut dynamic_scene_to_spawn: Query<
(Entity, &Handle<DynamicScene>, Option<&mut SceneInstance>),
(Changed<Handle<DynamicScene>>, Without<Handle<Scene>>),
>,
mut scene_spawner: ResMut<SceneSpawner>,
) {
for (entity, scene, instance) in scene_to_spawn.iter_mut() {
let new_instance = scene_spawner.spawn_as_child(scene.clone(), entity);
if let Some(mut old_instance) = instance {
scene_spawner.despawn_instance(**old_instance);
*old_instance = SceneInstance(new_instance);
} else {
commands.entity(entity).insert(SceneInstance(new_instance));
}
}
for (entity, dynamic_scene, instance) in dynamic_scene_to_spawn.iter_mut() {
let new_instance = scene_spawner.spawn_dynamic_as_child(dynamic_scene.clone(), entity);
if let Some(mut old_instance) = instance {
scene_spawner.despawn_instance(**old_instance);
*old_instance = SceneInstance(new_instance);
} else {
commands.entity(entity).insert(SceneInstance(new_instance));
}
}
}

View file

@ -1,56 +0,0 @@
use bevy_asset::Handle;
use bevy_ecs::{
entity::Entity,
system::{Command, Commands},
world::World,
};
use bevy_hierarchy::ChildBuilder;
use crate::{Scene, SceneSpawner};
pub struct SpawnScene {
scene_handle: Handle<Scene>,
}
impl Command for SpawnScene {
fn write(self, world: &mut World) {
let mut spawner = world.resource_mut::<SceneSpawner>();
spawner.spawn(self.scene_handle);
}
}
pub trait SpawnSceneCommands {
fn spawn_scene(&mut self, scene: Handle<Scene>);
}
impl<'w, 's> SpawnSceneCommands for Commands<'w, 's> {
fn spawn_scene(&mut self, scene_handle: Handle<Scene>) {
self.add(SpawnScene { scene_handle });
}
}
pub struct SpawnSceneAsChild {
scene_handle: Handle<Scene>,
parent: Entity,
}
impl Command for SpawnSceneAsChild {
fn write(self, world: &mut World) {
let mut spawner = world.resource_mut::<SceneSpawner>();
spawner.spawn_as_child(self.scene_handle, self.parent);
}
}
pub trait SpawnSceneAsChildCommands {
fn spawn_scene(&mut self, scene: Handle<Scene>) -> &mut Self;
}
impl<'w, 's, 'a> SpawnSceneAsChildCommands for ChildBuilder<'w, 's, 'a> {
fn spawn_scene(&mut self, scene_handle: Handle<Scene>) -> &mut Self {
self.add_command(SpawnSceneAsChild {
scene_handle,
parent: self.parent_entity(),
});
self
}
}

View file

@ -9,6 +9,12 @@ use bevy_reflect::{Reflect, TypeRegistryArc, TypeUuid};
use serde::Serialize;
/// A collection of serializable dynamic entities, each with its own run-time defined set of components.
/// To spawn a dynamic scene, you can use either:
/// * [`SceneSpawner::spawn_dynamic`](crate::SceneSpawner::spawn_dynamic)
/// * adding the [`DynamicSceneBundle`](crate::DynamicSceneBundle) to an entity
/// * adding the [`Handle<DynamicScene>`](bevy_asset::Handle) to an entity (the scene will only be
/// visible if the entity already has [`Transform`](bevy_transform::components::Transform) and
/// [`GlobalTransform`](bevy_transform::components::GlobalTransform) components)
#[derive(Default, TypeUuid)]
#[uuid = "749479b1-fb8c-4ff8-a775-623aa76014f5"]
pub struct DynamicScene {

View file

@ -1,11 +1,11 @@
mod command;
mod bundle;
mod dynamic_scene;
mod scene;
mod scene_loader;
mod scene_spawner;
pub mod serde;
pub use command::*;
pub use bundle::*;
pub use dynamic_scene::*;
pub use scene::*;
pub use scene_loader::*;
@ -13,9 +13,7 @@ pub use scene_spawner::*;
pub mod prelude {
#[doc(hidden)]
pub use crate::{
DynamicScene, Scene, SceneSpawner, SpawnSceneAsChildCommands, SpawnSceneCommands,
};
pub use crate::{DynamicScene, DynamicSceneBundle, Scene, SceneBundle, SceneSpawner};
}
use bevy_app::prelude::*;
@ -34,6 +32,8 @@ impl Plugin for ScenePlugin {
.add_system_to_stage(
CoreStage::PreUpdate,
scene_spawner_system.exclusive_system().at_end(),
);
)
// Systems `*_bundle_spawner` must run before `scene_spawner_system`
.add_system_to_stage(CoreStage::PreUpdate, scene_spawner);
}
}

View file

@ -1,6 +1,12 @@
use bevy_ecs::world::World;
use bevy_reflect::TypeUuid;
/// To spawn a scene, you can use either:
/// * [`SceneSpawner::spawn`](crate::SceneSpawner::spawn)
/// * adding the [`SceneBundle`](crate::SceneBundle) to an entity
/// * adding the [`Handle<Scene>`](bevy_asset::Handle) to an entity (the scene will only be
/// visible if the entity already has [`Transform`](bevy_transform::components::Transform) and
/// [`GlobalTransform`](bevy_transform::components::GlobalTransform) components)
#[derive(Debug, TypeUuid)]
#[uuid = "c156503c-edd9-4ec7-8d33-dab392df03cd"]
pub struct Scene {

View file

@ -33,9 +33,10 @@ pub struct SceneSpawner {
spawned_dynamic_scenes: HashMap<Handle<DynamicScene>, Vec<InstanceId>>,
spawned_instances: HashMap<InstanceId, InstanceInfo>,
scene_asset_event_reader: ManualEventReader<AssetEvent<DynamicScene>>,
dynamic_scenes_to_spawn: Vec<Handle<DynamicScene>>,
dynamic_scenes_to_spawn: Vec<(Handle<DynamicScene>, InstanceId)>,
scenes_to_spawn: Vec<(Handle<Scene>, InstanceId)>,
scenes_to_despawn: Vec<Handle<DynamicScene>>,
instances_to_despawn: Vec<InstanceId>,
scenes_with_parent: Vec<(InstanceId, Entity)>,
}
@ -53,7 +54,21 @@ pub enum SceneSpawnError {
impl SceneSpawner {
pub fn spawn_dynamic(&mut self, scene_handle: Handle<DynamicScene>) {
self.dynamic_scenes_to_spawn.push(scene_handle);
let instance_id = InstanceId::new();
self.dynamic_scenes_to_spawn
.push((scene_handle, instance_id));
}
pub fn spawn_dynamic_as_child(
&mut self,
scene_handle: Handle<DynamicScene>,
parent: Entity,
) -> InstanceId {
let instance_id = InstanceId::new();
self.dynamic_scenes_to_spawn
.push((scene_handle, instance_id));
self.scenes_with_parent.push((instance_id, parent));
instance_id
}
pub fn spawn(&mut self, scene_handle: Handle<Scene>) -> InstanceId {
@ -73,26 +88,31 @@ impl SceneSpawner {
self.scenes_to_despawn.push(scene_handle);
}
pub fn despawn_instance(&mut self, instance_id: InstanceId) {
self.instances_to_despawn.push(instance_id);
}
pub fn despawn_sync(
&mut self,
world: &mut World,
scene_handle: Handle<DynamicScene>,
) -> Result<(), SceneSpawnError> {
if let Some(instance_ids) = self.spawned_dynamic_scenes.get(&scene_handle) {
if let Some(instance_ids) = self.spawned_dynamic_scenes.remove(&scene_handle) {
for instance_id in instance_ids {
if let Some(instance) = self.spawned_instances.get(instance_id) {
for entity in instance.entity_map.values() {
let _ = world.despawn(entity); // Ignore the result, despawn only cares if
// it exists.
}
}
self.despawn_instance_sync(world, &instance_id);
}
self.spawned_dynamic_scenes.remove(&scene_handle);
}
Ok(())
}
pub fn despawn_instance_sync(&mut self, world: &mut World, instance_id: &InstanceId) {
if let Some(instance) = self.spawned_instances.remove(instance_id) {
for entity in instance.entity_map.values() {
let _ = world.despawn(entity);
}
}
}
pub fn spawn_dynamic_sync(
&mut self,
world: &mut World,
@ -235,14 +255,33 @@ impl SceneSpawner {
Ok(())
}
pub fn despawn_queued_instances(&mut self, world: &mut World) {
let instances_to_despawn = std::mem::take(&mut self.instances_to_despawn);
for instance_id in instances_to_despawn {
self.despawn_instance_sync(world, &instance_id);
}
}
pub fn spawn_queued_scenes(&mut self, world: &mut World) -> Result<(), SceneSpawnError> {
let scenes_to_spawn = std::mem::take(&mut self.dynamic_scenes_to_spawn);
for scene_handle in scenes_to_spawn {
match self.spawn_dynamic_sync(world, &scene_handle) {
Ok(_) => {}
for (scene_handle, instance_id) in scenes_to_spawn {
let mut entity_map = EntityMap::default();
match Self::spawn_dynamic_internal(world, &scene_handle, &mut entity_map) {
Ok(_) => {
self.spawned_instances
.insert(instance_id, InstanceInfo { entity_map });
let spawned = self
.spawned_dynamic_scenes
.entry(scene_handle.clone())
.or_insert_with(Vec::new);
spawned.push(instance_id);
}
Err(SceneSpawnError::NonExistentScene { .. }) => {
self.dynamic_scenes_to_spawn.push(scene_handle);
self.dynamic_scenes_to_spawn
.push((scene_handle, instance_id));
}
Err(err) => return Err(err),
}
@ -327,6 +366,7 @@ pub fn scene_spawner_system(world: &mut World) {
}
scene_spawner.despawn_queued_scenes(world).unwrap();
scene_spawner.despawn_queued_instances(world);
scene_spawner
.spawn_queued_scenes(world)
.unwrap_or_else(|err| panic!("{}", err));

View file

@ -15,7 +15,6 @@ fn main() {
}
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn_scene(asset_server.load("models/FlightHelmet/FlightHelmet.gltf#Scene0"));
commands.spawn_bundle(Camera3dBundle {
transform: Transform::from_xyz(0.7, 0.7, 1.0).looking_at(Vec3::new(0.0, 0.3, 0.0), Vec3::Y),
..default()
@ -37,6 +36,10 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
},
..default()
});
commands.spawn_bundle(SceneBundle {
scene: asset_server.load("models/FlightHelmet/FlightHelmet.gltf#Scene0"),
..default()
});
}
fn animate_light_direction(

View file

@ -29,7 +29,10 @@ fn setup(
..default()
});
commands.spawn_scene(asset_server.load("models/animated/Fox.glb#Scene0"));
commands.spawn_bundle(SceneBundle {
scene: asset_server.load("models/animated/Fox.glb#Scene0"),
..default()
});
// Light
commands.spawn_bundle(DirectionalLightBundle {

View file

@ -1,32 +1,20 @@
//! Update a scene from a glTF file, either by spawning the scene as a child of another entity,
//! or by accessing the entities of the scene.
use bevy::{prelude::*, scene::InstanceId};
use bevy::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.init_resource::<SceneInstance>()
.add_startup_system(setup)
.add_system(scene_update)
.add_system(move_scene_entities)
.run();
}
// Resource to hold the scene `instance_id` until it is loaded
#[derive(Default)]
struct SceneInstance(Option<InstanceId>);
// Component that will be used to tag entities in the scene
#[derive(Component)]
struct EntityInMyScene;
struct MovedScene;
fn setup(
mut commands: Commands,
asset_server: Res<AssetServer>,
mut scene_spawner: ResMut<SceneSpawner>,
mut scene_instance: ResMut<SceneInstance>,
) {
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn_bundle(PointLightBundle {
transform: Transform::from_xyz(4.0, 5.0, 4.0),
..default()
@ -37,56 +25,49 @@ fn setup(
..default()
});
// Spawn the scene as a child of another entity. This first scene will be translated backward
// with its parent
commands
.spawn_bundle(TransformBundle::from(Transform::from_xyz(0.0, 0.0, -1.0)))
.with_children(|parent| {
parent.spawn_scene(asset_server.load("models/FlightHelmet/FlightHelmet.gltf#Scene0"));
});
// Spawn the scene as a child of this entity at the given transform
commands.spawn_bundle(SceneBundle {
transform: Transform::from_xyz(0.0, 0.0, -1.0),
scene: asset_server.load("models/FlightHelmet/FlightHelmet.gltf#Scene0"),
..default()
});
// Spawn a second scene, and keep its `instance_id`
let instance_id =
scene_spawner.spawn(asset_server.load("models/FlightHelmet/FlightHelmet.gltf#Scene0"));
scene_instance.0 = Some(instance_id);
// Spawn a second scene, and add a tag component to be able to target it later
commands
.spawn_bundle(SceneBundle {
scene: asset_server.load("models/FlightHelmet/FlightHelmet.gltf#Scene0"),
..default()
})
.insert(MovedScene);
}
// This system will wait for the scene to be ready, and then tag entities from
// the scene with `EntityInMyScene`. All entities from the second scene will be
// tagged
fn scene_update(
mut commands: Commands,
scene_spawner: Res<SceneSpawner>,
scene_instance: Res<SceneInstance>,
mut done: Local<bool>,
// This system will move all entities that are descendants of MovedScene (which will be all entities spawned in the scene)
fn move_scene_entities(
time: Res<Time>,
moved_scene: Query<Entity, With<MovedScene>>,
children: Query<&Children>,
mut transforms: Query<&mut Transform>,
) {
if !*done {
if let Some(instance_id) = scene_instance.0 {
if let Some(entity_iter) = scene_spawner.iter_instance_entities(instance_id) {
entity_iter.for_each(|entity| {
commands.entity(entity).insert(EntityInMyScene);
});
*done = true;
for moved_scene_entity in moved_scene.iter() {
let mut offset = 0.;
iter_hierarchy(moved_scene_entity, &children, &mut |entity| {
if let Ok(mut transform) = transforms.get_mut(entity) {
transform.translation = Vec3::new(
offset * time.seconds_since_startup().sin() as f32 / 20.,
0.,
time.seconds_since_startup().cos() as f32 / 20.,
);
offset += 1.0;
}
});
}
}
fn iter_hierarchy(entity: Entity, children_query: &Query<&Children>, f: &mut impl FnMut(Entity)) {
(f)(entity);
if let Ok(children) = children_query.get(entity) {
for child in children.iter().copied() {
iter_hierarchy(child, children_query, f);
}
}
}
// This system will move all entities with component `EntityInMyScene`, so all
// entities from the second scene
fn move_scene_entities(
time: Res<Time>,
mut scene_entities: Query<&mut Transform, With<EntityInMyScene>>,
) {
let mut direction = 1.;
let mut scale = 1.;
for mut transform in scene_entities.iter_mut() {
transform.translation = Vec3::new(
scale * direction * time.seconds_since_startup().sin() as f32 / 20.,
0.,
time.seconds_since_startup().cos() as f32 / 20.,
);
direction *= -1.;
scale += 0.5;
}
}

View file

@ -20,7 +20,6 @@ struct Animations(Vec<Handle<AnimationClip>>);
fn setup(
mut commands: Commands,
asset_server: Res<AssetServer>,
mut scene_spawner: ResMut<SceneSpawner>,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
@ -61,7 +60,10 @@ fn setup(
});
// Fox
scene_spawner.spawn(asset_server.load("models/animated/Fox.glb#Scene0"));
commands.spawn_bundle(SceneBundle {
scene: asset_server.load("models/animated/Fox.glb#Scene0"),
..default()
});
println!("Animation controls:");
println!(" - spacebar: play / pause");

View file

@ -25,7 +25,10 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
});
// Spawn the first scene in `models/SimpleSkin/SimpleSkin.gltf`
commands.spawn_scene(asset_server.load::<Scene, _>("models/SimpleSkin/SimpleSkin.gltf#Scene0"));
commands.spawn_bundle(SceneBundle {
scene: asset_server.load("models/SimpleSkin/SimpleSkin.gltf#Scene0"),
..default()
});
}
/// The scene hierarchy currently looks somewhat like this:

View file

@ -24,7 +24,10 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
// You should see the changes immediately show up in your app.
// mesh
commands.spawn_scene(scene_handle);
commands.spawn_bundle(SceneBundle {
scene: scene_handle,
..default()
});
// light
commands.spawn_bundle(PointLightBundle {
transform: Transform::from_xyz(4.0, 5.0, 4.0),

View file

@ -116,15 +116,11 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>, mut game: ResMu
(0..BOARD_SIZE_I)
.map(|i| {
let height = rand::thread_rng().gen_range(-0.1..0.1);
commands
.spawn_bundle(TransformBundle::from(Transform::from_xyz(
i as f32,
height - 0.2,
j as f32,
)))
.with_children(|cell| {
cell.spawn_scene(cell_scene.clone());
});
commands.spawn_bundle(SceneBundle {
transform: Transform::from_xyz(i as f32, height - 0.2, j as f32),
scene: cell_scene.clone(),
..default()
});
Cell { height }
})
.collect()
@ -134,17 +130,18 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>, mut game: ResMu
// spawn the game character
game.player.entity = Some(
commands
.spawn_bundle(TransformBundle::from(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(-std::f32::consts::FRAC_PI_2),
.spawn_bundle(SceneBundle {
transform: 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(-std::f32::consts::FRAC_PI_2),
..default()
},
scene: asset_server.load("models/AlienCake/alien.glb#Scene0"),
..default()
}))
.with_children(|cell| {
cell.spawn_scene(asset_server.load("models/AlienCake/alien.glb#Scene0"));
})
.id(),
);
@ -322,11 +319,15 @@ fn spawn_bonus(
}
game.bonus.entity = Some(
commands
.spawn_bundle(TransformBundle::from(Transform::from_xyz(
game.bonus.i as f32,
game.board[game.bonus.j][game.bonus.i].height + 0.2,
game.bonus.j as f32,
)))
.spawn_bundle(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_bundle(PointLightBundle {
point_light: PointLight {
@ -338,7 +339,6 @@ fn spawn_bonus(
transform: Transform::from_xyz(0.0, 2.0, 0.0),
..default()
});
children.spawn_scene(game.bonus.handle.clone());
})
.id(),
);

View file

@ -49,14 +49,14 @@ impl FromWorld for ComponentB {
}
}
fn load_scene_system(asset_server: Res<AssetServer>, mut scene_spawner: ResMut<SceneSpawner>) {
// Scenes are loaded just like any other asset.
let scene_handle: Handle<DynamicScene> = asset_server.load("scenes/load_scene_example.scn.ron");
// SceneSpawner can "spawn" scenes. "Spawning" a scene creates a new instance of the scene in
// the World with new entity ids. This guarantees that it will not overwrite existing
// entities.
scene_spawner.spawn_dynamic(scene_handle);
fn load_scene_system(mut commands: Commands, asset_server: Res<AssetServer>) {
// "Spawning" a scene bundle creates a new entity and spawns new instances
// of the given scene's entities as children of that entity.
commands.spawn_bundle(DynamicSceneBundle {
// Scenes are loaded just like any other asset.
scene: asset_server.load("scenes/load_scene_example.scn.ron"),
..default()
});
// This tells the AssetServer to watch for changes to assets.
// It enables our scenes to automatically reload in game when we modify their files

View file

@ -67,7 +67,6 @@ struct Ring {
fn setup(
mut commands: Commands,
asset_server: Res<AssetServer>,
mut scene_spawner: ResMut<SceneSpawner>,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
foxes: Res<Foxes>,
@ -121,15 +120,13 @@ fn setup(
let (x, z) = (radius * c, radius * s);
commands.entity(ring_parent).with_children(|builder| {
let fox_parent = builder
.spawn_bundle((
Transform::from_xyz(x as f32, 0.0, z as f32)
.with_scale(Vec3::splat(0.01))
.with_rotation(base_rotation * Quat::from_rotation_y(-fox_angle)),
GlobalTransform::default(),
))
.id();
scene_spawner.spawn_as_child(fox_handle.clone(), fox_parent);
builder.spawn_bundle(SceneBundle {
scene: fox_handle.clone(),
transform: Transform::from_xyz(x as f32, 0.0, z as f32)
.with_scale(Vec3::splat(0.01))
.with_rotation(base_rotation * Quat::from_rotation_y(-fox_angle)),
..default()
});
});
}

View file

@ -20,7 +20,10 @@ fn setup(
mut create_window_events: EventWriter<CreateWindow>,
) {
// add entities to the world
commands.spawn_scene(asset_server.load("models/monkey/Monkey.gltf#Scene0"));
commands.spawn_bundle(SceneBundle {
scene: asset_server.load("models/monkey/Monkey.gltf#Scene0"),
..default()
});
// light
commands.spawn_bundle(PointLightBundle {
transform: Transform::from_xyz(4.0, 5.0, 4.0),