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.runtime.world.UpdateContext;
25
26import helios.engine.ecs.ComponentOpsRegistry;
27
28import helios.engine.core.data.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 * ## Usage
56 *
57 * ```cpp
58 * auto player = gameWorld.addGameObject();
59 *
60 * // Add components
61 * player.add<TransformComponent>(position);
62 * player.add<HealthComponent>(100.0f);
63 *
64 * // Query and access
65 * if (player.has<HealthComponent>()) {
66 * player.get<HealthComponent>()->takeDamage(10.0f);
67 * }
68 *
69 * // Activation state
70 * player.setActive(false); // Calls onDeactivate() on components
71 * ```
72 *
73 * @see EntityHandle
74 * @see EntityManager
75 * @see GameWorld
76 */
77 class GameObject {
78
79 /**
80 * @brief The underlying entity identifier.
81 */
82 helios::engine::ecs::EntityHandle entityHandle_{0,0};
83
84 /**
85 * @brief Non-owning pointer to the EntityManager.
86 */
88
89
90 public:
91
92 /**
93 * @brief Constructs a GameObject wrapper.
94 *
95 * @param entityHandle The entity handle to wrap.
96 * @param entityManager Pointer to the EntityManager. Must not be null.
97 */
98 explicit GameObject(
101
102 ) noexcept : entityHandle_(entityHandle), entityManager_(entityManager) {
103 assert(entityManager_ != nullptr && "EntityManager must not be null.");
104 };
105
106 /**
107 * @brief Returns a generator over all component type IDs attached to this entity.
108 *
109 * @return Generator yielding ComponentTypeId for each attached component.
110 */
111 [[nodiscard]] std::generator<helios::engine::core::data::ComponentTypeId>
113 return entityManager_->componentTypeIds(entityHandle_);
114 }
115
116 /**
117 * @brief Returns the underlying entity handle.
118 *
119 * @return The EntityHandle for this GameObject.
120 */
122 return entityHandle_;
123 }
124
125 /**
126 * @brief Returns the underlying entity handle (const).
127 *
128 * @return The EntityHandle for this GameObject.
129 */
130 [[nodiscard]] helios::engine::ecs::EntityHandle entityHandle() const noexcept {
131 return entityHandle_;
132 }
133
134 /**
135 * @brief Constructs and attaches a component to this entity.
136 *
137 * @details If the entity is active, calls `onActivate()` on the new component.
138 * If inactive, calls `onDeactivate()`.
139 *
140 * @tparam T The component type to add.
141 * @tparam Args Constructor argument types.
142 *
143 * @param args Arguments forwarded to the component constructor.
144 *
145 * @return Reference to the newly created component.
146 */
147 template<typename T, typename ...Args>
148 T& add(Args&& ...args) {
149
150 bool active = true;
151
152 if (entityManager_->has<helios::engine::mechanics::lifecycle::components::Active>(entityHandle_)) {
153 active = true;
154 } else {
155 active = false;
156 }
157
158 auto* cmp = entityManager_->emplace<T>(entityHandle_, std::forward<Args>(args)...);
159
162 void* raw = entityManager_->raw(entityHandle_, typeId);
163
164 if (active && ops.onActivate) {
165 ops.onActivate(raw);
166 } else if (!active && ops.onDeactivate) {
167 ops.onDeactivate(raw);
168 }
169
170 return *cmp;
171 }
172
173 /**
174 * @brief Returns existing component or creates a new one.
175 *
176 * @tparam T The component type.
177 * @tparam Args Constructor argument types.
178 *
179 * @param args Arguments forwarded to the constructor if creating.
180 *
181 * @return Reference to the existing or newly created component.
182 */
183 template<typename T, typename ...Args>
184 T& getOrAdd(Args&& ...args) {
185 if (entityManager_->has<T>(entityHandle_)) {
186 return *entityManager_->get<T>(entityHandle_);
187 }
188 return add<T>(std::forward<Args>(args)...);
189 }
190
191 /**
192 * @brief Returns raw pointer to component by type ID.
193 *
194 * @param typeId The component type identifier.
195 *
196 * @return Raw void pointer to the component, or nullptr if not found.
197 */
199 return entityManager_->raw(entityHandle_, typeId);
200 }
201
202 /**
203 * @brief Returns pointer to a component.
204 *
205 * @tparam T The component type.
206 *
207 * @return Pointer to the component, or nullptr if not attached.
208 */
209 template<typename T>
210 T* get() {
211 return entityManager_->get<T>(entityHandle_);
212 }
213
214 /**
215 * @brief Returns const pointer to a component.
216 *
217 * @tparam T The component type.
218 *
219 * @return Const pointer to the component, or nullptr if not attached.
220 */
221 template<typename T>
222 const T* get() const {
223 return entityManager_->get<T>(entityHandle_);
224 }
225
226 /**
227 * @brief Checks if this entity has a specific component type.
228 *
229 * @tparam T The component type to check.
230 *
231 * @return True if the component is attached, false otherwise.
232 */
233 template<typename T>
234 bool has() {
235 return entityManager_->has<T>(entityHandle_);
236 }
237
238 /**
239 * @brief Checks if this entity has a component by type ID.
240 *
241 * @param typeId The component type identifier.
242 *
243 * @return True if the component is attached, false otherwise.
244 */
246 return entityManager_->has(entityHandle_, typeId);
247 }
248
249 /**
250 * @brief Enables a specific component by type ID.
251 *
252 * @param typeId The component type identifier.
253 */
255 entityManager_->enable(entityHandle_, typeId);
256 }
257
258 /**
259 * @brief Disables a specific component by type ID.
260 *
261 * @param typeId The component type identifier.
262 */
264 entityManager_->disable(entityHandle_, typeId);
265 }
266
267 /**
268 * @brief Sets the activation state of this GameObject.
269 *
270 * @details When deactivated:
271 * - An `Inactive` tag component is added
272 * - The `Active` tag component is removed
273 * - `onDeactivate()` is called on components that support it
274 *
275 * When activated:
276 * - The `Inactive` tag component is removed
277 * - An `Active` tag component is added
278 * - `onActivate()` is called on components that support it
279 *
280 * @note Does **not** call `enable()`/`disable()` on components.
281 *
282 * @param active True to activate, false to deactivate.
283 */
284 void setActive(const bool active) {
285 if (active) {
286 entityManager_->remove<helios::engine::mechanics::lifecycle::components::Inactive>(entityHandle_);
287 entityManager_->emplaceOrGet<helios::engine::mechanics::lifecycle::components::Active>(entityHandle_);
288 } else {
289 entityManager_->emplaceOrGet<helios::engine::mechanics::lifecycle::components::Inactive>(entityHandle_);
290 entityManager_->remove<helios::engine::mechanics::lifecycle::components::Active>(entityHandle_);
291 }
292
293 for (auto typeId : componentTypeIds()) {
295 void* raw = entityManager_->raw(entityHandle_, typeId);
296
297 if (active && ops.onActivate) {
298 ops.onActivate(raw);
299 } else if (!active && ops.onDeactivate) {
300 ops.onDeactivate(raw);
301 }
302 }
303 }
304
305 /**
306 * @brief Returns whether this GameObject is active.
307 *
308 * @return True if the entity has the Active tag component.
309 */
310 [[nodiscard]] bool isActive() const {
311 return entityManager_->has<helios::engine::mechanics::lifecycle::components::Active>(entityHandle_);
312 }
313
314 /**
315 * @brief Notifies all components that the entity is being released to a pool.
316 *
317 * @details Iterates through all attached components and calls `onRelease()`
318 * on those that implement it.
319 */
320 void onRelease() {
321 for (auto typeId : componentTypeIds()) {
323 void* raw = entityManager_->raw(entityHandle_, typeId);
324
325 if (ops.onRelease) {
326 ops.onRelease(raw);
327 }
328 }
329 }
330
331 /**
332 * @brief Notifies all components that the entity is being acquired from a pool.
333 *
334 * @details Iterates through all attached components and calls `onAcquire()`
335 * on those that implement it.
336 */
337 void onAcquire() {
338 for (auto typeId : componentTypeIds()) {
340 void* raw = entityManager_->raw(entityHandle_, typeId);
341
342 if (ops.onAcquire) {
343 ops.onAcquire(raw);
344 }
345 }
346 }
347
348 };
349
350
351
352} // namespace helios::engine::modules

Generated via doxygen2docusaurus 2.0.0 by Doxygen 1.15.0.