From e1597e6fa483cb0329130158bd6c5a85aaa07dfc Mon Sep 17 00:00:00 2001 From: lisk77 Date: Mon, 14 Jul 2025 01:54:53 +0200 Subject: [PATCH] feat(ecs): added a simple prefab system --- crates/comet_ecs/Cargo.toml | 0 crates/comet_ecs/component_derive/Cargo.toml | 0 crates/comet_ecs/component_derive/src/lib.rs | 0 crates/comet_ecs/src/archetypes.rs | 0 crates/comet_ecs/src/entity.rs | 0 crates/comet_ecs/src/id.rs | 0 crates/comet_ecs/src/lib.rs | 19 +- crates/comet_ecs/src/prefabs.rs | 46 ++ crates/comet_ecs/src/scene.rs | 556 ++++++++++--------- examples/prefabs.rs | 52 ++ 10 files changed, 416 insertions(+), 257 deletions(-) mode change 100644 => 100755 crates/comet_ecs/Cargo.toml mode change 100644 => 100755 crates/comet_ecs/component_derive/Cargo.toml mode change 100644 => 100755 crates/comet_ecs/component_derive/src/lib.rs mode change 100644 => 100755 crates/comet_ecs/src/archetypes.rs mode change 100644 => 100755 crates/comet_ecs/src/entity.rs mode change 100644 => 100755 crates/comet_ecs/src/id.rs mode change 100644 => 100755 crates/comet_ecs/src/lib.rs create mode 100644 crates/comet_ecs/src/prefabs.rs create mode 100644 examples/prefabs.rs diff --git a/crates/comet_ecs/Cargo.toml b/crates/comet_ecs/Cargo.toml old mode 100644 new mode 100755 diff --git a/crates/comet_ecs/component_derive/Cargo.toml b/crates/comet_ecs/component_derive/Cargo.toml old mode 100644 new mode 100755 diff --git a/crates/comet_ecs/component_derive/src/lib.rs b/crates/comet_ecs/component_derive/src/lib.rs old mode 100644 new mode 100755 diff --git a/crates/comet_ecs/src/archetypes.rs b/crates/comet_ecs/src/archetypes.rs old mode 100644 new mode 100755 diff --git a/crates/comet_ecs/src/entity.rs b/crates/comet_ecs/src/entity.rs old mode 100644 new mode 100755 diff --git a/crates/comet_ecs/src/id.rs b/crates/comet_ecs/src/id.rs old mode 100644 new mode 100755 diff --git a/crates/comet_ecs/src/lib.rs b/crates/comet_ecs/src/lib.rs old mode 100644 new mode 100755 index 53429a6..ce12b40 --- a/crates/comet_ecs/src/lib.rs +++ b/crates/comet_ecs/src/lib.rs @@ -1,12 +1,15 @@ -pub use entity::*; -pub use component::*; -pub use scene::*; -pub use id::*; -pub use component_derive::*; pub use comet_math as math; +pub use component::*; +pub use component_derive::*; +pub use entity::*; +pub use id::*; +pub use prefabs::PrefabFactory; +pub use scene::*; -mod entity; +mod archetypes; mod component; -mod scene; +mod entity; mod id; -mod archetypes; \ No newline at end of file +mod prefabs; +mod scene; + diff --git a/crates/comet_ecs/src/prefabs.rs b/crates/comet_ecs/src/prefabs.rs new file mode 100644 index 0000000..5fe8698 --- /dev/null +++ b/crates/comet_ecs/src/prefabs.rs @@ -0,0 +1,46 @@ +use comet_structs::FlatMap; + +pub type PrefabFactory = fn(&mut crate::Scene) -> usize; + +pub(crate) struct PrefabManager { + pub(crate) prefabs: FlatMap, +} + +impl PrefabManager { + pub fn new() -> Self { + Self { + prefabs: FlatMap::new(), + } + } + + pub fn register(&mut self, name: &str, factory: PrefabFactory) { + self.prefabs.insert(name.to_string(), factory); + } + + pub fn has_prefab(&self, name: &str) -> bool { + self.prefabs.contains(&name.to_string()) + } +} + +#[macro_export] +macro_rules! register_prefab { + ($scene:expr, $name:expr, $($component:expr),* $(,)?) => { + { + fn prefab_factory(scene: &mut $crate::Scene) -> usize { + let entity = scene.new_entity() as usize; + $( + scene.add_component(entity, $component); + )* + entity + } + $scene.register_prefab($name, prefab_factory); + } + }; +} + +#[macro_export] +macro_rules! spawn_prefab { + ($scene:expr, $name:expr) => { + $scene.spawn_prefab($name) + }; +} diff --git a/crates/comet_ecs/src/scene.rs b/crates/comet_ecs/src/scene.rs index b05be2a..8c2d83d 100755 --- a/crates/comet_ecs/src/scene.rs +++ b/crates/comet_ecs/src/scene.rs @@ -1,297 +1,355 @@ -use std::any::TypeId; -use crate::{ - entity, Component, Entity, IdQueue -}; +use crate::archetypes::Archetypes; +use crate::prefabs::PrefabManager; +use crate::{Component, Entity, IdQueue}; use comet_log::*; use comet_structs::*; -use crate::archetypes::Archetypes; +use std::any::TypeId; pub struct Scene { - id_queue: IdQueue, - next_id: u32, - entities: Vec>, - components: ComponentStorage, - archetypes: Archetypes + id_queue: IdQueue, + next_id: u32, + entities: Vec>, + components: ComponentStorage, + archetypes: Archetypes, + prefabs: PrefabManager, } impl Scene { - pub fn new() -> Self { - Self { - id_queue: IdQueue::new(), - next_id: 0, - entities: Vec::new(), - components: ComponentStorage::new(), - archetypes: Archetypes::new() - } - } + pub fn new() -> Self { + Self { + id_queue: IdQueue::new(), + next_id: 0, + entities: Vec::new(), + components: ComponentStorage::new(), + archetypes: Archetypes::new(), + prefabs: PrefabManager::new(), + } + } - /// Returns the number of how many entities exist in the current Scene. - pub fn active_entities(&self) -> u32 { - self.entities.len() as u32 - self.id_queue.size() - } + /// Returns the number of how many entities exist in the current Scene. + pub fn active_entities(&self) -> u32 { + self.entities.len() as u32 - self.id_queue.size() + } - fn get_next_id(&mut self) { - if self.id_queue.is_empty() { - self.next_id = self.entities.len() as u32; - return; - } - if self.next_id > self.id_queue.front().unwrap() || self.entities[self.next_id as usize] != None { - self.next_id = self.id_queue.dequeue().unwrap(); - } - } + fn get_next_id(&mut self) { + if self.id_queue.is_empty() { + self.next_id = self.entities.len() as u32; + return; + } + if self.next_id > self.id_queue.front().unwrap() + || self.entities[self.next_id as usize] != None + { + self.next_id = self.id_queue.dequeue().unwrap(); + } + } - /// Retuns the `Vec` of `Option` which contains all the entities in the current Scene. - pub fn entities(&self) -> &Vec> { - &self.entities - } + /// Retuns the `Vec` of `Option` which contains all the entities in the current Scene. + pub fn entities(&self) -> &Vec> { + &self.entities + } - /// Creates a new entity and returns its ID. - pub fn new_entity(&mut self) -> u32 { - let id = self.next_id; - if (self.next_id as usize) >= self.entities.len() { - self.entities.push(Some(Entity::new(self.next_id))); - self.get_next_id(); - info!("Created entity! ID: {}", id); - return id; - } - self.entities[self.next_id as usize] = Some(Entity::new(self.next_id)); - self.get_next_id(); - info!("Created entity! ID: {}", id); - id - } + /// Creates a new entity and returns its ID. + pub fn new_entity(&mut self) -> u32 { + let id = self.next_id; + if (self.next_id as usize) >= self.entities.len() { + self.entities.push(Some(Entity::new(self.next_id))); + self.get_next_id(); + info!("Created entity! ID: {}", id); + return id; + } + self.entities[self.next_id as usize] = Some(Entity::new(self.next_id)); + self.get_next_id(); + info!("Created entity! ID: {}", id); + id + } - /// Gets an immutable reference to an entity by its ID. - pub fn get_entity(&self, entity_id: usize) -> Option<&Entity> { - self.entities.get(entity_id).unwrap().as_ref() - } + /// Gets an immutable reference to an entity by its ID. + pub fn get_entity(&self, entity_id: usize) -> Option<&Entity> { + self.entities.get(entity_id).unwrap().as_ref() + } - /// Gets a mutable reference to an entity by its ID. - pub fn get_entity_mut(&mut self, entity_id: usize) -> Option<&mut Entity> { - self.entities.get_mut(entity_id).unwrap().as_mut() - } + /// Gets a mutable reference to an entity by its ID. + pub fn get_entity_mut(&mut self, entity_id: usize) -> Option<&mut Entity> { + self.entities.get_mut(entity_id).unwrap().as_mut() + } - /// Deletes an entity by its ID. - pub fn delete_entity(&mut self, entity_id: usize) { - self.remove_entity_from_archetype_subsets(entity_id as u32, self.get_component_set(entity_id)); - self.entities[entity_id] = None; - info!("Deleted entity! ID: {}", entity_id); - for (_, value) in self.components.iter_mut() { - value.remove::(entity_id); + /// Deletes an entity by its ID. + pub fn delete_entity(&mut self, entity_id: usize) { + self.remove_entity_from_archetype_subsets( + entity_id as u32, + self.get_component_set(entity_id), + ); + self.entities[entity_id] = None; + info!("Deleted entity! ID: {}", entity_id); + for (_, value) in self.components.iter_mut() { + value.remove::(entity_id); + } + self.id_queue.sorted_enqueue(entity_id as u32); + self.get_next_id(); + } - } - self.id_queue.sorted_enqueue(entity_id as u32); - self.get_next_id(); - } + fn create_archetype(&mut self, components: ComponentSet) { + self.archetypes.create_archetype(components.clone()); - fn create_archetype(&mut self, components: ComponentSet) { - self.archetypes.create_archetype(components.clone()); - - // Collect entities that match the component set first to avoid borrow checker issues - let mut matching_entities = Vec::new(); - for (entity_id, entity_option) in self.entities.iter().enumerate() { - if let Some(_entity) = entity_option { - let entity_component_set = self.get_component_set(entity_id); - - // If the entity has all the required components (components is subset of entity's components) - if components.is_subset(&entity_component_set) { - matching_entities.push(entity_id as u32); - } - } - } - - // Add all matching entities to the archetype - for entity_id in matching_entities { - self.add_entity_to_archetype(entity_id, components.clone()); - } - } + let mut matching_entities = Vec::new(); + for (entity_id, entity_option) in self.entities.iter().enumerate() { + if let Some(_entity) = entity_option { + let entity_component_set = self.get_component_set(entity_id); - fn remove_archetype(&mut self, components: ComponentSet) { - self.archetypes.remove_archetype(&components); - } + if components.is_subset(&entity_component_set) { + matching_entities.push(entity_id as u32); + } + } + } - fn get_keys(&self, components: ComponentSet) -> Vec { - let component_sets = self.archetypes.component_sets(); - component_sets.iter() - .enumerate() - .filter_map(|(i, &ref elem)| if elem.is_subset(&components) { Some(i) } else { None }) - .collect::>() - .iter() - .map(|index| component_sets[*index].clone()) - .collect::>() - } + for entity_id in matching_entities { + self.add_entity_to_archetype(entity_id, components.clone()); + } + } - fn remove_archetype_subsets(&mut self, components: ComponentSet) { - let keys = self.get_keys(components); + fn get_keys(&self, components: ComponentSet) -> Vec { + let component_sets = self.archetypes.component_sets(); + component_sets + .iter() + .enumerate() + .filter_map(|(i, &ref elem)| { + if elem.is_subset(&components) { + Some(i) + } else { + None + } + }) + .collect::>() + .iter() + .map(|index| component_sets[*index].clone()) + .collect::>() + } - for key in keys { - self.remove_archetype(key.clone()); - } - } + fn add_entity_to_archetype(&mut self, entity_id: u32, components: ComponentSet) { + self.archetypes + .add_entity_to_archetype(&components, entity_id); + } - fn add_entity_to_archetype(&mut self, entity_id: u32, components: ComponentSet) { - self.archetypes.add_entity_to_archetype(&components, entity_id); - } + fn remove_entity_from_archetype(&mut self, entity_id: u32, components: ComponentSet) { + self.archetypes + .remove_entity_from_archetype(&components, entity_id); + } - fn remove_entity_from_archetype(&mut self, entity_id: u32, components: ComponentSet) { - self.archetypes.remove_entity_from_archetype(&components, entity_id); - } + fn remove_entity_from_archetype_subsets(&mut self, entity_id: u32, components: ComponentSet) { + let keys = self.get_keys(components); - fn remove_entity_from_archetype_subsets(&mut self, entity_id: u32, components: ComponentSet) { - let keys = self.get_keys(components); + for key in keys { + self.remove_entity_from_archetype(entity_id, key.clone()); + if self.archetypes.get_archetype(&key).unwrap().len() == 0 { + self.archetypes.remove_archetype(&key); + } + } + info!("Removed entity {} from all archetypes!", entity_id); + } - for key in keys { - self.remove_entity_from_archetype(entity_id, key.clone()); - if self.archetypes.get_archetype(&key).unwrap().len() == 0 { - self.archetypes.remove_archetype(&key); - } - } - info!("Removed entity {} from all archetypes!", entity_id); - } + fn get_component_set(&self, entity_id: usize) -> ComponentSet { + let components = match self.entities.get(entity_id) { + Some(cmp) => match cmp.as_ref() { + Some(e) => e.get_components().iter().collect::>(), + None => { + error!("This entity ({}) does not have any components!", entity_id); + Vec::new() + } + }, + _ => { + error!("This entity ({}) does not exist!", entity_id); + Vec::new() + } + }; - fn get_component_set(&self, entity_id: usize) -> ComponentSet { - let components = match self.entities.get(entity_id) { - Some(cmp) => match cmp.as_ref() { - Some(e) => e.get_components().iter().collect::>(), - None => { - error!("This entity ({}) does not have any components!", entity_id); - Vec::new() - } - }, - _ => { - error!("This entity ({}) does not exist!", entity_id); - Vec::new() - } - }; + let type_ids = components + .iter() + .map(|index| self.components.keys()[*index]) + .collect::>(); + ComponentSet::from_ids(type_ids) + } - let type_ids = components.iter().map(|index| self.components.keys()[*index]).collect::>(); - ComponentSet::from_ids(type_ids) - } + /// Registers a new component in the scene. + pub fn register_component(&mut self) { + if !self.components.contains(&C::type_id()) { + self.components.register_component::(self.entities.len()); + self.create_archetype(ComponentSet::from_ids(vec![C::type_id()])); + info!("Registered component: {}", C::type_name()); + return; + } + warn!("Component {} is already registered!", C::type_name()); + } - /// Registers a new component in the scene. - pub fn register_component(&mut self) { - if !self.components.contains(&C::type_id()) { - self.components.register_component::(self.entities.len()); - self.create_archetype(ComponentSet::from_ids(vec![C::type_id()])); - info!("Registered component: {}", C::type_name()); - return; - } - warn!("Component {} is already registered!", C::type_name()); - } + /// Deregisters a component from the scene. + pub fn deregister_component(&mut self) { + if self.components.contains(&C::type_id()) { + self.components.deregister_component::(); + info!("Deregistered component: {}", C::type_name()); + return; + } + warn!("Component {} was not registered!", C::type_name()); + } - /// Deregisters a component from the scene. - pub fn deregister_component(&mut self) { - if self.components.contains(&C::type_id()) { - self.components.deregister_component::(); - info!("Deregistered component: {}", C::type_name()); - return; - } - warn!("Component {} was not registered!", C::type_name()); - } + /// Adds a component to an entity by its ID and an instance of the component. + /// Overwrites the previous component if another component of the same type is added. + pub fn add_component(&mut self, entity_id: usize, component: C) { + let old_component_set = self.get_component_set(entity_id); + if !old_component_set.to_vec().is_empty() { + self.remove_entity_from_archetype_subsets(entity_id as u32, old_component_set); + } - /// Adds a component to an entity by its ID and an instance of the component. - /// Overwrites the previous component if another component of the same type is added. - pub fn add_component(&mut self, entity_id: usize, component: C) { - let old_component_set = self.get_component_set(entity_id); - if !old_component_set.to_vec().is_empty() { - self.remove_entity_from_archetype_subsets(entity_id as u32, old_component_set); - } + self.components.set_component(entity_id, component); + let component_index = self + .components + .keys() + .iter_mut() + .position(|x| *x == C::type_id()) + .unwrap(); + self.get_entity_mut(entity_id) + .unwrap() + .add_component(component_index); - self.components.set_component(entity_id, component); - let component_index = self.components.keys().iter_mut().position(|x| *x == C::type_id()).unwrap(); - self.get_entity_mut(entity_id).unwrap().add_component(component_index); + let new_component_set = self.get_component_set(entity_id); - let new_component_set = self.get_component_set(entity_id); + if !self.archetypes.contains_archetype(&new_component_set) { + self.create_archetype(new_component_set.clone()); + } - if !self.archetypes.contains_archetype(&new_component_set) { - self.create_archetype(new_component_set.clone()); - } + let powerset = ComponentSet::powerset(new_component_set.to_vec()); - let powerset = ComponentSet::powerset(new_component_set.to_vec()); - - for subset in powerset { - let component_set = ComponentSet::from_ids(subset.iter().cloned().collect()); - - if !self.archetypes.contains_archetype(&component_set) { - self.create_archetype(component_set.clone()); - } - - self.add_entity_to_archetype(entity_id as u32, component_set); - } + for subset in powerset { + let component_set = ComponentSet::from_ids(subset.iter().cloned().collect()); - info!("Added component {} to entity {}!", C::type_name(), entity_id); - } + if !self.archetypes.contains_archetype(&component_set) { + self.create_archetype(component_set.clone()); + } - pub fn remove_component(&mut self, entity_id: usize) { - let old_component_set = self.get_component_set(entity_id); - self.remove_entity_from_archetype_subsets(entity_id as u32, old_component_set); + self.add_entity_to_archetype(entity_id as u32, component_set); + } - self.components.remove_component::(entity_id); - let component_index = self.components.keys().iter().position(|x| *x == C::type_id()).unwrap(); - self.get_entity_mut(entity_id).unwrap().remove_component(component_index); + info!( + "Added component {} to entity {}!", + C::type_name(), + entity_id + ); + } - let new_component_set = self.get_component_set(entity_id); + pub fn remove_component(&mut self, entity_id: usize) { + let old_component_set = self.get_component_set(entity_id); + self.remove_entity_from_archetype_subsets(entity_id as u32, old_component_set); - if !new_component_set.to_vec().is_empty() { - if !self.archetypes.contains_archetype(&new_component_set) { - self.create_archetype(new_component_set.clone()); - } + self.components.remove_component::(entity_id); + let component_index = self + .components + .keys() + .iter() + .position(|x| *x == C::type_id()) + .unwrap(); + self.get_entity_mut(entity_id) + .unwrap() + .remove_component(component_index); - let powerset = ComponentSet::powerset(new_component_set.to_vec()); - - for subset in powerset { - let component_set = ComponentSet::from_ids(subset.iter().cloned().collect()); - - if !self.archetypes.contains_archetype(&component_set) { - self.create_archetype(component_set.clone()); - } - - self.add_entity_to_archetype(entity_id as u32, component_set); - } - } + let new_component_set = self.get_component_set(entity_id); - info!("Removed component {} from entity {}!", C::type_name(), entity_id); - } + if !new_component_set.to_vec().is_empty() { + if !self.archetypes.contains_archetype(&new_component_set) { + self.create_archetype(new_component_set.clone()); + } - /// Returns a reference to a component of an entity by its ID. - pub fn get_component(&self, entity_id: usize) -> Option<&C> { - self.components.get_component::(entity_id) - } + let powerset = ComponentSet::powerset(new_component_set.to_vec()); - pub fn get_component_mut(&mut self, entity_id: usize) -> Option<&mut C> { - self.components.get_component_mut::(entity_id) - } + for subset in powerset { + let component_set = ComponentSet::from_ids(subset.iter().cloned().collect()); - pub fn has(&self, entity_id: usize) -> bool { - self.components.get_component::(entity_id).is_some() - } + if !self.archetypes.contains_archetype(&component_set) { + self.create_archetype(component_set.clone()); + } - /// Returns a list of entities that have the given components. - pub fn get_entities_with(&self, components: Vec) -> Vec { - let component_set = ComponentSet::from_ids(components); - if self.archetypes.contains_archetype(&component_set) { - return self.archetypes.get_archetype(&component_set).unwrap().clone().iter().map(|x| *x as usize).collect(); - } - Vec::new() - } + self.add_entity_to_archetype(entity_id as u32, component_set); + } + } - /// Deletes all entities that have the given components. - pub fn delete_entities_with(&mut self, components: Vec) { - let entities = self.get_entities_with(components); - for entity in entities { - self.delete_entity(entity); - } - } + info!( + "Removed component {} from entity {}!", + C::type_name(), + entity_id + ); + } - /// Iterates over all entities that have the given components and calls the given function. - pub fn foreach(&mut self, func: fn(&mut C,&mut K)) { - let entities = self.get_entities_with(vec![C::type_id(), K::type_id()]); - for entity in entities { - let c_ptr = self.get_component_mut::(entity).unwrap() as *mut C; - let k_ptr = self.get_component_mut::(entity).unwrap() as *mut K; + /// Returns a reference to a component of an entity by its ID. + pub fn get_component(&self, entity_id: usize) -> Option<&C> { + self.components.get_component::(entity_id) + } - unsafe { - func(&mut *c_ptr, &mut *k_ptr); - } - } - } + pub fn get_component_mut( + &mut self, + entity_id: usize, + ) -> Option<&mut C> { + self.components.get_component_mut::(entity_id) + } + + pub fn has(&self, entity_id: usize) -> bool { + self.components.get_component::(entity_id).is_some() + } + + /// Returns a list of entities that have the given components. + pub fn get_entities_with(&self, components: Vec) -> Vec { + let component_set = ComponentSet::from_ids(components); + if self.archetypes.contains_archetype(&component_set) { + return self + .archetypes + .get_archetype(&component_set) + .unwrap() + .clone() + .iter() + .map(|x| *x as usize) + .collect(); + } + Vec::new() + } + + /// Deletes all entities that have the given components. + pub fn delete_entities_with(&mut self, components: Vec) { + let entities = self.get_entities_with(components); + for entity in entities { + self.delete_entity(entity); + } + } + + /// Iterates over all entities that have the given components and calls the given function. + pub fn foreach(&mut self, func: fn(&mut C, &mut K)) { + let entities = self.get_entities_with(vec![C::type_id(), K::type_id()]); + for entity in entities { + let c_ptr = self.get_component_mut::(entity).unwrap() as *mut C; + let k_ptr = self.get_component_mut::(entity).unwrap() as *mut K; + + unsafe { + func(&mut *c_ptr, &mut *k_ptr); + } + } + } + + /// Registers a prefab with the given name and factory function. + pub fn register_prefab(&mut self, name: &str, factory: crate::prefabs::PrefabFactory) { + self.prefabs.register(name, factory); + } + + /// Spawns a prefab with the given name. + pub fn spawn_prefab(&mut self, name: &str) -> Option { + if self.prefabs.has_prefab(name) { + if let Some(factory) = self.prefabs.prefabs.get(&name.to_string()) { + let factory = *factory; // Copy the function pointer + Some(factory(self)) + } else { + None + } + } else { + None + } + } + + /// Checks if a prefab with the given name exists. + pub fn has_prefab(&self, name: &str) -> bool { + self.prefabs.has_prefab(name) + } } diff --git a/examples/prefabs.rs b/examples/prefabs.rs new file mode 100644 index 0000000..9bcfe2d --- /dev/null +++ b/examples/prefabs.rs @@ -0,0 +1,52 @@ +use comet::prelude::*; + +fn setup(app: &mut App, renderer: &mut Renderer2D) { + // Initialize the texture atlas + renderer.initialize_atlas(); + + // Register components + app.register_component::(); + app.register_component::(); + + // Register prefabs + register_prefab!( + app, + "player", + Position2D::from_vec(v2::new(0.0, 0.0)), + Color::new(0.0, 1.0, 0.0, 1.0) // Green player + ); + + register_prefab!( + app, + "enemy", + Position2D::from_vec(v2::new(5.0, 5.0)), + Color::new(1.0, 0.0, 0.0, 1.0) // Red enemy + ); + + register_prefab!( + app, + "pickup", + Position2D::from_vec(v2::new(-5.0, -5.0)), + Color::new(1.0, 1.0, 0.0, 1.0) // Yellow pickup + ); + + if let Some(player_id) = app.spawn_prefab("player") { + debug!("Spawned player with ID: {}", player_id); + } + + if let Some(enemy_id) = app.spawn_prefab("enemy") { + debug!("Spawned enemy with ID: {}", enemy_id); + } + + if let Some(pickup_id) = app.spawn_prefab("pickup") { + debug!("Spawned pickup with ID: {}", pickup_id); + } +} + +fn update(app: &mut App, renderer: &mut Renderer2D, dt: f32) {} + +fn main() { + App::new() + .with_title("Prefabs Example") + .run::(setup, update); +}