Skip to content

Commit 894334b

Browse files
committed
bevy_scene: Use map for scene components (#6345)
# Objective Currently scenes define components using a list: ```rust [ ( entity: 0, components: [ { "bevy_transform::components::transform::Transform": ( translation: ( x: 0.0, y: 0.0, z: 0.0 ), rotation: (0.0, 0.0, 0.0, 1.0), scale: ( x: 1.0, y: 1.0, z: 1.0 ), ), }, { "my_crate::Foo": ( text: "Hello World", ), }, { "my_crate::Bar": ( baz: 123, ), }, ], ), ] ``` However, this representation has some drawbacks (as pointed out by @Metadorius in [this](#4561 (comment)) comment): 1. Increased nesting and more characters (minor effect on overall size) 2. More importantly, by definition, entities cannot have more than one instance of any given component. Therefore, such data is best stored as a map— where all values are meant to have unique keys. ## Solution Change `components` to store a map of components rather than a list: ```rust [ ( entity: 0, components: { "bevy_transform::components::transform::Transform": ( translation: ( x: 0.0, y: 0.0, z: 0.0 ), rotation: (0.0, 0.0, 0.0, 1.0), scale: ( x: 1.0, y: 1.0, z: 1.0 ), ), "my_crate::Foo": ( text: "Hello World", ), "my_crate::Bar": ( baz: 123 ), }, ), ] ``` #### Code Representation This change only affects the scene format itself. `DynamicEntity` still stores its components as a list. The reason for this is that storing such data as a map is not really needed since: 1. The "key" of each value is easily found by just calling `Reflect::type_name` on it 2. We should be generating such structs using the `World` itself which upholds the one-component-per-entity rule One could in theory create manually create a `DynamicEntity` with duplicate components, but this isn't something I think we should focus on in this PR. `DynamicEntity` can be broken in other ways (i.e. storing a non-component in the components list), and resolving its issues can be done in a separate PR. --- ## Changelog * The scene format now uses a map to represent the collection of components rather than a list ## Migration Guide The scene format now uses a map to represent the collection of components. Scene files will need to update from the old list format. <details> <summary>Example Code</summary> ```rust // OLD [ ( entity: 0, components: [ { "bevy_transform::components::transform::Transform": ( translation: ( x: 0.0, y: 0.0, z: 0.0 ), rotation: (0.0, 0.0, 0.0, 1.0), scale: ( x: 1.0, y: 1.0, z: 1.0 ), ), }, { "my_crate::Foo": ( text: "Hello World", ), }, { "my_crate::Bar": ( baz: 123, ), }, ], ), ] // NEW [ ( entity: 0, components: { "bevy_transform::components::transform::Transform": ( translation: ( x: 0.0, y: 0.0, z: 0.0 ), rotation: (0.0, 0.0, 0.0, 1.0), scale: ( x: 1.0, y: 1.0, z: 1.0 ), ), "my_crate::Foo": ( text: "Hello World", ), "my_crate::Bar": ( baz: 123 ), }, ), ] ``` </details>
1 parent 4d3d3c8 commit 894334b

File tree

2 files changed

+64
-47
lines changed

2 files changed

+64
-47
lines changed

assets/scenes/load_scene_example.scn.ron

+25-33
Original file line numberDiff line numberDiff line change
@@ -2,45 +2,37 @@
22
entities: [
33
(
44
entity: 0,
5-
components: [
6-
{
7-
"bevy_transform::components::transform::Transform": (
8-
translation: (
9-
x: 0.0,
10-
y: 0.0,
11-
z: 0.0
12-
),
13-
rotation: (0.0, 0.0, 0.0, 1.0),
14-
scale: (
15-
x: 1.0,
16-
y: 1.0,
17-
z: 1.0
18-
),
5+
components: {
6+
"bevy_transform::components::transform::Transform": (
7+
translation: (
8+
x: 0.0,
9+
y: 0.0,
10+
z: 0.0
1911
),
20-
},
21-
{
22-
"scene::ComponentB": (
23-
value: "hello",
24-
),
25-
},
26-
{
27-
"scene::ComponentA": (
12+
rotation: (0.0, 0.0, 0.0, 1.0),
13+
scale: (
2814
x: 1.0,
29-
y: 2.0,
15+
y: 1.0,
16+
z: 1.0
3017
),
31-
},
32-
],
18+
),
19+
"scene::ComponentB": (
20+
value: "hello",
21+
),
22+
"scene::ComponentA": (
23+
x: 1.0,
24+
y: 2.0,
25+
),
26+
},
3327
),
3428
(
3529
entity: 1,
36-
components: [
37-
{
38-
"scene::ComponentA": (
39-
x: 3.0,
40-
y: 4.0,
41-
),
42-
},
43-
],
30+
components: {
31+
"scene::ComponentA": (
32+
x: 3.0,
33+
y: 4.0,
34+
),
35+
},
4436
),
4537
]
4638
)

crates/bevy_scene/src/serde.rs

+39-14
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use crate::{DynamicEntity, DynamicScene};
22
use anyhow::Result;
3-
use bevy_reflect::{
4-
serde::{ReflectSerializer, UntypedReflectDeserializer},
5-
Reflect, TypeRegistry, TypeRegistryArc,
6-
};
3+
use bevy_reflect::serde::{TypedReflectDeserializer, TypedReflectSerializer};
4+
use bevy_reflect::{serde::UntypedReflectDeserializer, Reflect, TypeRegistry, TypeRegistryArc};
5+
use bevy_utils::HashSet;
6+
use serde::ser::SerializeMap;
77
use serde::{
88
de::{DeserializeSeed, Error, MapAccess, SeqAccess, Visitor},
99
ser::{SerializeSeq, SerializeStruct},
@@ -100,10 +100,12 @@ impl<'a> Serialize for ComponentsSerializer<'a> {
100100
where
101101
S: serde::Serializer,
102102
{
103-
let mut state = serializer.serialize_seq(Some(self.components.len()))?;
103+
let mut state = serializer.serialize_map(Some(self.components.len()))?;
104104
for component in self.components {
105-
state
106-
.serialize_element(&ReflectSerializer::new(&**component, &self.registry.read()))?;
105+
state.serialize_entry(
106+
component.type_name(),
107+
&TypedReflectSerializer::new(&**component, &*self.registry.read()),
108+
)?;
107109
}
108110
state.end()
109111
}
@@ -285,7 +287,7 @@ impl<'a, 'de> Visitor<'de> for SceneEntityVisitor<'a> {
285287
return Err(Error::duplicate_field(ENTITY_FIELD_COMPONENTS));
286288
}
287289

288-
components = Some(map.next_value_seed(ComponentVecDeserializer {
290+
components = Some(map.next_value_seed(ComponentDeserializer {
289291
registry: self.registry,
290292
})?);
291293
}
@@ -306,32 +308,55 @@ impl<'a, 'de> Visitor<'de> for SceneEntityVisitor<'a> {
306308
}
307309
}
308310

309-
pub struct ComponentVecDeserializer<'a> {
311+
pub struct ComponentDeserializer<'a> {
310312
pub registry: &'a TypeRegistry,
311313
}
312314

313-
impl<'a, 'de> DeserializeSeed<'de> for ComponentVecDeserializer<'a> {
315+
impl<'a, 'de> DeserializeSeed<'de> for ComponentDeserializer<'a> {
314316
type Value = Vec<Box<dyn Reflect>>;
315317

316318
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
317319
where
318320
D: serde::Deserializer<'de>,
319321
{
320-
deserializer.deserialize_seq(ComponentSeqVisitor {
322+
deserializer.deserialize_map(ComponentVisitor {
321323
registry: self.registry,
322324
})
323325
}
324326
}
325327

326-
struct ComponentSeqVisitor<'a> {
328+
struct ComponentVisitor<'a> {
327329
pub registry: &'a TypeRegistry,
328330
}
329331

330-
impl<'a, 'de> Visitor<'de> for ComponentSeqVisitor<'a> {
332+
impl<'a, 'de> Visitor<'de> for ComponentVisitor<'a> {
331333
type Value = Vec<Box<dyn Reflect>>;
332334

333335
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
334-
formatter.write_str("list of components")
336+
formatter.write_str("map of components")
337+
}
338+
339+
fn visit_map<A>(self, mut map: A) -> std::result::Result<Self::Value, A::Error>
340+
where
341+
A: MapAccess<'de>,
342+
{
343+
let mut added = HashSet::new();
344+
let mut components = Vec::new();
345+
while let Some(key) = map.next_key::<&str>()? {
346+
if !added.insert(key) {
347+
return Err(Error::custom(format!("duplicate component: `{}`", key)));
348+
}
349+
350+
let registration = self
351+
.registry
352+
.get_with_name(key)
353+
.ok_or_else(|| Error::custom(format!("no registration found for `{}`", key)))?;
354+
components.push(
355+
map.next_value_seed(TypedReflectDeserializer::new(registration, self.registry))?,
356+
);
357+
}
358+
359+
Ok(components)
335360
}
336361

337362
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>

0 commit comments

Comments
 (0)