euv_engine/entity/impl.rs
1use super::*;
2
3/// Implements static factory and ID generation methods for `Entity`.
4impl Entity {
5 /// Generates the next unique entity ID using a global atomic counter.
6 ///
7 /// # Returns
8 ///
9 /// - `u64` - The next unique ID.
10 pub fn generate_id() -> u64 {
11 NEXT_ENTITY_ID.fetch_add(1, Ordering::Relaxed)
12 }
13
14 /// Creates a new entity with the given name and a default identity transform.
15 ///
16 /// Creates a new entity with the given name and a default identity transform.
17 ///
18 /// # Arguments
19 ///
20 /// - `N` - The name of the entity.
21 ///
22 /// # Returns
23 ///
24 /// - `Entity` - The newly created entity.
25 pub fn create<N>(name: N) -> Entity
26 where
27 N: AsRef<str>,
28 {
29 Entity::new(
30 Self::generate_id(),
31 name.as_ref().to_string(),
32 Transform2D::identity(),
33 true,
34 Vec::new(),
35 Vec::new(),
36 )
37 }
38
39 /// Creates a new entity at the specified position with a default name.
40 ///
41 /// # Arguments
42 ///
43 /// - `Vector2D` - The initial position.
44 ///
45 /// # Returns
46 ///
47 /// - `Entity` - The newly created entity.
48 pub fn create_at(position: Vector2D) -> Entity {
49 let mut entity: Entity = Self::create(DEFAULT_ENTITY_NAME);
50 entity.get_mut_transform().set_position(position);
51 entity
52 }
53}
54
55/// Implements pooled entity lifecycle methods for `Entity`.
56///
57/// These pair [`ObjectPool`] with `Entity` so a spawn/despawn cycle reuses
58/// the same allocation instead of dropping the entity and building a new one.
59/// A recycled entity is reset to a clean state and given a **fresh** id, so a
60/// stale reference held elsewhere can never alias a live entity.
61impl Entity {
62 /// Checks an entity out of the pool, creating one if the pool is empty.
63 ///
64 /// # Arguments
65 ///
66 /// - `N` - The name given to the entity, anything that converts into a
67 /// `String`.
68 /// - `&mut ObjectPool<Entity>` - The pool the entity is checked out from.
69 ///
70 /// # Returns
71 ///
72 /// - `Entity` - A ready-to-use entity carrying the requested name.
73 pub fn create_pooled<N>(name: N, pool: &mut ObjectPool<Entity>) -> Self
74 where
75 N: Into<String>,
76 {
77 Self::take_from(pool, name.into(), Transform2D::identity())
78 }
79
80 /// Checks an entity out of the pool and places it at `position`.
81 ///
82 /// The position is applied after the recycle reset, so a recycled entity
83 /// never keeps the position it was last drawn at.
84 ///
85 /// # Arguments
86 ///
87 /// - `Vector2D` - Where the entity should sit.
88 /// - `&mut ObjectPool<Entity>` - The pool the entity is checked out from.
89 ///
90 /// # Returns
91 ///
92 /// - `Entity` - A ready-to-use entity at the requested position.
93 pub fn create_pooled_at(position: Vector2D, pool: &mut ObjectPool<Entity>) -> Self {
94 let mut transform: Transform2D = Transform2D::identity();
95 transform.set_position(position);
96 Self::take_from(pool, String::from(DEFAULT_ENTITY_NAME), transform)
97 }
98
99 /// Builds `count` entities into the pool's free list.
100 ///
101 /// Only the shortfall is built, so prewarming an already-warm pool costs
102 /// nothing and a spawn burst does not grow the pool mid-frame.
103 ///
104 /// # Arguments
105 ///
106 /// - `usize` - How many free entities the pool should hold.
107 /// - `&mut ObjectPool<Entity>` - The pool to prewarm.
108 ///
109 /// # Returns
110 ///
111 /// - `usize` - The number of entities that were built.
112 pub fn prewarm_pool(count: usize, pool: &mut ObjectPool<Entity>) -> usize {
113 pool.prewarm(count, || Self::create(DEFAULT_ENTITY_NAME))
114 }
115
116 /// Resets an entity and hands it back to the pool.
117 ///
118 /// Components, tags and transform are cleared before the entity is
119 /// released so a recycled entity never inherits stale state. A fresh id
120 /// is assigned on the next checkout, so the released entity's id is not
121 /// reused.
122 ///
123 /// # Arguments
124 ///
125 /// - `&mut Entity` - The entity to recycle.
126 /// - `&mut ObjectPool<Entity>` - The pool to return it to.
127 pub fn release_to_pool(entity: &mut Entity, pool: &mut ObjectPool<Entity>) {
128 // The caller's slot is handed a fresh entity so the reference it keeps
129 // is never left aliasing a pooled one, then the released entity is
130 // scrubbed before it goes back: a recycled entity must not inherit the
131 // components, tags or transform it happened to hold on the way out.
132 let mut released: Entity = std::mem::replace(entity, Self::create(DEFAULT_ENTITY_NAME));
133 released.get_mut_components().clear();
134 released.get_mut_tags().clear();
135 *released.get_mut_transform() = Transform2D::identity();
136 pool.release(released);
137 }
138
139 /// Checks out an entity and applies the caller's name and transform.
140 ///
141 /// # Arguments
142 ///
143 /// - `&mut ObjectPool<Entity>` - The pool to check out from.
144 /// - `String` - The name to apply.
145 /// - `Transform2D` - The transform to apply.
146 ///
147 /// # Returns
148 ///
149 /// - `Entity` - The reset, freshly-identified entity.
150 fn take_from(pool: &mut ObjectPool<Entity>, name: String, transform: Transform2D) -> Self {
151 let mut entity: Entity = match pool.acquire() {
152 // `acquire` counted the checkout itself, so a recycled entity only
153 // needs its identity and state refreshed.
154 Some(recycled) => recycled,
155 None => {
156 // Nothing was checked out, so the checkout `acquire` skipped has
157 // to be counted here or `len` under-reports the new entity.
158 pool.set_active(pool.get_active() + 1);
159 Self::create(DEFAULT_ENTITY_NAME)
160 }
161 };
162 entity.set_id(Self::generate_id());
163 entity.set_name(name);
164 *entity.get_mut_transform() = transform;
165 entity.set_active(true);
166 entity
167 }
168}
169
170/// Implements lifecycle and component management methods for `Entity`.
171impl Entity {
172 /// Adds a component to this entity and calls its `on_start` lifecycle method.
173 ///
174 /// # Arguments
175 ///
176 /// - `ComponentRc` - The component to add.
177 pub fn add_component(&mut self, component: ComponentRc) {
178 component.get_mut().on_start();
179 self.get_mut_components().push(component);
180 }
181
182 /// Removes the first component matching the given name.
183 ///
184 /// # Arguments
185 ///
186 /// - `N` - The component name to match.
187 ///
188 /// # Returns
189 ///
190 /// - `Option<ComponentRc>` - The removed component, if found.
191 pub fn remove_component_by_name<N>(&mut self, name: N) -> Option<ComponentRc>
192 where
193 N: AsRef<str>,
194 {
195 let target: &str = name.as_ref();
196 let position: Option<usize> = self
197 .get_components()
198 .iter()
199 .position(|component: &ComponentRc| component.get().name() == target);
200 let index: usize = position?;
201 let removed: ComponentRc = self.get_mut_components().remove(index);
202 removed.get_mut().on_destroy();
203 Some(removed)
204 }
205
206 /// Returns the first component matching the given name.
207 ///
208 /// # Arguments
209 ///
210 /// - `N` - The component name to match.
211 ///
212 /// # Returns
213 ///
214 /// - `Option<ComponentRc>` - The matching component, if found.
215 pub fn get_component_by_name<N>(&self, name: N) -> Option<ComponentRc>
216 where
217 N: AsRef<str>,
218 {
219 let target: &str = name.as_ref();
220 self.get_components()
221 .iter()
222 .find(|component: &&ComponentRc| component.get().name() == target)
223 .cloned()
224 }
225
226 /// Calls `on_update` on all active components.
227 ///
228 /// # Arguments
229 ///
230 /// - `f64` - The delta time in seconds.
231 pub fn update(&mut self, delta_time: f64) {
232 if !self.get_active() {
233 return;
234 }
235 for component in self.get_components() {
236 component.get_mut().on_update(delta_time);
237 }
238 }
239
240 /// Calls `on_render` on all active components, recording into the draw list.
241 ///
242 /// # Arguments
243 ///
244 /// - `&mut DrawList` - The draw list to record commands into.
245 pub fn render(&self, draw_list: &mut DrawList) {
246 if !self.get_active() {
247 return;
248 }
249 let transform: Transform2D = self.get_transform();
250 for component in self.get_components() {
251 component.get_mut().on_render(draw_list, &transform);
252 }
253 }
254
255 /// Calls `on_destroy` on all components and clears the component list.
256 pub fn destroy(&mut self) {
257 for component in self.get_components() {
258 component.get_mut().on_destroy();
259 }
260 self.get_mut_components().clear();
261 }
262
263 /// Adds a tag string to this entity.
264 ///
265 /// # Arguments
266 ///
267 /// - `String` - The tag to add.
268 pub fn add_tag(&mut self, tag: String) {
269 if !self.get_tags().contains(&tag) {
270 self.get_mut_tags().push(tag);
271 }
272 }
273
274 /// Tests whether this entity has the given tag.
275 ///
276 /// # Arguments
277 ///
278 /// - `T` - The tag to check.
279 ///
280 /// # Returns
281 ///
282 /// - `bool` - True if the tag is present.
283 pub fn has_tag<T>(&self, tag: T) -> bool
284 where
285 T: AsRef<str>,
286 {
287 let target: &str = tag.as_ref();
288 self.get_tags().iter().any(|t: &String| t == target)
289 }
290}
291
292/// Forwards `Entity::update` through the [`Updatable`] trait so that collections
293/// of heterogeneous updateable objects can be driven by the scheduler.
294///
295/// The inherent [`Entity::update`] method is the canonical implementation;
296/// this impl exists purely for trait dispatch. The inherent call resolves
297/// first when both are in scope, so there is no recursion.
298impl Updatable for Entity {
299 /// Advances the simulation by `delta_time` seconds.
300 ///
301 /// # Arguments
302 ///
303 /// - `f64` - Seconds elapsed since the previous update.
304 fn update(&mut self, delta_time: f64) {
305 Entity::update(self, delta_time);
306 }
307}
308
309/// Implements event subscription, emission, and management for `EventBus`.
310impl EventBus {
311 /// Creates a new empty event bus.
312 ///
313 /// # Returns
314 ///
315 /// - `EventBus` - The new event bus.
316 pub fn create() -> EventBus {
317 EventBus::new()
318 }
319
320 /// Subscribes a handler to the named event channel.
321 ///
322 /// # Arguments
323 ///
324 /// - `String` - The event name to subscribe to.
325 /// - `EventHandler` - The handler closure to call when the event is emitted.
326 pub fn subscribe(&mut self, event_name: String, handler: EventHandler) {
327 self.get_mut_handlers()
328 .entry(event_name)
329 .or_default()
330 .push(handler);
331 }
332
333 /// Emits an event to all handlers subscribed to the matching channel.
334 ///
335 /// The event name is derived from the `EntityEvent` variant.
336 ///
337 /// # Arguments
338 ///
339 /// - `&EntityEvent` - The event to emit.
340 pub fn emit(&self, event: &EntityEvent) {
341 let event_name: &str = Self::event_name(event);
342 if let Some(handlers) = self.get_handlers().get(event_name) {
343 for handler in handlers {
344 handler(event);
345 }
346 }
347 }
348
349 /// Removes all handlers for the named event channel.
350 ///
351 /// # Arguments
352 ///
353 /// - `E` - The event name to clear.
354 pub fn unsubscribe_all<E>(&mut self, event_name: E)
355 where
356 E: AsRef<str>,
357 {
358 self.get_mut_handlers().remove(event_name.as_ref());
359 }
360
361 /// Returns the number of handlers registered for the named event.
362 ///
363 /// # Arguments
364 ///
365 /// - `E` - The event name.
366 ///
367 /// # Returns
368 ///
369 /// - `usize` - The handler count.
370 pub fn handler_count<E>(&self, event_name: E) -> usize
371 where
372 E: AsRef<str>,
373 {
374 self.get_handlers()
375 .get(event_name.as_ref())
376 .map(|handlers: &Vec<EventHandler>| handlers.len())
377 .unwrap_or_default()
378 }
379
380 /// Derives the event channel name from an `EntityEvent` variant.
381 ///
382 /// # Arguments
383 ///
384 /// - `&EntityEvent` - The event.
385 ///
386 /// # Returns
387 ///
388 /// - `&str` - The channel name (borrowed — no allocation per emit;
389 /// the `Custom` variant borrows its `name` field).
390 fn event_name(event: &EntityEvent) -> &str {
391 match event {
392 EntityEvent::Collision { .. } => ENTITY_EVENT_NAME_COLLISION,
393 EntityEvent::TriggerEnter { .. } => ENTITY_EVENT_NAME_TRIGGER_ENTER,
394 EntityEvent::TriggerExit { .. } => ENTITY_EVENT_NAME_TRIGGER_EXIT,
395 EntityEvent::Spawn => ENTITY_EVENT_NAME_SPAWN,
396 EntityEvent::Destroy => ENTITY_EVENT_NAME_DESTROY,
397 EntityEvent::Custom { name, .. } => name.as_str(),
398 }
399 }
400}
401
402/// Implements `Default` for `EventBus` as a new empty bus.
403impl Default for EventBus {
404 /// Constructs a default [`EventBus`] value.
405 ///
406 /// # Returns
407 ///
408 /// - `EventBus` - A default-constructed instance with the documented initial state.
409 fn default() -> EventBus {
410 EventBus::create()
411 }
412}