diff --git a/assets/scenes/load_scene_example.scn.ron b/assets/scenes/load_scene_example.scn.ron index de4d3ce9280a0..4c03d1c53cc1a 100644 --- a/assets/scenes/load_scene_example.scn.ron +++ b/assets/scenes/load_scene_example.scn.ron @@ -1,4 +1,9 @@ ( + resources: { + "scene::ResourceA": ( + score: 2, + ), + }, entities: { 0: ( components: { diff --git a/crates/bevy_scene/src/dynamic_scene.rs b/crates/bevy_scene/src/dynamic_scene.rs index cb5bd1a387fce..adac64c425c83 100644 --- a/crates/bevy_scene/src/dynamic_scene.rs +++ b/crates/bevy_scene/src/dynamic_scene.rs @@ -10,10 +10,13 @@ use bevy_reflect::{Reflect, TypeRegistryArc, TypeUuid}; #[cfg(feature = "serialize")] use crate::serde::SceneSerializer; +use bevy_ecs::reflect::ReflectResource; #[cfg(feature = "serialize")] use serde::Serialize; -/// A collection of serializable dynamic entities, each with its own run-time defined set of components. +/// A collection of serializable resources and dynamic entities. +/// +/// Each dynamic entity in the collection contains 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 @@ -23,6 +26,7 @@ use serde::Serialize; #[derive(Default, TypeUuid)] #[uuid = "749479b1-fb8c-4ff8-a775-623aa76014f5"] pub struct DynamicScene { + pub resources: Vec>, pub entities: Vec, } @@ -47,15 +51,16 @@ impl DynamicScene { DynamicSceneBuilder::from_world_with_type_registry(world, type_registry.clone()); builder.extract_entities(world.iter_entities().map(|entity| entity.id())); + builder.extract_resources(); builder.build() } - /// Write the dynamic entities and their corresponding components to the given world. + /// Write the resources, the dynamic entities, and their corresponding components to the given world. /// /// This method will return a [`SceneSpawnError`] if a type either is not registered /// in the provided [`AppTypeRegistry`] resource, or doesn't reflect the - /// [`Component`](bevy_ecs::component::Component) trait. + /// [`Component`](bevy_ecs::component::Component) or [`Resource`](bevy_ecs::prelude::Resource) trait. pub fn write_to_world_with( &self, world: &mut World, @@ -64,6 +69,23 @@ impl DynamicScene { ) -> Result<(), SceneSpawnError> { let type_registry = type_registry.read(); + for resource in &self.resources { + let registration = type_registry + .get_with_name(resource.type_name()) + .ok_or_else(|| SceneSpawnError::UnregisteredType { + type_name: resource.type_name().to_string(), + })?; + let reflect_resource = registration.data::().ok_or_else(|| { + SceneSpawnError::UnregisteredResource { + type_name: resource.type_name().to_string(), + } + })?; + + // If the world already contains an instance of the given resource + // just apply the (possibly) new value, otherwise insert the resource + reflect_resource.apply_or_insert(world, &**resource); + } + for scene_entity in &self.entities { // Fetch the entity with the given entity id from the `entity_map` // or spawn a new entity with a transiently unique id if there is @@ -105,7 +127,7 @@ impl DynamicScene { Ok(()) } - /// Write the dynamic entities and their corresponding components to the given world. + /// Write the resources, the dynamic entities, and their corresponding components to the given world. /// /// This method will return a [`SceneSpawnError`] if a type either is not registered /// in the world's [`AppTypeRegistry`] resource, or doesn't reflect the diff --git a/crates/bevy_scene/src/dynamic_scene_builder.rs b/crates/bevy_scene/src/dynamic_scene_builder.rs index 1187f087b6809..0f25b1e484ba3 100644 --- a/crates/bevy_scene/src/dynamic_scene_builder.rs +++ b/crates/bevy_scene/src/dynamic_scene_builder.rs @@ -1,10 +1,16 @@ use crate::{DynamicEntity, DynamicScene}; use bevy_app::AppTypeRegistry; -use bevy_ecs::{prelude::Entity, reflect::ReflectComponent, world::World}; +use bevy_ecs::component::ComponentId; +use bevy_ecs::{ + prelude::Entity, + reflect::{ReflectComponent, ReflectResource}, + world::World, +}; +use bevy_reflect::Reflect; use bevy_utils::default; use std::collections::BTreeMap; -/// A [`DynamicScene`] builder, used to build a scene from a [`World`] by extracting some entities. +/// A [`DynamicScene`] builder, used to build a scene from a [`World`] by extracting some entities and resources. /// /// # Entity Order /// @@ -31,6 +37,7 @@ use std::collections::BTreeMap; /// let dynamic_scene = builder.build(); /// ``` pub struct DynamicSceneBuilder<'w> { + extracted_resources: BTreeMap>, extracted_scene: BTreeMap, type_registry: AppTypeRegistry, original_world: &'w World, @@ -41,6 +48,7 @@ impl<'w> DynamicSceneBuilder<'w> { /// All components registered in that world's [`AppTypeRegistry`] resource will be extracted. pub fn from_world(world: &'w World) -> Self { Self { + extracted_resources: default(), extracted_scene: default(), type_registry: world.resource::().clone(), original_world: world, @@ -51,6 +59,7 @@ impl<'w> DynamicSceneBuilder<'w> { /// Only components registered in the given [`AppTypeRegistry`] will be extracted. pub fn from_world_with_type_registry(world: &'w World, type_registry: AppTypeRegistry) -> Self { Self { + extracted_resources: default(), extracted_scene: default(), type_registry, original_world: world, @@ -63,6 +72,7 @@ impl<'w> DynamicSceneBuilder<'w> { /// [`Self::remove_empty_entities`] before building the scene. pub fn build(self) -> DynamicScene { DynamicScene { + resources: self.extracted_resources.into_values().collect(), entities: self.extracted_scene.into_values().collect(), } } @@ -126,17 +136,20 @@ impl<'w> DynamicSceneBuilder<'w> { let entity = self.original_world.entity(entity); for component_id in entity.archetype().components() { - let reflect_component = self - .original_world - .components() - .get_info(component_id) - .and_then(|info| type_registry.get(info.type_id().unwrap())) - .and_then(|registration| registration.data::()) - .and_then(|reflect_component| reflect_component.reflect(entity)); - - if let Some(reflect_component) = reflect_component { - entry.components.push(reflect_component.clone_value()); - } + let mut extract_and_push = || { + let type_id = self + .original_world + .components() + .get_info(component_id)? + .type_id()?; + let component = type_registry + .get(type_id)? + .data::()? + .reflect(entity)?; + entry.components.push(component.clone_value()); + Some(()) + }; + extract_and_push(); } self.extracted_scene.insert(index, entry); } @@ -144,13 +157,59 @@ impl<'w> DynamicSceneBuilder<'w> { drop(type_registry); self } + + /// Extract resources from the builder's [`World`]. + /// + /// Only resources registered in the builder's [`AppTypeRegistry`] will be extracted. + /// Re-extracting a resource that was already extracted will have no effect. + /// ``` + /// # use bevy_scene::DynamicSceneBuilder; + /// # use bevy_app::AppTypeRegistry; + /// # use bevy_ecs::prelude::{ReflectResource, Resource, World}; + /// # use bevy_reflect::Reflect; + /// #[derive(Resource, Default, Reflect)] + /// #[reflect(Resource)] + /// struct MyResource; + /// + /// # let mut world = World::default(); + /// # world.init_resource::(); + /// world.insert_resource(MyResource); + /// + /// let mut builder = DynamicSceneBuilder::from_world(&world); + /// builder.extract_resources(); + /// let scene = builder.build(); + /// ``` + pub fn extract_resources(&mut self) -> &mut Self { + let type_registry = self.type_registry.read(); + for (component_id, _) in self.original_world.storages().resources.iter() { + let mut extract_and_push = || { + let type_id = self + .original_world + .components() + .get_info(component_id)? + .type_id()?; + let resource = type_registry + .get(type_id)? + .data::()? + .reflect(self.original_world)?; + self.extracted_resources + .insert(component_id, resource.clone_value()); + Some(()) + }; + extract_and_push(); + } + + drop(type_registry); + self + } } #[cfg(test)] mod tests { use bevy_app::AppTypeRegistry; use bevy_ecs::{ - component::Component, prelude::Entity, query::With, reflect::ReflectComponent, world::World, + component::Component, prelude::Entity, prelude::Resource, query::With, + reflect::ReflectComponent, reflect::ReflectResource, world::World, }; use bevy_reflect::Reflect; @@ -160,10 +219,15 @@ mod tests { #[derive(Component, Reflect, Default, Eq, PartialEq, Debug)] #[reflect(Component)] struct ComponentA; + #[derive(Component, Reflect, Default, Eq, PartialEq, Debug)] #[reflect(Component)] struct ComponentB; + #[derive(Resource, Reflect, Default, Eq, PartialEq, Debug)] + #[reflect(Resource)] + struct ResourceA; + #[test] fn extract_one_entity() { let mut world = World::default(); @@ -303,4 +367,41 @@ mod tests { assert_eq!(scene.entities.len(), 1); assert_eq!(scene.entities[0].entity, entity_a.index()); } + + #[test] + fn extract_one_resource() { + let mut world = World::default(); + + let atr = AppTypeRegistry::default(); + atr.write().register::(); + world.insert_resource(atr); + + world.insert_resource(ResourceA); + + let mut builder = DynamicSceneBuilder::from_world(&world); + builder.extract_resources(); + let scene = builder.build(); + + assert_eq!(scene.resources.len(), 1); + assert!(scene.resources[0].represents::()); + } + + #[test] + fn extract_one_resource_twice() { + let mut world = World::default(); + + let atr = AppTypeRegistry::default(); + atr.write().register::(); + world.insert_resource(atr); + + world.insert_resource(ResourceA); + + let mut builder = DynamicSceneBuilder::from_world(&world); + builder.extract_resources(); + builder.extract_resources(); + let scene = builder.build(); + + assert_eq!(scene.resources.len(), 1); + assert!(scene.resources[0].represents::()); + } } diff --git a/crates/bevy_scene/src/scene.rs b/crates/bevy_scene/src/scene.rs index 4df757aa5838f..460f7cf8b2f3a 100644 --- a/crates/bevy_scene/src/scene.rs +++ b/crates/bevy_scene/src/scene.rs @@ -1,7 +1,7 @@ use bevy_app::AppTypeRegistry; use bevy_ecs::{ entity::EntityMap, - reflect::{ReflectComponent, ReflectMapEntities}, + reflect::{ReflectComponent, ReflectMapEntities, ReflectResource}, world::World, }; use bevy_reflect::TypeUuid; @@ -61,6 +61,33 @@ impl Scene { }; let type_registry = type_registry.read(); + + // Resources archetype + for (component_id, _) in self.world.storages().resources.iter() { + let component_info = self + .world + .components() + .get_info(component_id) + .expect("component_ids in archetypes should have ComponentInfo"); + + let type_id = component_info + .type_id() + .expect("reflected resources must have a type_id"); + + let registration = + type_registry + .get(type_id) + .ok_or_else(|| SceneSpawnError::UnregisteredType { + type_name: component_info.name().to_string(), + })?; + let reflect_resource = registration.data::().ok_or_else(|| { + SceneSpawnError::UnregisteredResource { + type_name: component_info.name().to_string(), + } + })?; + reflect_resource.copy(&self.world, world); + } + for archetype in self.world.archetypes().iter() { for scene_entity in archetype.entities() { let entity = *instance_info diff --git a/crates/bevy_scene/src/scene_spawner.rs b/crates/bevy_scene/src/scene_spawner.rs index 7a0c8f851b33b..5a46aa14e256c 100644 --- a/crates/bevy_scene/src/scene_spawner.rs +++ b/crates/bevy_scene/src/scene_spawner.rs @@ -45,6 +45,8 @@ pub struct SceneSpawner { pub enum SceneSpawnError { #[error("scene contains the unregistered component `{type_name}`. consider adding `#[reflect(Component)]` to your type")] UnregisteredComponent { type_name: String }, + #[error("scene contains the unregistered resource `{type_name}`. consider adding `#[reflect(Resource)]` to your type")] + UnregisteredResource { type_name: String }, #[error("scene contains the unregistered type `{type_name}`. consider registering the type using `app.register_type::()`")] UnregisteredType { type_name: String }, #[error("scene does not exist")] diff --git a/crates/bevy_scene/src/serde.rs b/crates/bevy_scene/src/serde.rs index 51b8a5ebce0e7..debe7c54515f3 100644 --- a/crates/bevy_scene/src/serde.rs +++ b/crates/bevy_scene/src/serde.rs @@ -15,6 +15,7 @@ use serde::{ use std::fmt::Formatter; pub const SCENE_STRUCT: &str = "Scene"; +pub const SCENE_RESOURCES: &str = "resources"; pub const SCENE_ENTITIES: &str = "entities"; pub const ENTITY_STRUCT: &str = "Entity"; @@ -36,7 +37,14 @@ impl<'a> Serialize for SceneSerializer<'a> { where S: serde::Serializer, { - let mut state = serializer.serialize_struct(SCENE_STRUCT, 1)?; + let mut state = serializer.serialize_struct(SCENE_STRUCT, 2)?; + state.serialize_field( + SCENE_RESOURCES, + &SceneMapSerializer { + entries: &self.scene.resources, + registry: self.registry, + }, + )?; state.serialize_field( SCENE_ENTITIES, &EntitiesSerializer { @@ -85,8 +93,8 @@ impl<'a> Serialize for EntitySerializer<'a> { let mut state = serializer.serialize_struct(ENTITY_STRUCT, 1)?; state.serialize_field( ENTITY_FIELD_COMPONENTS, - &ComponentsSerializer { - components: &self.entity.components, + &SceneMapSerializer { + entries: &self.entity.components, registry: self.registry, }, )?; @@ -94,21 +102,21 @@ impl<'a> Serialize for EntitySerializer<'a> { } } -pub struct ComponentsSerializer<'a> { - pub components: &'a [Box], +pub struct SceneMapSerializer<'a> { + pub entries: &'a [Box], pub registry: &'a TypeRegistryArc, } -impl<'a> Serialize for ComponentsSerializer<'a> { +impl<'a> Serialize for SceneMapSerializer<'a> { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { - let mut state = serializer.serialize_map(Some(self.components.len()))?; - for component in self.components { + let mut state = serializer.serialize_map(Some(self.entries.len()))?; + for reflect in self.entries { state.serialize_entry( - component.type_name(), - &TypedReflectSerializer::new(&**component, &self.registry.read()), + reflect.type_name(), + &TypedReflectSerializer::new(&**reflect, &self.registry.read()), )?; } state.end() @@ -118,6 +126,7 @@ impl<'a> Serialize for ComponentsSerializer<'a> { #[derive(Deserialize)] #[serde(field_identifier, rename_all = "lowercase")] enum SceneField { + Resources, Entities, } @@ -140,7 +149,7 @@ impl<'a, 'de> DeserializeSeed<'de> for SceneDeserializer<'a> { { deserializer.deserialize_struct( SCENE_STRUCT, - &[SCENE_ENTITIES], + &[SCENE_RESOURCES, SCENE_ENTITIES], SceneVisitor { type_registry: self.type_registry, }, @@ -163,9 +172,18 @@ impl<'a, 'de> Visitor<'de> for SceneVisitor<'a> { where A: MapAccess<'de>, { + let mut resources = None; let mut entities = None; while let Some(key) = map.next_key()? { match key { + SceneField::Resources => { + if resources.is_some() { + return Err(Error::duplicate_field(SCENE_RESOURCES)); + } + resources = Some(map.next_value_seed(SceneMapDeserializer { + registry: self.type_registry, + })?); + } SceneField::Entities => { if entities.is_some() { return Err(Error::duplicate_field(SCENE_ENTITIES)); @@ -177,22 +195,35 @@ impl<'a, 'de> Visitor<'de> for SceneVisitor<'a> { } } + let resources = resources.ok_or_else(|| Error::missing_field(SCENE_RESOURCES))?; let entities = entities.ok_or_else(|| Error::missing_field(SCENE_ENTITIES))?; - Ok(DynamicScene { entities }) + Ok(DynamicScene { + resources, + entities, + }) } fn visit_seq(self, mut seq: A) -> Result where A: SeqAccess<'de>, { + let resources = seq + .next_element_seed(SceneMapDeserializer { + registry: self.type_registry, + })? + .ok_or_else(|| Error::missing_field(SCENE_RESOURCES))?; + let entities = seq .next_element_seed(SceneEntitiesDeserializer { type_registry: self.type_registry, })? .ok_or_else(|| Error::missing_field(SCENE_ENTITIES))?; - Ok(DynamicScene { entities }) + Ok(DynamicScene { + resources, + entities, + }) } } @@ -281,7 +312,7 @@ impl<'a, 'de> Visitor<'de> for SceneEntityVisitor<'a> { A: SeqAccess<'de>, { let components = seq - .next_element_seed(ComponentDeserializer { + .next_element_seed(SceneMapDeserializer { registry: self.registry, })? .ok_or_else(|| Error::missing_field(ENTITY_FIELD_COMPONENTS))?; @@ -304,7 +335,7 @@ impl<'a, 'de> Visitor<'de> for SceneEntityVisitor<'a> { return Err(Error::duplicate_field(ENTITY_FIELD_COMPONENTS)); } - components = Some(map.next_value_seed(ComponentDeserializer { + components = Some(map.next_value_seed(SceneMapDeserializer { registry: self.registry, })?); } @@ -321,32 +352,32 @@ impl<'a, 'de> Visitor<'de> for SceneEntityVisitor<'a> { } } -pub struct ComponentDeserializer<'a> { +pub struct SceneMapDeserializer<'a> { pub registry: &'a TypeRegistry, } -impl<'a, 'de> DeserializeSeed<'de> for ComponentDeserializer<'a> { +impl<'a, 'de> DeserializeSeed<'de> for SceneMapDeserializer<'a> { type Value = Vec>; fn deserialize(self, deserializer: D) -> Result where D: serde::Deserializer<'de>, { - deserializer.deserialize_map(ComponentVisitor { + deserializer.deserialize_map(SceneMapVisitor { registry: self.registry, }) } } -struct ComponentVisitor<'a> { +struct SceneMapVisitor<'a> { pub registry: &'a TypeRegistry, } -impl<'a, 'de> Visitor<'de> for ComponentVisitor<'a> { +impl<'a, 'de> Visitor<'de> for SceneMapVisitor<'a> { type Value = Vec>; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("map of components") + formatter.write_str("map of reflect types") } fn visit_map(self, mut map: A) -> std::result::Result @@ -354,23 +385,23 @@ impl<'a, 'de> Visitor<'de> for ComponentVisitor<'a> { A: MapAccess<'de>, { let mut added = HashSet::new(); - let mut components = Vec::new(); + let mut entries = Vec::new(); while let Some(registration) = map.next_key_seed(TypeRegistrationDeserializer::new(self.registry))? { if !added.insert(registration.type_id()) { return Err(Error::custom(format_args!( - "duplicate component: `{}`", + "duplicate reflect type: `{}`", registration.type_name() ))); } - components.push( + entries.push( map.next_value_seed(TypedReflectDeserializer::new(registration, self.registry))?, ); } - Ok(components) + Ok(entries) } fn visit_seq(self, mut seq: A) -> Result @@ -394,7 +425,7 @@ mod tests { use crate::{DynamicScene, DynamicSceneBuilder}; use bevy_app::AppTypeRegistry; use bevy_ecs::entity::EntityMap; - use bevy_ecs::prelude::{Component, ReflectComponent, World}; + use bevy_ecs::prelude::{Component, ReflectComponent, ReflectResource, Resource, World}; use bevy_reflect::{FromReflect, Reflect, ReflectSerialize}; use bincode::Options; use serde::de::DeserializeSeed; @@ -429,6 +460,12 @@ mod tests { }, } + #[derive(Resource, Reflect, Default)] + #[reflect(Resource)] + struct MyResource { + foo: i32, + } + fn create_world() -> World { let mut world = World::new(); let registry = AppTypeRegistry::default(); @@ -443,6 +480,7 @@ mod tests { registry.register_type_data::(); registry.register::<[usize; 3]>(); registry.register::<(f32, f32)>(); + registry.register::(); } world.insert_resource(registry); world @@ -456,11 +494,19 @@ mod tests { let b = world.spawn((Foo(123), Bar(345))).id(); let c = world.spawn((Foo(123), Bar(345), Baz(789))).id(); + world.insert_resource(MyResource { foo: 123 }); + let mut builder = DynamicSceneBuilder::from_world(&world); builder.extract_entities([a, b, c].into_iter()); + builder.extract_resources(); let scene = builder.build(); let expected = r#"( + resources: { + "bevy_scene::serde::tests::MyResource": ( + foo: 123, + ), + }, entities: { 0: ( components: { @@ -493,6 +539,11 @@ mod tests { let world = create_world(); let input = r#"( + resources: { + "bevy_scene::serde::tests::MyResource": ( + foo: 123, + ), + }, entities: { 0: ( components: { @@ -520,6 +571,11 @@ mod tests { }; let scene = scene_deserializer.deserialize(&mut deserializer).unwrap(); + assert_eq!( + 1, + scene.resources.len(), + "expected `resources` to contain 1 resource" + ); assert_eq!( 3, scene.entities.len(), @@ -530,6 +586,11 @@ mod tests { let mut dst_world = create_world(); scene.write_to_world(&mut dst_world, &mut map).unwrap(); + let my_resource = dst_world.get_resource::(); + assert!(my_resource.is_some()); + let my_resource = my_resource.unwrap(); + assert_eq!(my_resource.foo, 123); + assert_eq!(3, dst_world.query::<&Foo>().iter(&dst_world).count()); assert_eq!(2, dst_world.query::<&Bar>().iter(&dst_world).count()); assert_eq!(1, dst_world.query::<&Baz>().iter(&dst_world).count()); @@ -554,10 +615,10 @@ mod tests { assert_eq!( vec![ - 1, 0, 1, 37, 98, 101, 118, 121, 95, 115, 99, 101, 110, 101, 58, 58, 115, 101, 114, - 100, 101, 58, 58, 116, 101, 115, 116, 115, 58, 58, 77, 121, 67, 111, 109, 112, 111, - 110, 101, 110, 116, 1, 2, 3, 102, 102, 166, 63, 205, 204, 108, 64, 1, 12, 72, 101, - 108, 108, 111, 32, 87, 111, 114, 108, 100, 33 + 0, 1, 0, 1, 37, 98, 101, 118, 121, 95, 115, 99, 101, 110, 101, 58, 58, 115, 101, + 114, 100, 101, 58, 58, 116, 101, 115, 116, 115, 58, 58, 77, 121, 67, 111, 109, 112, + 111, 110, 101, 110, 116, 1, 2, 3, 102, 102, 166, 63, 205, 204, 108, 64, 1, 12, 72, + 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33 ], serialized_scene ); @@ -594,11 +655,11 @@ mod tests { assert_eq!( vec![ - 145, 129, 0, 145, 129, 217, 37, 98, 101, 118, 121, 95, 115, 99, 101, 110, 101, 58, - 58, 115, 101, 114, 100, 101, 58, 58, 116, 101, 115, 116, 115, 58, 58, 77, 121, 67, - 111, 109, 112, 111, 110, 101, 110, 116, 147, 147, 1, 2, 3, 146, 202, 63, 166, 102, - 102, 202, 64, 108, 204, 205, 129, 165, 84, 117, 112, 108, 101, 172, 72, 101, 108, - 108, 111, 32, 87, 111, 114, 108, 100, 33 + 146, 128, 129, 0, 145, 129, 217, 37, 98, 101, 118, 121, 95, 115, 99, 101, 110, 101, + 58, 58, 115, 101, 114, 100, 101, 58, 58, 116, 101, 115, 116, 115, 58, 58, 77, 121, + 67, 111, 109, 112, 111, 110, 101, 110, 116, 147, 147, 1, 2, 3, 146, 202, 63, 166, + 102, 102, 202, 64, 108, 204, 205, 129, 165, 84, 117, 112, 108, 101, 172, 72, 101, + 108, 108, 111, 32, 87, 111, 114, 108, 100, 33 ], buf ); @@ -635,12 +696,12 @@ mod tests { assert_eq!( vec![ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, - 0, 98, 101, 118, 121, 95, 115, 99, 101, 110, 101, 58, 58, 115, 101, 114, 100, 101, - 58, 58, 116, 101, 115, 116, 115, 58, 58, 77, 121, 67, 111, 109, 112, 111, 110, 101, - 110, 116, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, - 102, 102, 166, 63, 205, 204, 108, 64, 1, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 72, 101, - 108, 108, 111, 32, 87, 111, 114, 108, 100, 33 + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 37, 0, 0, 0, 0, 0, 0, 0, 98, 101, 118, 121, 95, 115, 99, 101, 110, 101, 58, 58, + 115, 101, 114, 100, 101, 58, 58, 116, 101, 115, 116, 115, 58, 58, 77, 121, 67, 111, + 109, 112, 111, 110, 101, 110, 116, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, + 3, 0, 0, 0, 0, 0, 0, 0, 102, 102, 166, 63, 205, 204, 108, 64, 1, 0, 0, 0, 12, 0, 0, + 0, 0, 0, 0, 0, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33 ], serialized_scene ); diff --git a/examples/scene/scene.rs b/examples/scene/scene.rs index e6477d7dedd6b..e066ba6207e42 100644 --- a/examples/scene/scene.rs +++ b/examples/scene/scene.rs @@ -1,8 +1,6 @@ //! This example illustrates loading scenes from files. -use std::fs::File; -use std::io::Write; - use bevy::{prelude::*, tasks::IoTaskPool, utils::Duration}; +use std::{fs::File, io::Write}; fn main() { App::new() @@ -14,6 +12,7 @@ fn main() { })) .register_type::() .register_type::() + .register_type::() .add_systems( Startup, (save_scene_system, load_scene_system, infotext_system), @@ -57,6 +56,13 @@ impl FromWorld for ComponentB { } } +// Resources can be serialized in scenes as well, with the same requirements `Component`s have. +#[derive(Resource, Reflect, Default)] +#[reflect(Resource)] +struct ResourceA { + pub score: u32, +} + // The initial scene file will be loaded below and not change when the scene is saved const SCENE_FILE_PATH: &str = "scenes/load_scene_example.scn.ron"; @@ -75,7 +81,10 @@ fn load_scene_system(mut commands: Commands, asset_server: Res) { // This system logs all ComponentA components in our world. Try making a change to a ComponentA in // load_scene_example.scn. You should immediately see the changes appear in the console. -fn log_system(query: Query<(Entity, &ComponentA), Changed>) { +fn log_system( + query: Query<(Entity, &ComponentA), Changed>, + res: Option>, +) { for (entity, component_a) in &query { info!(" Entity({})", entity.index()); info!( @@ -83,6 +92,11 @@ fn log_system(query: Query<(Entity, &ComponentA), Changed>) { component_a.x, component_a.y ); } + if let Some(res) = res { + if res.is_added() { + info!(" New ResourceA: {{ score: {} }}\n", res.score); + } + } } fn save_scene_system(world: &mut World) { @@ -97,6 +111,7 @@ fn save_scene_system(world: &mut World) { Transform::IDENTITY, )); scene_world.spawn(ComponentA { x: 3.0, y: 4.0 }); + scene_world.insert_resource(ResourceA { score: 1 }); // The TypeRegistry resource contains information about all registered types (including // components). This is used to construct scenes.