Skip to content

Commit fb9a65f

Browse files
ShaturMrGVSV
andauthored
bevy_scene: Add ReflectBundle (#9165)
# Objective Similar to #6344, but contains only `ReflectBundle` changes. Useful for scripting. The implementation has also been updated to look exactly like `ReflectComponent`. --- ## Changelog ### Added - Reflection for bundles. --------- Co-authored-by: Gino Valente <[email protected]>
1 parent 44f677a commit fb9a65f

File tree

2 files changed

+220
-0
lines changed

2 files changed

+220
-0
lines changed

crates/bevy_ecs/src/reflect/bundle.rs

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
//! Definitions for [`Bundle`] reflection.
2+
//!
3+
//! This module exports two types: [`ReflectBundleFns`] and [`ReflectBundle`].
4+
//!
5+
//! Same as [`super::component`], but for bundles.
6+
use std::any::TypeId;
7+
8+
use crate::{
9+
prelude::Bundle,
10+
world::{EntityMut, FromWorld, World},
11+
};
12+
use bevy_reflect::{FromType, Reflect, ReflectRef, TypeRegistry};
13+
14+
use super::ReflectComponent;
15+
16+
/// A struct used to operate on reflected [`Bundle`] of a type.
17+
///
18+
/// A [`ReflectBundle`] for type `T` can be obtained via
19+
/// [`bevy_reflect::TypeRegistration::data`].
20+
#[derive(Clone)]
21+
pub struct ReflectBundle(ReflectBundleFns);
22+
23+
/// The raw function pointers needed to make up a [`ReflectBundle`].
24+
///
25+
/// The also [`super::component::ReflectComponentFns`].
26+
#[derive(Clone)]
27+
pub struct ReflectBundleFns {
28+
/// Function pointer implementing [`ReflectBundle::from_world()`].
29+
pub from_world: fn(&mut World) -> Box<dyn Reflect>,
30+
/// Function pointer implementing [`ReflectBundle::insert()`].
31+
pub insert: fn(&mut EntityMut, &dyn Reflect),
32+
/// Function pointer implementing [`ReflectBundle::apply()`].
33+
pub apply: fn(&mut EntityMut, &dyn Reflect, &TypeRegistry),
34+
/// Function pointer implementing [`ReflectBundle::apply_or_insert()`].
35+
pub apply_or_insert: fn(&mut EntityMut, &dyn Reflect, &TypeRegistry),
36+
/// Function pointer implementing [`ReflectBundle::remove()`].
37+
pub remove: fn(&mut EntityMut),
38+
}
39+
40+
impl ReflectBundleFns {
41+
/// Get the default set of [`ReflectBundleFns`] for a specific bundle type using its
42+
/// [`FromType`] implementation.
43+
///
44+
/// This is useful if you want to start with the default implementation before overriding some
45+
/// of the functions to create a custom implementation.
46+
pub fn new<T: Bundle + Reflect + FromWorld>() -> Self {
47+
<ReflectBundle as FromType<T>>::from_type().0
48+
}
49+
}
50+
51+
impl ReflectBundle {
52+
/// Constructs default reflected [`Bundle`] from world using [`from_world()`](FromWorld::from_world).
53+
pub fn from_world(&self, world: &mut World) -> Box<dyn Reflect> {
54+
(self.0.from_world)(world)
55+
}
56+
57+
/// Insert a reflected [`Bundle`] into the entity like [`insert()`](crate::world::EntityMut::insert).
58+
pub fn insert(&self, entity: &mut EntityMut, bundle: &dyn Reflect) {
59+
(self.0.insert)(entity, bundle);
60+
}
61+
62+
/// Uses reflection to set the value of this [`Bundle`] type in the entity to the given value.
63+
///
64+
/// # Panics
65+
///
66+
/// Panics if there is no [`Bundle`] of the given type.
67+
pub fn apply(&self, entity: &mut EntityMut, bundle: &dyn Reflect, registry: &TypeRegistry) {
68+
(self.0.apply)(entity, bundle, registry);
69+
}
70+
71+
/// Uses reflection to set the value of this [`Bundle`] type in the entity to the given value or insert a new one if it does not exist.
72+
pub fn apply_or_insert(
73+
&self,
74+
entity: &mut EntityMut,
75+
bundle: &dyn Reflect,
76+
registry: &TypeRegistry,
77+
) {
78+
(self.0.apply_or_insert)(entity, bundle, registry);
79+
}
80+
81+
/// Removes this [`Bundle`] type from the entity. Does nothing if it doesn't exist.
82+
pub fn remove(&self, entity: &mut EntityMut) {
83+
(self.0.remove)(entity);
84+
}
85+
86+
/// Create a custom implementation of [`ReflectBundle`].
87+
///
88+
/// This is an advanced feature,
89+
/// useful for scripting implementations,
90+
/// that should not be used by most users
91+
/// unless you know what you are doing.
92+
///
93+
/// Usually you should derive [`Reflect`] and add the `#[reflect(Bundle)]` bundle
94+
/// to generate a [`ReflectBundle`] implementation automatically.
95+
///
96+
/// See [`ReflectBundleFns`] for more information.
97+
pub fn new(fns: ReflectBundleFns) -> Self {
98+
Self(fns)
99+
}
100+
101+
/// The underlying function pointers implementing methods on `ReflectBundle`.
102+
///
103+
/// This is useful when you want to keep track locally of an individual
104+
/// function pointer.
105+
///
106+
/// Calling [`TypeRegistry::get`] followed by
107+
/// [`TypeRegistration::data::<ReflectBundle>`] can be costly if done several
108+
/// times per frame. Consider cloning [`ReflectBundle`] and keeping it
109+
/// between frames, cloning a `ReflectBundle` is very cheap.
110+
///
111+
/// If you only need a subset of the methods on `ReflectBundle`,
112+
/// use `fn_pointers` to get the underlying [`ReflectBundleFns`]
113+
/// and copy the subset of function pointers you care about.
114+
///
115+
/// [`TypeRegistration::data::<ReflectBundle>`]: bevy_reflect::TypeRegistration::data
116+
/// [`TypeRegistry::get`]: bevy_reflect::TypeRegistry::get
117+
pub fn fn_pointers(&self) -> &ReflectBundleFns {
118+
&self.0
119+
}
120+
}
121+
122+
impl<B: Bundle + Reflect + FromWorld> FromType<B> for ReflectBundle {
123+
fn from_type() -> Self {
124+
ReflectBundle(ReflectBundleFns {
125+
from_world: |world| Box::new(B::from_world(world)),
126+
insert: |entity, reflected_bundle| {
127+
let mut bundle = entity.world_scope(|world| B::from_world(world));
128+
bundle.apply(reflected_bundle);
129+
entity.insert(bundle);
130+
},
131+
apply: |entity, reflected_bundle, registry| {
132+
let mut bundle = entity.world_scope(|world| B::from_world(world));
133+
bundle.apply(reflected_bundle);
134+
135+
match bundle.reflect_ref() {
136+
ReflectRef::Struct(bundle) => bundle
137+
.iter_fields()
138+
.for_each(|field| insert_field::<B>(entity, field, registry)),
139+
ReflectRef::Tuple(bundle) => bundle
140+
.iter_fields()
141+
.for_each(|field| insert_field::<B>(entity, field, registry)),
142+
_ => panic!(
143+
"expected bundle `{}` to be named struct or tuple",
144+
std::any::type_name::<B>()
145+
),
146+
}
147+
},
148+
apply_or_insert: |entity, reflected_bundle, registry| {
149+
let mut bundle = entity.world_scope(|world| B::from_world(world));
150+
bundle.apply(reflected_bundle);
151+
152+
match bundle.reflect_ref() {
153+
ReflectRef::Struct(bundle) => bundle
154+
.iter_fields()
155+
.for_each(|field| apply_or_insert_field::<B>(entity, field, registry)),
156+
ReflectRef::Tuple(bundle) => bundle
157+
.iter_fields()
158+
.for_each(|field| apply_or_insert_field::<B>(entity, field, registry)),
159+
_ => panic!(
160+
"expected bundle `{}` to be named struct or tuple",
161+
std::any::type_name::<B>()
162+
),
163+
}
164+
},
165+
remove: |entity| {
166+
entity.remove::<B>();
167+
},
168+
})
169+
}
170+
}
171+
172+
fn insert_field<B: 'static>(entity: &mut EntityMut, field: &dyn Reflect, registry: &TypeRegistry) {
173+
if let Some(reflect_component) = registry.get_type_data::<ReflectComponent>(field.type_id()) {
174+
reflect_component.apply(entity, field);
175+
} else if let Some(reflect_bundle) = registry.get_type_data::<ReflectBundle>(field.type_id()) {
176+
reflect_bundle.apply(entity, field, registry);
177+
} else {
178+
entity.world_scope(|world| {
179+
if world.components().get_id(TypeId::of::<B>()).is_some() {
180+
panic!(
181+
"no `ReflectComponent` registration found for `{}`",
182+
field.type_name()
183+
);
184+
};
185+
});
186+
187+
panic!(
188+
"no `ReflectBundle` registration found for `{}`",
189+
field.type_name()
190+
)
191+
}
192+
}
193+
194+
fn apply_or_insert_field<B: 'static>(
195+
entity: &mut EntityMut,
196+
field: &dyn Reflect,
197+
registry: &TypeRegistry,
198+
) {
199+
if let Some(reflect_component) = registry.get_type_data::<ReflectComponent>(field.type_id()) {
200+
reflect_component.apply_or_insert(entity, field);
201+
} else if let Some(reflect_bundle) = registry.get_type_data::<ReflectBundle>(field.type_id()) {
202+
reflect_bundle.apply_or_insert(entity, field, registry);
203+
} else {
204+
entity.world_scope(|world| {
205+
if world.components().get_id(TypeId::of::<B>()).is_some() {
206+
panic!(
207+
"no `ReflectComponent` registration found for `{}`",
208+
field.type_name()
209+
);
210+
};
211+
});
212+
213+
panic!(
214+
"no `ReflectBundle` registration found for `{}`",
215+
field.type_name()
216+
)
217+
}
218+
}

crates/bevy_ecs/src/reflect/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ use crate as bevy_ecs;
66
use crate::{entity::Entity, system::Resource};
77
use bevy_reflect::{impl_reflect_value, ReflectDeserialize, ReflectSerialize, TypeRegistryArc};
88

9+
mod bundle;
910
mod component;
1011
mod map_entities;
1112
mod resource;
1213

14+
pub use bundle::{ReflectBundle, ReflectBundleFns};
1315
pub use component::{ReflectComponent, ReflectComponentFns};
1416
pub use map_entities::ReflectMapEntities;
1517
pub use resource::{ReflectResource, ReflectResourceFns};

0 commit comments

Comments
 (0)