(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:
Francesco 2023-03-20 22:17:02 +01:00 committed by GitHub
parent 6a85eb3d7e
commit 7b38de0a64
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 297 additions and 64 deletions

View file

@ -1,4 +1,9 @@
(
resources: {
"scene::ResourceA": (
score: 2,
),
},
entities: {
0: (
components: {

View file

@ -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<Box<dyn Reflect>>,
pub entities: Vec<DynamicEntity>,
}
@ -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::<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 {
// 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

View file

@ -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<ComponentId, Box<dyn Reflect>>,
extracted_scene: BTreeMap<u32, DynamicEntity>,
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::<AppTypeRegistry>().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::<ReflectComponent>())
.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::<ReflectComponent>()?
.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::<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)]
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::<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>());
}
}

View file

@ -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::<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 scene_entity in archetype.entities() {
let entity = *instance_info

View file

@ -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::<T>()`")]
UnregisteredType { type_name: String },
#[error("scene does not exist")]

View file

@ -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<dyn Reflect>],
pub struct SceneMapSerializer<'a> {
pub entries: &'a [Box<dyn Reflect>],
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>
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<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
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<Box<dyn Reflect>>;
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
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<Box<dyn Reflect>>;
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>
@ -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<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
@ -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::<String, ReflectSerialize>();
registry.register::<[usize; 3]>();
registry.register::<(f32, f32)>();
registry.register::<MyResource>();
}
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::<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!(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
);

View file

@ -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::<ComponentA>()
.register_type::<ComponentB>()
.register_type::<ResourceA>()
.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<AssetServer>) {
// 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<ComponentA>>) {
fn log_system(
query: Query<(Entity, &ComponentA), Changed<ComponentA>>,
res: Option<Res<ResourceA>>,
) {
for (entity, component_a) in &query {
info!(" Entity({})", entity.index());
info!(
@ -83,6 +92,11 @@ fn log_system(query: Query<(Entity, &ComponentA), Changed<ComponentA>>) {
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.