Skip to main content

GameObject.ixx File

High-level facade for entity manipulation in the ECS. More...

Included Headers

Namespaces Index

namespacehelios
namespaceengine

Main engine module aggregating core infrastructure and game systems. More...

namespaceecs

Core Entity-Component-System architecture. More...

Classes Index

classGameObject

Lightweight facade for entity component manipulation. More...

Description

High-level facade for entity manipulation in the ECS.

File Listing

The file content with the documentation metadata removed is:

1/**
2 * @file GameObject.ixx
3 * @brief High-level facade for entity manipulation in the ECS.
4 */
5module;
6
7#include <cassert>
8#include <type_traits>
9#include <typeindex>
10#include <generator>
11
12
13export module helios.engine.ecs.GameObject;
14
15
16import helios.engine.ecs.EntityHandle;
17
18import helios.engine.mechanics.lifecycle.components.Inactive;
19import helios.engine.mechanics.lifecycle.components.Active;
20
21import helios.util.Guid;
22
23import helios.engine.ecs.EntityManager;
24import helios.engine.ecs.components.HierarchyComponent;
25
26import helios.engine.ecs.ComponentOpsRegistry;
27
28import helios.engine.ecs.types.ComponentTypeId;
29import helios.core.data;
30
31
32export namespace helios::engine::ecs {
33
34 /**
35 * @brief Lightweight facade for entity component manipulation.
36 *
37 * @details `GameObject` provides a type-safe, convenient interface for
38 * working with entities and their components. It wraps an `EntityHandle`
39 * and a pointer to the `EntityManager`, delegating all operations.
40 *
41 * ## Size and Copy Semantics
42 *
43 * GameObject is only 16 bytes (8 bytes EntityHandle + 8 bytes pointer) and
44 * should be **passed by value**, not by reference:
45 *
46 * ```cpp
47 * // Correct - pass by value
48 * void processEntity(GameObject entity) { ... }
49 *
50 * // Unnecessary - reference adds indirection
51 * void processEntity(GameObject& entity); // Avoid
52 * void processEntity(const GameObject& entity); // Avoid
53 * ```
54 *
55 * ## Hierarchy Integration
56 *
57 * When a GameObject has a `HierarchyComponent`, activation changes trigger
58 * hierarchy propagation. Calling `setActive()` marks the hierarchy as dirty,
59 * and `HierarchyPropagationSystem` propagates the state to all descendants.
60 *
61 * ## Usage
62 *
63 * ```cpp
64 * auto player = gameWorld.addGameObject();
65 *
66 * // Add components
67 * player.add<TransformComponent>(position);
68 * player.add<HealthComponent>(100.0f);
69 *
70 * // Query and access
71 * if (player.has<HealthComponent>()) {
72 * player.get<HealthComponent>()->takeDamage(10.0f);
73 * }
74 *
75 * // Activation state (propagates to children if HierarchyComponent present)
76 * player.setActive(false); // Calls onDeactivate() on components
77 * ```
78 *
79 * @see EntityHandle
80 * @see EntityManager
81 * @see GameWorld
82 * @see HierarchyComponent
83 * @see HierarchyPropagationSystem
84 */
85 class GameObject {
86
87 /**
88 * @brief The underlying entity identifier.
89 */
90 helios::engine::ecs::EntityHandle entityHandle_{0,0};
91
92 /**
93 * @brief Non-owning pointer to the EntityManager.
94 */
96
97
98 public:
99
100 /**
101 * @brief Constructs a GameObject wrapper.
102 *
103 * @param entityHandle The entity handle to wrap.
104 * @param entityManager Pointer to the EntityManager. Must not be null.
105 */
106 explicit GameObject(
109
110 ) noexcept : entityHandle_(entityHandle), entityManager_(entityManager) {
111 assert(entityManager_ != nullptr && "EntityManager must not be null.");
112 };
113
114 /**
115 * @brief Returns a generator over all component type IDs attached to this entity.
116 *
117 * @return Generator yielding ComponentTypeId for each attached component.
118 */
119 [[nodiscard]] std::generator<helios::engine::ecs::types::ComponentTypeId>
121 return entityManager_->componentTypeIds(entityHandle_);
122 }
123
124 /**
125 * @brief Returns the underlying entity handle.
126 *
127 * @return The EntityHandle for this GameObject.
128 */
130 return entityHandle_;
131 }
132
133 /**
134 * @brief Returns the underlying entity handle (const).
135 *
136 * @return The EntityHandle for this GameObject.
137 */
138 [[nodiscard]] helios::engine::ecs::EntityHandle entityHandle() const noexcept {
139 return entityHandle_;
140 }
141
142 /**
143 * @brief Constructs and attaches a component to this entity.
144 *
145 * @details If the entity is active, calls `onActivate()` on the new component.
146 * If inactive, calls `onDeactivate()`.
147 *
148 * @tparam T The component type to add.
149 * @tparam Args Constructor argument types.
150 *
151 * @param args Arguments forwarded to the component constructor.
152 *
153 * @return Reference to the newly created component.
154 */
155 template<typename T, typename ...Args>
156 T& add(Args&& ...args) {
157
158 bool active = true;
159
160 if (entityManager_->has<helios::engine::mechanics::lifecycle::components::Active>(entityHandle_)) {
161 active = true;
162 } else {
163 active = false;
164 }
165
166 auto* cmp = entityManager_->emplace<T>(entityHandle_, std::forward<Args>(args)...);
167
170 void* raw = entityManager_->raw(entityHandle_, typeId);
171
172 if (active && ops.onActivate) {
173 ops.onActivate(raw);
174 } else if (!active && ops.onDeactivate) {
175 ops.onDeactivate(raw);
176 }
177
178 return *cmp;
179 }
180
181 /**
182 * @brief Returns existing component or creates a new one.
183 *
184 * @tparam T The component type.
185 * @tparam Args Constructor argument types.
186 *
187 * @param args Arguments forwarded to the constructor if creating.
188 *
189 * @return Reference to the existing or newly created component.
190 */
191 template<typename T, typename ...Args>
192 T& getOrAdd(Args&& ...args) {
193 if (entityManager_->has<T>(entityHandle_)) {
194 return *entityManager_->get<T>(entityHandle_);
195 }
196 return add<T>(std::forward<Args>(args)...);
197 }
198
199 /**
200 * @brief Removes a component from this entity.
201 *
202 * @tparam T The component type to remove.
203 *
204 * @return True if the component was removed, false if not present.
205 */
206 template<typename T>
207 bool remove() {
208 return entityManager_->remove<T>(entityHandle_);
209 }
210
211 /**
212 * @brief Returns raw pointer to component by type ID.
213 *
214 * @param typeId The component type identifier.
215 *
216 * @return Raw void pointer to the component, or nullptr if not found.
217 */
219 return entityManager_->raw(entityHandle_, typeId);
220 }
221
222 /**
223 * @brief Returns pointer to a component.
224 *
225 * @tparam T The component type.
226 *
227 * @return Pointer to the component, or nullptr if not attached.
228 */
229 template<typename T>
230 T* get() {
231 return entityManager_->get<T>(entityHandle_);
232 }
233
234 /**
235 * @brief Returns const pointer to a component.
236 *
237 * @tparam T The component type.
238 *
239 * @return Const pointer to the component, or nullptr if not attached.
240 */
241 template<typename T>
242 const T* get() const {
243 return entityManager_->get<T>(entityHandle_);
244 }
245
246 /**
247 * @brief Checks if this entity has a specific component type.
248 *
249 * @tparam T The component type to check.
250 *
251 * @return True if the component is attached, false otherwise.
252 */
253 template<typename T>
254 bool has() {
255 return entityManager_->has<T>(entityHandle_);
256 }
257
258 /**
259 * @brief Checks if this entity has a component by type ID.
260 *
261 * @param typeId The component type identifier.
262 *
263 * @return True if the component is attached, false otherwise.
264 */
266 return entityManager_->has(entityHandle_, typeId);
267 }
268
269 /**
270 * @brief Enables a specific component by type ID.
271 *
272 * @param typeId The component type identifier.
273 */
275 entityManager_->enable(entityHandle_, typeId);
276 }
277
278 /**
279 * @brief Disables a specific component by type ID.
280 *
281 * @param typeId The component type identifier.
282 */
284 entityManager_->disable(entityHandle_, typeId);
285 }
286
287 /**
288 * @brief Sets the activation state of this GameObject.
289 *
290 * @details When deactivated:
291 * - An `Inactive` tag component is added
292 * - The `Active` tag component is removed
293 * - `onDeactivate()` is called on components that support it
294 * - If a `HierarchyComponent` is present, it is marked dirty for propagation
295 *
296 * When activated:
297 * - The `Inactive` tag component is removed
298 * - An `Active` tag component is added
299 * - `onActivate()` is called on components that support it
300 * - If a `HierarchyComponent` is present, it is marked dirty for propagation
301 *
302 * @note Does **not** call `enable()`/`disable()` on components.
303 *
304 * @param active True to activate, false to deactivate.
305 *
306 * @see HierarchyComponent
307 * @see HierarchyPropagationSystem
308 */
309 void setActive(const bool active) {
310
311 bool isActive = entityManager_->has<helios::engine::mechanics::lifecycle::components::Active>(entityHandle_);
312 bool isInActive = entityManager_->has<helios::engine::mechanics::lifecycle::components::Inactive>(entityHandle_);
313
314 if (!isActive && active) {
315 auto* hc = entityManager_->get<helios::engine::ecs::components::HierarchyComponent>(entityHandle_);
316 if (hc) {
317 hc->markDirty();
318 }
319
320 entityManager_->remove<helios::engine::mechanics::lifecycle::components::Inactive>(entityHandle_);
321 entityManager_->emplaceOrGet<helios::engine::mechanics::lifecycle::components::Active>(entityHandle_);
322 }
323
324 if (!isInActive && !active) {
325 auto* hc = entityManager_->get<helios::engine::ecs::components::HierarchyComponent>(entityHandle_);
326 if (hc) {
327 hc->markDirty();
328 }
329
330 entityManager_->emplaceOrGet<helios::engine::mechanics::lifecycle::components::Inactive>(entityHandle_);
331 entityManager_->remove<helios::engine::mechanics::lifecycle::components::Active>(entityHandle_);
332 }
333
334 for (auto typeId : componentTypeIds()) {
336 void* raw = entityManager_->raw(entityHandle_, typeId);
337
338 if (active && ops.onActivate) {
339 ops.onActivate(raw);
340 } else if (!active && ops.onDeactivate) {
341 ops.onDeactivate(raw);
342 }
343 }
344 }
345
346 /**
347 * @brief Returns whether this GameObject is active.
348 *
349 * @return True if the entity has the Active tag component.
350 */
351 [[nodiscard]] bool isActive() const {
352 return entityManager_->has<helios::engine::mechanics::lifecycle::components::Active>(entityHandle_);
353 }
354
355 /**
356 * @brief Notifies all components that the entity is being released to a pool.
357 *
358 * @details Iterates through all attached components and calls `onRelease()`
359 * on those that implement it.
360 */
361 void onRelease() {
362 for (auto typeId : componentTypeIds()) {
364 void* raw = entityManager_->raw(entityHandle_, typeId);
365
366 if (ops.onRelease) {
367 ops.onRelease(raw);
368 }
369 }
370 }
371
372 /**
373 * @brief Notifies all components that the entity is being acquired from a pool.
374 *
375 * @details Iterates through all attached components and calls `onAcquire()`
376 * on those that implement it.
377 */
378 void onAcquire() {
379 for (auto typeId : componentTypeIds()) {
381 void* raw = entityManager_->raw(entityHandle_, typeId);
382
383 if (ops.onAcquire) {
384 ops.onAcquire(raw);
385 }
386 }
387 }
388
389 };
390
391
392
393} // namespace helios::engine::modules

Generated via doxygen2docusaurus 2.0.0 by Doxygen 1.15.0.