Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_scene/src/scene.rs
9438 views
1
use core::any::TypeId;
2
3
use crate::reflect_utils::clone_reflect_value;
4
use crate::{DynamicScene, SceneSpawnError};
5
use bevy_asset::Asset;
6
use bevy_ecs::resource::IS_RESOURCE;
7
use bevy_ecs::{
8
component::ComponentCloneBehavior,
9
entity::{Entity, EntityHashMap, SceneEntityMapper},
10
entity_disabling::DefaultQueryFilters,
11
reflect::{AppTypeRegistry, ReflectComponent, ReflectResource},
12
relationship::RelationshipHookMode,
13
world::World,
14
};
15
use bevy_reflect::TypePath;
16
17
/// A composition of [`World`] objects.
18
///
19
/// To spawn a scene, you can use either:
20
/// * [`SceneSpawner::spawn`](crate::SceneSpawner::spawn)
21
/// * adding the [`SceneRoot`](crate::components::SceneRoot) component to an entity.
22
#[derive(Asset, TypePath, Debug)]
23
pub struct Scene {
24
/// The world of the scene, containing its entities and resources.
25
pub world: World,
26
}
27
28
impl Scene {
29
/// Creates a new scene with the given world.
30
pub fn new(world: World) -> Self {
31
Self { world }
32
}
33
34
/// Create a new scene from a given dynamic scene.
35
pub fn from_dynamic_scene(
36
dynamic_scene: &DynamicScene,
37
type_registry: &AppTypeRegistry,
38
) -> Result<Scene, SceneSpawnError> {
39
let mut world = World::new();
40
let mut entity_map = EntityHashMap::default();
41
dynamic_scene.write_to_world_with(&mut world, &mut entity_map, type_registry)?;
42
43
Ok(Self { world })
44
}
45
46
/// Clone the scene.
47
///
48
/// This method will return a [`SceneSpawnError`] if a type either is not registered in the
49
/// provided [`AppTypeRegistry`] or doesn't reflect the [`Component`](bevy_ecs::component::Component) trait.
50
pub fn clone_with(&self, type_registry: &AppTypeRegistry) -> Result<Scene, SceneSpawnError> {
51
let mut new_world = World::new();
52
let mut entity_map = EntityHashMap::default();
53
self.write_to_world_with(&mut new_world, &mut entity_map, type_registry)?;
54
Ok(Self { world: new_world })
55
}
56
57
/// Write the entities and their corresponding components to the given world.
58
///
59
/// This method will return a [`SceneSpawnError`] if a type either is not registered in the
60
/// provided [`AppTypeRegistry`] or doesn't reflect the [`Component`](bevy_ecs::component::Component) trait.
61
pub fn write_to_world_with(
62
&self,
63
world: &mut World,
64
entity_map: &mut EntityHashMap<Entity>,
65
type_registry: &AppTypeRegistry,
66
) -> Result<(), SceneSpawnError> {
67
let type_registry = type_registry.read();
68
69
let self_dqf_id = self
70
.world
71
.components()
72
.get_id(TypeId::of::<DefaultQueryFilters>());
73
74
// Resources archetype
75
for (component_id, source_entity) in self.world.resource_entities().iter() {
76
if Some(*component_id) == self_dqf_id {
77
continue;
78
}
79
if !world
80
.get_entity(*source_entity)
81
.ok()
82
.is_some_and(|entity_ref| entity_ref.contains_id(*component_id))
83
{
84
continue;
85
}
86
87
let component_info = self
88
.world
89
.components()
90
.get_info(*component_id)
91
.expect("component_ids in archetypes should have ComponentInfo");
92
93
let type_id = component_info
94
.type_id()
95
.expect("reflected resources must have a type_id");
96
97
let registration =
98
type_registry
99
.get(type_id)
100
.ok_or_else(|| SceneSpawnError::UnregisteredType {
101
std_type_name: component_info.name(),
102
})?;
103
registration.data::<ReflectResource>().ok_or_else(|| {
104
SceneSpawnError::UnregisteredResource {
105
type_path: registration.type_info().type_path().to_string(),
106
}
107
})?;
108
// reflect_resource existing, implies that reflect_component also exists
109
let reflect_component = registration
110
.data::<ReflectComponent>()
111
.expect("ReflectComponent is depended on ReflectResource");
112
113
// check if the resource already exists in the other world, if not spawn it
114
let destination_entity =
115
if let Some(entity) = world.resource_entities().get(*component_id) {
116
*entity
117
} else {
118
world.spawn_empty().id()
119
};
120
121
reflect_component.copy(
122
&self.world,
123
world,
124
*source_entity,
125
destination_entity,
126
&type_registry,
127
);
128
}
129
130
// Ensure that all scene entities have been allocated in the destination
131
// world before handling components that may contain references that need mapping.
132
for archetype in self.world.archetypes().iter() {
133
if archetype.contains(IS_RESOURCE) {
134
continue;
135
}
136
for scene_entity in archetype.entities() {
137
entity_map
138
.entry(scene_entity.id())
139
.or_insert_with(|| world.spawn_empty().id());
140
}
141
}
142
143
for archetype in self.world.archetypes().iter() {
144
if archetype.contains(IS_RESOURCE) {
145
continue;
146
}
147
for scene_entity in archetype.entities() {
148
let entity = *entity_map
149
.get(&scene_entity.id())
150
.expect("should have previously spawned an entity");
151
152
for component_id in archetype.iter_components() {
153
let component_info = self
154
.world
155
.components()
156
.get_info(component_id)
157
.expect("component_ids in archetypes should have ComponentInfo");
158
159
if matches!(
160
*component_info.clone_behavior(),
161
ComponentCloneBehavior::Ignore
162
) {
163
continue;
164
}
165
166
let registration = type_registry
167
.get(component_info.type_id().unwrap())
168
.ok_or_else(|| SceneSpawnError::UnregisteredType {
169
std_type_name: component_info.name(),
170
})?;
171
let reflect_component =
172
registration.data::<ReflectComponent>().ok_or_else(|| {
173
SceneSpawnError::UnregisteredComponent {
174
type_path: registration.type_info().type_path().to_string(),
175
}
176
})?;
177
178
let Some(component) = reflect_component
179
.reflect(self.world.entity(scene_entity.id()))
180
.map(|component| {
181
clone_reflect_value(component.as_partial_reflect(), registration)
182
})
183
else {
184
continue;
185
};
186
187
// If this component references entities in the scene,
188
// update them to the entities in the world.
189
SceneEntityMapper::world_scope(entity_map, world, |world, mapper| {
190
reflect_component.apply_or_insert_mapped(
191
&mut world.entity_mut(entity),
192
component.as_partial_reflect(),
193
&type_registry,
194
mapper,
195
RelationshipHookMode::Skip,
196
);
197
});
198
}
199
}
200
}
201
202
Ok(())
203
}
204
}
205
206