bevy_dynamic_bundle/
lib.rs

1use 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
85// Implementation for Commands
86impl<'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.spawn(()).dyn_insert(dyn_bundle.clone());
111            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(()).dyn_insert(dyn_bundle.clone());
136            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}