bevy_dynamic_bundle/
lib.rs1use bevy::ecs::system::{
2 EntityCommands, EntityCommand
3};
4use bevy::prelude::{
5 Entity, World, Bundle, Commands
6};
7
8use dyn_clone::DynClone;
9
10pub mod prelude{
11 pub use super::{
12 DynamicBundel,
13 DynamicSpawn,
14 DynamicInsert,
15 };
16}
17
18fn insert<T: Bundle + Clone>(bundle: T) -> impl DynEntityCommand {
19 move |entity: Entity, world: &mut World| {
20 if let Some(mut entity) = world.get_entity_mut(entity) {
21 entity.insert(bundle);
22 } else {
23 panic!("error[B0003]: Could not insert a bundle (of type `{}`) for entity {:?} because it doesn't exist in this World.", std::any::type_name::<T>(), entity);
24 }
25 }
26}
27
28trait DynEntityCommand<Marker = ()>: DynClone + Send + Sync + 'static {
29 fn apply(self: Box<Self>, id: Entity, world: &mut World);
30}
31
32impl<F> DynEntityCommand for F
33where
34 F: FnOnce(Entity, &mut World) + DynClone + Send + Sync + 'static,
35{
36 fn apply(self: Box<Self>, id: Entity, world: &mut World) {
37 self(id, world);
38 }
39}
40
41impl EntityCommand for Box<dyn DynEntityCommand> {
42 fn apply(self: Self, id: Entity, world: &mut World) {
43 self.apply(id, world);
44 }
45}
46
47dyn_clone::clone_trait_object!(DynEntityCommand);
48#[derive(Clone)]
49pub struct DynamicBundel {
50 #[allow(dead_code)]
51 bundle_fn: Box<dyn DynEntityCommand>
52}
53
54impl DynamicBundel {
55 pub fn new<T: Bundle + Clone>(bundle: T) -> DynamicBundel {
56 DynamicBundel {
57 bundle_fn: Box::new(insert(bundle))
58 }
59 }
60}
61
62impl<T: Bundle + Clone> From<T> for DynamicBundel {
63 fn from(bundle: T) -> Self {
64 DynamicBundel::new(bundle)
65 }
66}
67
68#[allow(dead_code)]
69pub trait DynamicInsert<'a> {
70 fn dyn_insert(&mut self, dyn_bundel: DynamicBundel) -> &mut EntityCommands<'a>;
71}
72
73impl<'a> DynamicInsert<'a> for EntityCommands<'a> {
74 fn dyn_insert(&mut self, dyn_bundel: DynamicBundel) -> &mut EntityCommands<'a> {
75 self.add(dyn_bundel.bundle_fn);
76 self
77 }
78}
79
80#[allow(dead_code)]
81pub trait DynamicSpawn{
82 fn dyn_spawn(&mut self, dyn_bundel: DynamicBundel) -> EntityCommands;
83}
84
85impl<'a, 'b> DynamicSpawn for Commands<'a, 'b> {
87 fn dyn_spawn(&mut self, dyn_bundel: DynamicBundel) -> EntityCommands {
88 let mut entity_commands = self.spawn(());
89 entity_commands.dyn_insert(dyn_bundel);
90 entity_commands
91 }
92}
93
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98 use bevy::prelude::*;
99
100 #[test]
101 fn simple_dyn_bundle_test() {
102 #[derive(Component, Clone)]
103 struct ComponentA(i32);
104
105 App::new().add_systems(Startup, (setup, query).chain()).run();
106
107 fn setup(mut commands: Commands) {
108 let dyn_bundle = DynamicBundel::new(ComponentA(2));
109
110 commands.dyn_spawn(dyn_bundle);
112
113
114 }
115
116 fn query(components: Query<&ComponentA>) {
117 assert_eq!(2 ,components.get_single().unwrap().0);
118 }
119
120 }
121
122 #[test]
123 fn spawner_test() {
124 #[derive(Component, Clone)]
125 struct Spawner(DynamicBundel);
126
127 #[derive(Component, Clone)]
128 struct ComponentA(i32);
129
130 App::new().add_systems(Startup, (setup, spawn, query).chain()).run();
131
132 fn setup(mut commands: Commands) {
133 let dyn_bundle = DynamicBundel::new(ComponentA(2));
134
135 commands.spawn(Spawner(dyn_bundle));
137
138
139 }
140
141 fn spawn(mut commands: Commands, spawner_q: Query<&Spawner>) {
142 let spawner = spawner_q.get_single().unwrap();
143 commands.dyn_spawn(spawner.0.clone());
144 }
145
146 fn query(components: Query<&ComponentA>) {
147 assert_eq!(2 ,components.get_single().unwrap().0);
148 }
149
150 }
151
152}