(De) serialize resources in scenes (#6846)
# Objective Co-Authored-By: davier [bricedavier@gmail.com](mailto:bricedavier@gmail.com) Fixes #3576. Adds a `resources` field in scene serialization data to allow de/serializing resources that have reflection enabled. ## Solution Most of this code is taken from a previous closed PR: https://github.com/bevyengine/bevy/pull/3580. Most of the credit goes to @Davier , what I did was mostly getting it to work on the latest main branch of Bevy, along with adding a few asserts in the currently existing tests to be sure everything is working properly. This PR changes the scene format to include resources in this way: ``` ( resources: { // List of resources here, keyed by resource type name. }, entities: [ // Previous scene format here ], ) ``` An example taken from the tests: ``` ( resources: { "bevy_scene::serde::tests::MyResource": ( foo: 123, ), }, entities: { // Previous scene format here }, ) ``` For this, a `resources` fields has been added on the `DynamicScene` and the `DynamicSceneBuilder` structs. The latter now also has a method named `extract_resources` to properly extract the existing resources registered in the local type registry, in a similar way to `extract_entities`. --- ## Changelog Added: Reflect resources registered in the type registry used by dynamic scenes will now be properly de/serialized in scene data. ## Migration Guide Since the scene format has been changed, the user may not be able to use scenes saved prior to this PR due to the `resources` scene field being missing. ~~To preserve backwards compatibility, I will try to make the `resources` fully optional so that old scenes can be loaded without issue.~~ ## TODOs - [x] I may have to update a few doc blocks still referring to dynamic scenes as mere container of entities, since they now include resources as well. - [x] ~~I want to make the `resources` key optional, as specified in the Migration Guide, so that old scenes will be compatible with this change.~~ Since this would only be trivial for ron format, I think it might be better to consider it in a separate PR/discussion to figure out if it could be done for binary serialization too. - [x] I suppose it might be a good idea to add a resources in the scene example so that users will quickly notice they can serialize resources just like entities. --------- Co-authored-by: Carter Anderson <mcanders1@gmail.com>
This commit is contained in:
parent
6a85eb3d7e
commit
7b38de0a64
@ -1,4 +1,9 @@
|
|||||||
(
|
(
|
||||||
|
resources: {
|
||||||
|
"scene::ResourceA": (
|
||||||
|
score: 2,
|
||||||
|
),
|
||||||
|
},
|
||||||
entities: {
|
entities: {
|
||||||
0: (
|
0: (
|
||||||
components: {
|
components: {
|
||||||
|
|||||||
@ -10,10 +10,13 @@ use bevy_reflect::{Reflect, TypeRegistryArc, TypeUuid};
|
|||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serialize")]
|
||||||
use crate::serde::SceneSerializer;
|
use crate::serde::SceneSerializer;
|
||||||
|
use bevy_ecs::reflect::ReflectResource;
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serialize")]
|
||||||
use serde::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:
|
/// To spawn a dynamic scene, you can use either:
|
||||||
/// * [`SceneSpawner::spawn_dynamic`](crate::SceneSpawner::spawn_dynamic)
|
/// * [`SceneSpawner::spawn_dynamic`](crate::SceneSpawner::spawn_dynamic)
|
||||||
/// * adding the [`DynamicSceneBundle`](crate::DynamicSceneBundle) to an entity
|
/// * adding the [`DynamicSceneBundle`](crate::DynamicSceneBundle) to an entity
|
||||||
@ -23,6 +26,7 @@ use serde::Serialize;
|
|||||||
#[derive(Default, TypeUuid)]
|
#[derive(Default, TypeUuid)]
|
||||||
#[uuid = "749479b1-fb8c-4ff8-a775-623aa76014f5"]
|
#[uuid = "749479b1-fb8c-4ff8-a775-623aa76014f5"]
|
||||||
pub struct DynamicScene {
|
pub struct DynamicScene {
|
||||||
|
pub resources: Vec<Box<dyn Reflect>>,
|
||||||
pub entities: Vec<DynamicEntity>,
|
pub entities: Vec<DynamicEntity>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,15 +51,16 @@ impl DynamicScene {
|
|||||||
DynamicSceneBuilder::from_world_with_type_registry(world, type_registry.clone());
|
DynamicSceneBuilder::from_world_with_type_registry(world, type_registry.clone());
|
||||||
|
|
||||||
builder.extract_entities(world.iter_entities().map(|entity| entity.id()));
|
builder.extract_entities(world.iter_entities().map(|entity| entity.id()));
|
||||||
|
builder.extract_resources();
|
||||||
|
|
||||||
builder.build()
|
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
|
/// This method will return a [`SceneSpawnError`] if a type either is not registered
|
||||||
/// in the provided [`AppTypeRegistry`] resource, or doesn't reflect the
|
/// 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(
|
pub fn write_to_world_with(
|
||||||
&self,
|
&self,
|
||||||
world: &mut World,
|
world: &mut World,
|
||||||
@ -64,6 +69,23 @@ impl DynamicScene {
|
|||||||
) -> Result<(), SceneSpawnError> {
|
) -> Result<(), SceneSpawnError> {
|
||||||
let type_registry = type_registry.read();
|
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::<ReflectResource>().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 {
|
for scene_entity in &self.entities {
|
||||||
// Fetch the entity with the given entity id from the `entity_map`
|
// 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
|
// or spawn a new entity with a transiently unique id if there is
|
||||||
@ -105,7 +127,7 @@ impl DynamicScene {
|
|||||||
Ok(())
|
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
|
/// This method will return a [`SceneSpawnError`] if a type either is not registered
|
||||||
/// in the world's [`AppTypeRegistry`] resource, or doesn't reflect the
|
/// in the world's [`AppTypeRegistry`] resource, or doesn't reflect the
|
||||||
|
|||||||
@ -1,10 +1,16 @@
|
|||||||
use crate::{DynamicEntity, DynamicScene};
|
use crate::{DynamicEntity, DynamicScene};
|
||||||
use bevy_app::AppTypeRegistry;
|
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 bevy_utils::default;
|
||||||
use std::collections::BTreeMap;
|
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
|
/// # Entity Order
|
||||||
///
|
///
|
||||||
@ -31,6 +37,7 @@ use std::collections::BTreeMap;
|
|||||||
/// let dynamic_scene = builder.build();
|
/// let dynamic_scene = builder.build();
|
||||||
/// ```
|
/// ```
|
||||||
pub struct DynamicSceneBuilder<'w> {
|
pub struct DynamicSceneBuilder<'w> {
|
||||||
|
extracted_resources: BTreeMap<ComponentId, Box<dyn Reflect>>,
|
||||||
extracted_scene: BTreeMap<u32, DynamicEntity>,
|
extracted_scene: BTreeMap<u32, DynamicEntity>,
|
||||||
type_registry: AppTypeRegistry,
|
type_registry: AppTypeRegistry,
|
||||||
original_world: &'w World,
|
original_world: &'w World,
|
||||||
@ -41,6 +48,7 @@ impl<'w> DynamicSceneBuilder<'w> {
|
|||||||
/// All components registered in that world's [`AppTypeRegistry`] resource will be extracted.
|
/// All components registered in that world's [`AppTypeRegistry`] resource will be extracted.
|
||||||
pub fn from_world(world: &'w World) -> Self {
|
pub fn from_world(world: &'w World) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
extracted_resources: default(),
|
||||||
extracted_scene: default(),
|
extracted_scene: default(),
|
||||||
type_registry: world.resource::<AppTypeRegistry>().clone(),
|
type_registry: world.resource::<AppTypeRegistry>().clone(),
|
||||||
original_world: world,
|
original_world: world,
|
||||||
@ -51,6 +59,7 @@ impl<'w> DynamicSceneBuilder<'w> {
|
|||||||
/// Only components registered in the given [`AppTypeRegistry`] will be extracted.
|
/// Only components registered in the given [`AppTypeRegistry`] will be extracted.
|
||||||
pub fn from_world_with_type_registry(world: &'w World, type_registry: AppTypeRegistry) -> Self {
|
pub fn from_world_with_type_registry(world: &'w World, type_registry: AppTypeRegistry) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
extracted_resources: default(),
|
||||||
extracted_scene: default(),
|
extracted_scene: default(),
|
||||||
type_registry,
|
type_registry,
|
||||||
original_world: world,
|
original_world: world,
|
||||||
@ -63,6 +72,7 @@ impl<'w> DynamicSceneBuilder<'w> {
|
|||||||
/// [`Self::remove_empty_entities`] before building the scene.
|
/// [`Self::remove_empty_entities`] before building the scene.
|
||||||
pub fn build(self) -> DynamicScene {
|
pub fn build(self) -> DynamicScene {
|
||||||
DynamicScene {
|
DynamicScene {
|
||||||
|
resources: self.extracted_resources.into_values().collect(),
|
||||||
entities: self.extracted_scene.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);
|
let entity = self.original_world.entity(entity);
|
||||||
for component_id in entity.archetype().components() {
|
for component_id in entity.archetype().components() {
|
||||||
let reflect_component = self
|
let mut extract_and_push = || {
|
||||||
.original_world
|
let type_id = self
|
||||||
.components()
|
.original_world
|
||||||
.get_info(component_id)
|
.components()
|
||||||
.and_then(|info| type_registry.get(info.type_id().unwrap()))
|
.get_info(component_id)?
|
||||||
.and_then(|registration| registration.data::<ReflectComponent>())
|
.type_id()?;
|
||||||
.and_then(|reflect_component| reflect_component.reflect(entity));
|
let component = type_registry
|
||||||
|
.get(type_id)?
|
||||||
if let Some(reflect_component) = reflect_component {
|
.data::<ReflectComponent>()?
|
||||||
entry.components.push(reflect_component.clone_value());
|
.reflect(entity)?;
|
||||||
}
|
entry.components.push(component.clone_value());
|
||||||
|
Some(())
|
||||||
|
};
|
||||||
|
extract_and_push();
|
||||||
}
|
}
|
||||||
self.extracted_scene.insert(index, entry);
|
self.extracted_scene.insert(index, entry);
|
||||||
}
|
}
|
||||||
@ -144,13 +157,59 @@ impl<'w> DynamicSceneBuilder<'w> {
|
|||||||
drop(type_registry);
|
drop(type_registry);
|
||||||
self
|
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::<AppTypeRegistry>();
|
||||||
|
/// 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::<ReflectResource>()?
|
||||||
|
.reflect(self.original_world)?;
|
||||||
|
self.extracted_resources
|
||||||
|
.insert(component_id, resource.clone_value());
|
||||||
|
Some(())
|
||||||
|
};
|
||||||
|
extract_and_push();
|
||||||
|
}
|
||||||
|
|
||||||
|
drop(type_registry);
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use bevy_app::AppTypeRegistry;
|
use bevy_app::AppTypeRegistry;
|
||||||
use bevy_ecs::{
|
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;
|
use bevy_reflect::Reflect;
|
||||||
@ -160,10 +219,15 @@ mod tests {
|
|||||||
#[derive(Component, Reflect, Default, Eq, PartialEq, Debug)]
|
#[derive(Component, Reflect, Default, Eq, PartialEq, Debug)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
struct ComponentA;
|
struct ComponentA;
|
||||||
|
|
||||||
#[derive(Component, Reflect, Default, Eq, PartialEq, Debug)]
|
#[derive(Component, Reflect, Default, Eq, PartialEq, Debug)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
struct ComponentB;
|
struct ComponentB;
|
||||||
|
|
||||||
|
#[derive(Resource, Reflect, Default, Eq, PartialEq, Debug)]
|
||||||
|
#[reflect(Resource)]
|
||||||
|
struct ResourceA;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn extract_one_entity() {
|
fn extract_one_entity() {
|
||||||
let mut world = World::default();
|
let mut world = World::default();
|
||||||
@ -303,4 +367,41 @@ mod tests {
|
|||||||
assert_eq!(scene.entities.len(), 1);
|
assert_eq!(scene.entities.len(), 1);
|
||||||
assert_eq!(scene.entities[0].entity, entity_a.index());
|
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::<ResourceA>();
|
||||||
|
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::<ResourceA>());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn extract_one_resource_twice() {
|
||||||
|
let mut world = World::default();
|
||||||
|
|
||||||
|
let atr = AppTypeRegistry::default();
|
||||||
|
atr.write().register::<ResourceA>();
|
||||||
|
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::<ResourceA>());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
use bevy_app::AppTypeRegistry;
|
use bevy_app::AppTypeRegistry;
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
entity::EntityMap,
|
entity::EntityMap,
|
||||||
reflect::{ReflectComponent, ReflectMapEntities},
|
reflect::{ReflectComponent, ReflectMapEntities, ReflectResource},
|
||||||
world::World,
|
world::World,
|
||||||
};
|
};
|
||||||
use bevy_reflect::TypeUuid;
|
use bevy_reflect::TypeUuid;
|
||||||
@ -61,6 +61,33 @@ impl Scene {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let type_registry = type_registry.read();
|
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::<ReflectResource>().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 archetype in self.world.archetypes().iter() {
|
||||||
for scene_entity in archetype.entities() {
|
for scene_entity in archetype.entities() {
|
||||||
let entity = *instance_info
|
let entity = *instance_info
|
||||||
|
|||||||
@ -45,6 +45,8 @@ pub struct SceneSpawner {
|
|||||||
pub enum SceneSpawnError {
|
pub enum SceneSpawnError {
|
||||||
#[error("scene contains the unregistered component `{type_name}`. consider adding `#[reflect(Component)]` to your type")]
|
#[error("scene contains the unregistered component `{type_name}`. consider adding `#[reflect(Component)]` to your type")]
|
||||||
UnregisteredComponent { type_name: String },
|
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::<T>()`")]
|
#[error("scene contains the unregistered type `{type_name}`. consider registering the type using `app.register_type::<T>()`")]
|
||||||
UnregisteredType { type_name: String },
|
UnregisteredType { type_name: String },
|
||||||
#[error("scene does not exist")]
|
#[error("scene does not exist")]
|
||||||
|
|||||||
@ -15,6 +15,7 @@ use serde::{
|
|||||||
use std::fmt::Formatter;
|
use std::fmt::Formatter;
|
||||||
|
|
||||||
pub const SCENE_STRUCT: &str = "Scene";
|
pub const SCENE_STRUCT: &str = "Scene";
|
||||||
|
pub const SCENE_RESOURCES: &str = "resources";
|
||||||
pub const SCENE_ENTITIES: &str = "entities";
|
pub const SCENE_ENTITIES: &str = "entities";
|
||||||
|
|
||||||
pub const ENTITY_STRUCT: &str = "Entity";
|
pub const ENTITY_STRUCT: &str = "Entity";
|
||||||
@ -36,7 +37,14 @@ impl<'a> Serialize for SceneSerializer<'a> {
|
|||||||
where
|
where
|
||||||
S: serde::Serializer,
|
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(
|
state.serialize_field(
|
||||||
SCENE_ENTITIES,
|
SCENE_ENTITIES,
|
||||||
&EntitiesSerializer {
|
&EntitiesSerializer {
|
||||||
@ -85,8 +93,8 @@ impl<'a> Serialize for EntitySerializer<'a> {
|
|||||||
let mut state = serializer.serialize_struct(ENTITY_STRUCT, 1)?;
|
let mut state = serializer.serialize_struct(ENTITY_STRUCT, 1)?;
|
||||||
state.serialize_field(
|
state.serialize_field(
|
||||||
ENTITY_FIELD_COMPONENTS,
|
ENTITY_FIELD_COMPONENTS,
|
||||||
&ComponentsSerializer {
|
&SceneMapSerializer {
|
||||||
components: &self.entity.components,
|
entries: &self.entity.components,
|
||||||
registry: self.registry,
|
registry: self.registry,
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
@ -94,21 +102,21 @@ impl<'a> Serialize for EntitySerializer<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ComponentsSerializer<'a> {
|
pub struct SceneMapSerializer<'a> {
|
||||||
pub components: &'a [Box<dyn Reflect>],
|
pub entries: &'a [Box<dyn Reflect>],
|
||||||
pub registry: &'a TypeRegistryArc,
|
pub registry: &'a TypeRegistryArc,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Serialize for ComponentsSerializer<'a> {
|
impl<'a> Serialize for SceneMapSerializer<'a> {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
where
|
where
|
||||||
S: serde::Serializer,
|
S: serde::Serializer,
|
||||||
{
|
{
|
||||||
let mut state = serializer.serialize_map(Some(self.components.len()))?;
|
let mut state = serializer.serialize_map(Some(self.entries.len()))?;
|
||||||
for component in self.components {
|
for reflect in self.entries {
|
||||||
state.serialize_entry(
|
state.serialize_entry(
|
||||||
component.type_name(),
|
reflect.type_name(),
|
||||||
&TypedReflectSerializer::new(&**component, &self.registry.read()),
|
&TypedReflectSerializer::new(&**reflect, &self.registry.read()),
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
state.end()
|
state.end()
|
||||||
@ -118,6 +126,7 @@ impl<'a> Serialize for ComponentsSerializer<'a> {
|
|||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(field_identifier, rename_all = "lowercase")]
|
#[serde(field_identifier, rename_all = "lowercase")]
|
||||||
enum SceneField {
|
enum SceneField {
|
||||||
|
Resources,
|
||||||
Entities,
|
Entities,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,7 +149,7 @@ impl<'a, 'de> DeserializeSeed<'de> for SceneDeserializer<'a> {
|
|||||||
{
|
{
|
||||||
deserializer.deserialize_struct(
|
deserializer.deserialize_struct(
|
||||||
SCENE_STRUCT,
|
SCENE_STRUCT,
|
||||||
&[SCENE_ENTITIES],
|
&[SCENE_RESOURCES, SCENE_ENTITIES],
|
||||||
SceneVisitor {
|
SceneVisitor {
|
||||||
type_registry: self.type_registry,
|
type_registry: self.type_registry,
|
||||||
},
|
},
|
||||||
@ -163,9 +172,18 @@ impl<'a, 'de> Visitor<'de> for SceneVisitor<'a> {
|
|||||||
where
|
where
|
||||||
A: MapAccess<'de>,
|
A: MapAccess<'de>,
|
||||||
{
|
{
|
||||||
|
let mut resources = None;
|
||||||
let mut entities = None;
|
let mut entities = None;
|
||||||
while let Some(key) = map.next_key()? {
|
while let Some(key) = map.next_key()? {
|
||||||
match 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 => {
|
SceneField::Entities => {
|
||||||
if entities.is_some() {
|
if entities.is_some() {
|
||||||
return Err(Error::duplicate_field(SCENE_ENTITIES));
|
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))?;
|
let entities = entities.ok_or_else(|| Error::missing_field(SCENE_ENTITIES))?;
|
||||||
|
|
||||||
Ok(DynamicScene { entities })
|
Ok(DynamicScene {
|
||||||
|
resources,
|
||||||
|
entities,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
||||||
where
|
where
|
||||||
A: SeqAccess<'de>,
|
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
|
let entities = seq
|
||||||
.next_element_seed(SceneEntitiesDeserializer {
|
.next_element_seed(SceneEntitiesDeserializer {
|
||||||
type_registry: self.type_registry,
|
type_registry: self.type_registry,
|
||||||
})?
|
})?
|
||||||
.ok_or_else(|| Error::missing_field(SCENE_ENTITIES))?;
|
.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>,
|
A: SeqAccess<'de>,
|
||||||
{
|
{
|
||||||
let components = seq
|
let components = seq
|
||||||
.next_element_seed(ComponentDeserializer {
|
.next_element_seed(SceneMapDeserializer {
|
||||||
registry: self.registry,
|
registry: self.registry,
|
||||||
})?
|
})?
|
||||||
.ok_or_else(|| Error::missing_field(ENTITY_FIELD_COMPONENTS))?;
|
.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));
|
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,
|
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,
|
pub registry: &'a TypeRegistry,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'de> DeserializeSeed<'de> for ComponentDeserializer<'a> {
|
impl<'a, 'de> DeserializeSeed<'de> for SceneMapDeserializer<'a> {
|
||||||
type Value = Vec<Box<dyn Reflect>>;
|
type Value = Vec<Box<dyn Reflect>>;
|
||||||
|
|
||||||
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
|
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
|
||||||
where
|
where
|
||||||
D: serde::Deserializer<'de>,
|
D: serde::Deserializer<'de>,
|
||||||
{
|
{
|
||||||
deserializer.deserialize_map(ComponentVisitor {
|
deserializer.deserialize_map(SceneMapVisitor {
|
||||||
registry: self.registry,
|
registry: self.registry,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ComponentVisitor<'a> {
|
struct SceneMapVisitor<'a> {
|
||||||
pub registry: &'a TypeRegistry,
|
pub registry: &'a TypeRegistry,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'de> Visitor<'de> for ComponentVisitor<'a> {
|
impl<'a, 'de> Visitor<'de> for SceneMapVisitor<'a> {
|
||||||
type Value = Vec<Box<dyn Reflect>>;
|
type Value = Vec<Box<dyn Reflect>>;
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
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<A>(self, mut map: A) -> std::result::Result<Self::Value, A::Error>
|
fn visit_map<A>(self, mut map: A) -> std::result::Result<Self::Value, A::Error>
|
||||||
@ -354,23 +385,23 @@ impl<'a, 'de> Visitor<'de> for ComponentVisitor<'a> {
|
|||||||
A: MapAccess<'de>,
|
A: MapAccess<'de>,
|
||||||
{
|
{
|
||||||
let mut added = HashSet::new();
|
let mut added = HashSet::new();
|
||||||
let mut components = Vec::new();
|
let mut entries = Vec::new();
|
||||||
while let Some(registration) =
|
while let Some(registration) =
|
||||||
map.next_key_seed(TypeRegistrationDeserializer::new(self.registry))?
|
map.next_key_seed(TypeRegistrationDeserializer::new(self.registry))?
|
||||||
{
|
{
|
||||||
if !added.insert(registration.type_id()) {
|
if !added.insert(registration.type_id()) {
|
||||||
return Err(Error::custom(format_args!(
|
return Err(Error::custom(format_args!(
|
||||||
"duplicate component: `{}`",
|
"duplicate reflect type: `{}`",
|
||||||
registration.type_name()
|
registration.type_name()
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
components.push(
|
entries.push(
|
||||||
map.next_value_seed(TypedReflectDeserializer::new(registration, self.registry))?,
|
map.next_value_seed(TypedReflectDeserializer::new(registration, self.registry))?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(components)
|
Ok(entries)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
||||||
@ -394,7 +425,7 @@ mod tests {
|
|||||||
use crate::{DynamicScene, DynamicSceneBuilder};
|
use crate::{DynamicScene, DynamicSceneBuilder};
|
||||||
use bevy_app::AppTypeRegistry;
|
use bevy_app::AppTypeRegistry;
|
||||||
use bevy_ecs::entity::EntityMap;
|
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 bevy_reflect::{FromReflect, Reflect, ReflectSerialize};
|
||||||
use bincode::Options;
|
use bincode::Options;
|
||||||
use serde::de::DeserializeSeed;
|
use serde::de::DeserializeSeed;
|
||||||
@ -429,6 +460,12 @@ mod tests {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Resource, Reflect, Default)]
|
||||||
|
#[reflect(Resource)]
|
||||||
|
struct MyResource {
|
||||||
|
foo: i32,
|
||||||
|
}
|
||||||
|
|
||||||
fn create_world() -> World {
|
fn create_world() -> World {
|
||||||
let mut world = World::new();
|
let mut world = World::new();
|
||||||
let registry = AppTypeRegistry::default();
|
let registry = AppTypeRegistry::default();
|
||||||
@ -443,6 +480,7 @@ mod tests {
|
|||||||
registry.register_type_data::<String, ReflectSerialize>();
|
registry.register_type_data::<String, ReflectSerialize>();
|
||||||
registry.register::<[usize; 3]>();
|
registry.register::<[usize; 3]>();
|
||||||
registry.register::<(f32, f32)>();
|
registry.register::<(f32, f32)>();
|
||||||
|
registry.register::<MyResource>();
|
||||||
}
|
}
|
||||||
world.insert_resource(registry);
|
world.insert_resource(registry);
|
||||||
world
|
world
|
||||||
@ -456,11 +494,19 @@ mod tests {
|
|||||||
let b = world.spawn((Foo(123), Bar(345))).id();
|
let b = world.spawn((Foo(123), Bar(345))).id();
|
||||||
let c = world.spawn((Foo(123), Bar(345), Baz(789))).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);
|
let mut builder = DynamicSceneBuilder::from_world(&world);
|
||||||
builder.extract_entities([a, b, c].into_iter());
|
builder.extract_entities([a, b, c].into_iter());
|
||||||
|
builder.extract_resources();
|
||||||
let scene = builder.build();
|
let scene = builder.build();
|
||||||
|
|
||||||
let expected = r#"(
|
let expected = r#"(
|
||||||
|
resources: {
|
||||||
|
"bevy_scene::serde::tests::MyResource": (
|
||||||
|
foo: 123,
|
||||||
|
),
|
||||||
|
},
|
||||||
entities: {
|
entities: {
|
||||||
0: (
|
0: (
|
||||||
components: {
|
components: {
|
||||||
@ -493,6 +539,11 @@ mod tests {
|
|||||||
let world = create_world();
|
let world = create_world();
|
||||||
|
|
||||||
let input = r#"(
|
let input = r#"(
|
||||||
|
resources: {
|
||||||
|
"bevy_scene::serde::tests::MyResource": (
|
||||||
|
foo: 123,
|
||||||
|
),
|
||||||
|
},
|
||||||
entities: {
|
entities: {
|
||||||
0: (
|
0: (
|
||||||
components: {
|
components: {
|
||||||
@ -520,6 +571,11 @@ mod tests {
|
|||||||
};
|
};
|
||||||
let scene = scene_deserializer.deserialize(&mut deserializer).unwrap();
|
let scene = scene_deserializer.deserialize(&mut deserializer).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
1,
|
||||||
|
scene.resources.len(),
|
||||||
|
"expected `resources` to contain 1 resource"
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
3,
|
3,
|
||||||
scene.entities.len(),
|
scene.entities.len(),
|
||||||
@ -530,6 +586,11 @@ mod tests {
|
|||||||
let mut dst_world = create_world();
|
let mut dst_world = create_world();
|
||||||
scene.write_to_world(&mut dst_world, &mut map).unwrap();
|
scene.write_to_world(&mut dst_world, &mut map).unwrap();
|
||||||
|
|
||||||
|
let my_resource = dst_world.get_resource::<MyResource>();
|
||||||
|
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!(3, dst_world.query::<&Foo>().iter(&dst_world).count());
|
||||||
assert_eq!(2, dst_world.query::<&Bar>().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());
|
assert_eq!(1, dst_world.query::<&Baz>().iter(&dst_world).count());
|
||||||
@ -554,10 +615,10 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
vec![
|
vec![
|
||||||
1, 0, 1, 37, 98, 101, 118, 121, 95, 115, 99, 101, 110, 101, 58, 58, 115, 101, 114,
|
0, 1, 0, 1, 37, 98, 101, 118, 121, 95, 115, 99, 101, 110, 101, 58, 58, 115, 101,
|
||||||
100, 101, 58, 58, 116, 101, 115, 116, 115, 58, 58, 77, 121, 67, 111, 109, 112, 111,
|
114, 100, 101, 58, 58, 116, 101, 115, 116, 115, 58, 58, 77, 121, 67, 111, 109, 112,
|
||||||
110, 101, 110, 116, 1, 2, 3, 102, 102, 166, 63, 205, 204, 108, 64, 1, 12, 72, 101,
|
111, 110, 101, 110, 116, 1, 2, 3, 102, 102, 166, 63, 205, 204, 108, 64, 1, 12, 72,
|
||||||
108, 108, 111, 32, 87, 111, 114, 108, 100, 33
|
101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33
|
||||||
],
|
],
|
||||||
serialized_scene
|
serialized_scene
|
||||||
);
|
);
|
||||||
@ -594,11 +655,11 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
vec![
|
vec![
|
||||||
145, 129, 0, 145, 129, 217, 37, 98, 101, 118, 121, 95, 115, 99, 101, 110, 101, 58,
|
146, 128, 129, 0, 145, 129, 217, 37, 98, 101, 118, 121, 95, 115, 99, 101, 110, 101,
|
||||||
58, 115, 101, 114, 100, 101, 58, 58, 116, 101, 115, 116, 115, 58, 58, 77, 121, 67,
|
58, 58, 115, 101, 114, 100, 101, 58, 58, 116, 101, 115, 116, 115, 58, 58, 77, 121,
|
||||||
111, 109, 112, 111, 110, 101, 110, 116, 147, 147, 1, 2, 3, 146, 202, 63, 166, 102,
|
67, 111, 109, 112, 111, 110, 101, 110, 116, 147, 147, 1, 2, 3, 146, 202, 63, 166,
|
||||||
102, 202, 64, 108, 204, 205, 129, 165, 84, 117, 112, 108, 101, 172, 72, 101, 108,
|
102, 102, 202, 64, 108, 204, 205, 129, 165, 84, 117, 112, 108, 101, 172, 72, 101,
|
||||||
108, 111, 32, 87, 111, 114, 108, 100, 33
|
108, 108, 111, 32, 87, 111, 114, 108, 100, 33
|
||||||
],
|
],
|
||||||
buf
|
buf
|
||||||
);
|
);
|
||||||
@ -635,12 +696,12 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
vec![
|
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, 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,
|
||||||
0, 98, 101, 118, 121, 95, 115, 99, 101, 110, 101, 58, 58, 115, 101, 114, 100, 101,
|
37, 0, 0, 0, 0, 0, 0, 0, 98, 101, 118, 121, 95, 115, 99, 101, 110, 101, 58, 58,
|
||||||
58, 58, 116, 101, 115, 116, 115, 58, 58, 77, 121, 67, 111, 109, 112, 111, 110, 101,
|
115, 101, 114, 100, 101, 58, 58, 116, 101, 115, 116, 115, 58, 58, 77, 121, 67, 111,
|
||||||
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,
|
109, 112, 111, 110, 101, 110, 116, 1, 0, 0, 0, 0, 0, 0, 0, 2, 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,
|
3, 0, 0, 0, 0, 0, 0, 0, 102, 102, 166, 63, 205, 204, 108, 64, 1, 0, 0, 0, 12, 0, 0,
|
||||||
108, 108, 111, 32, 87, 111, 114, 108, 100, 33
|
0, 0, 0, 0, 0, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33
|
||||||
],
|
],
|
||||||
serialized_scene
|
serialized_scene
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
//! This example illustrates loading scenes from files.
|
//! This example illustrates loading scenes from files.
|
||||||
use std::fs::File;
|
|
||||||
use std::io::Write;
|
|
||||||
|
|
||||||
use bevy::{prelude::*, tasks::IoTaskPool, utils::Duration};
|
use bevy::{prelude::*, tasks::IoTaskPool, utils::Duration};
|
||||||
|
use std::{fs::File, io::Write};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
App::new()
|
App::new()
|
||||||
@ -14,6 +12,7 @@ fn main() {
|
|||||||
}))
|
}))
|
||||||
.register_type::<ComponentA>()
|
.register_type::<ComponentA>()
|
||||||
.register_type::<ComponentB>()
|
.register_type::<ComponentB>()
|
||||||
|
.register_type::<ResourceA>()
|
||||||
.add_systems(
|
.add_systems(
|
||||||
Startup,
|
Startup,
|
||||||
(save_scene_system, load_scene_system, infotext_system),
|
(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
|
// 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";
|
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<AssetServer>) {
|
|||||||
|
|
||||||
// This system logs all ComponentA components in our world. Try making a change to a ComponentA in
|
// 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.
|
// load_scene_example.scn. You should immediately see the changes appear in the console.
|
||||||
fn log_system(query: Query<(Entity, &ComponentA), Changed<ComponentA>>) {
|
fn log_system(
|
||||||
|
query: Query<(Entity, &ComponentA), Changed<ComponentA>>,
|
||||||
|
res: Option<Res<ResourceA>>,
|
||||||
|
) {
|
||||||
for (entity, component_a) in &query {
|
for (entity, component_a) in &query {
|
||||||
info!(" Entity({})", entity.index());
|
info!(" Entity({})", entity.index());
|
||||||
info!(
|
info!(
|
||||||
@ -83,6 +92,11 @@ fn log_system(query: Query<(Entity, &ComponentA), Changed<ComponentA>>) {
|
|||||||
component_a.x, component_a.y
|
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) {
|
fn save_scene_system(world: &mut World) {
|
||||||
@ -97,6 +111,7 @@ fn save_scene_system(world: &mut World) {
|
|||||||
Transform::IDENTITY,
|
Transform::IDENTITY,
|
||||||
));
|
));
|
||||||
scene_world.spawn(ComponentA { x: 3.0, y: 4.0 });
|
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
|
// The TypeRegistry resource contains information about all registered types (including
|
||||||
// components). This is used to construct scenes.
|
// components). This is used to construct scenes.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user