SleakEngine 1.0.0
C++23 multi-backend game engine
Loading...
Searching...
No Matches
GameObject.hpp
Go to the documentation of this file.
1#ifndef _GAMEOBJECT_H_
2#define _GAMEOBJECT_H_
3
4#include "Object.hpp"
5#include <Core/Logger.hpp>
6#include <ECS/Component.hpp>
8#include <Memory/RefPtr.hpp>
9#include <type_traits>
10#include <string>
11
12namespace Sleak {
13 namespace Math {class Vector3D;};
14 /// Scene entity holding components and an optional parent/child transform
15 /// hierarchy. Scene owns the instance; destroy via Scene::DestroyObject().
16 ///
17 /// A GameObject on its own does nothing. Behavior comes from the
18 /// components attached to it: a TransformComponent for placement, a
19 /// MeshComponent and MaterialComponent to be drawn, a ColliderComponent
20 /// and RigidbodyComponent to be simulated, and your own Component
21 /// subclasses for gameplay.
22 ///
23 /// AddComponent<T>() constructs the component in place, forwarding
24 /// extra arguments to its constructor after the owner pointer. Only one
25 /// component of a given type is allowed per object: a second
26 /// AddComponent<T>() logs a warning and does nothing. GetComponent<T>()
27 /// resolves through `dynamic_cast`, so it also matches subclasses.
28 ///
29 /// The scene that received the object through SceneBase::AddObject()
30 /// owns it. Never `delete` a GameObject. Use SceneBase::DestroyObject()
31 /// for deferred destruction, which is what you want while iterating, or
32 /// SceneBase::RemoveObject() for immediate destruction.
33 ///
34 /// Camera, Light, and its subclasses all derive from GameObject, so
35 /// they are added and found the same way as anything else.
36 ///
37 /// @code{.cpp}
38 /// auto* crate = Sleak::GameObject::CreateCube(
39 /// Sleak::Math::Vector3D(0.0f, 1.0f, 0.0f));
40 /// crate->SetTag("Destructible");
41 ///
42 /// crate->AddComponent<Sleak::MaterialComponent>(
43 /// Sleak::RefPtr<Sleak::Material>(new Sleak::Material()));
44 /// crate->AddComponent<Sleak::RigidbodyComponent>(
45 /// Sleak::BodyType::Dynamic);
46 ///
47 /// if (auto* t = crate->GetComponent<Sleak::TransformComponent>()) {
48 /// t->SetScale(Sleak::Math::Vector3D(2.0f, 2.0f, 2.0f));
49 /// }
50 ///
51 /// AddObject(crate); // the scene now owns it
52 /// @endcode
53 ///
54 /// @see Component, SceneBase, TransformComponent, MeshComponent,
55 /// MaterialComponent, Camera
56 /// @ingroup scene
57 class ENGINE_API GameObject : public Object {
58 public:
59 GameObject(const std::string& name = "GameObject")
60 : Object(name), m_isActive(true), bIsInitialized(false),
61 m_pendingDestroy(false), m_parent(nullptr) {}
62
63 ~GameObject() override;
64
65 /// Constructs and attaches a component of type T. Warns and no-ops if one already exists.
66 template<typename T, typename... Args>
67 void AddComponent(Args&&... args) {
68 static_assert(std::is_base_of<Component, T>::value, "T must derive from Component!");
69
70 if (GetComponent<T>() != nullptr) {
71 SLEAK_WARN("The component already exists!");
72 return;
73 }
74
75 RefPtr<Component> newComponent = RefPtr<T>(new T(this, std::forward<Args>(args)...));
76 if (bIsInitialized) newComponent->Initialize();
77 if (m_isActive && bIsInitialized) newComponent->OnEnable();
78 Components.add(std::move(newComponent));
79 }
80
81 /// Destroys and detaches the first component of type T, if present.
82 template<typename T>
84 static_assert(std::is_base_of<Component, T>::value, "T must derive from Component!");
85
86 for (size_t i = 0; i < Components.GetSize(); ++i) {
87 if (dynamic_cast<T*>(Components[i].get()) != nullptr) {
88 Components[i]->OnDestroy();
89 Components.erase(i);
90 break;
91 }
92 }
93 }
94
95 /// Finds the first attached component of type T, or nullptr.
96 template <typename T>
98 static_assert(std::is_base_of_v<Component, T>,
99 "T must derive from Component!");
100
101 for (size_t i = 0; i < Components.GetSize(); ++i) {
102 Component* rawPtr = Components[i].get();
103 if (!rawPtr) continue;
104
105 T* component = dynamic_cast<T*>(rawPtr);
106 if (component) return component;
107 }
108
109 return nullptr;
110 }
111
112 /// True if a component of type T is attached.
113 template <typename T>
115 static_assert(std::is_base_of_v<Component, T>,
116 "T must derive from Component!");
117 return GetComponent<T>() != nullptr;
118 }
119
120 /// Initializes the object and its components; called once before the first Update.
121 virtual void Initialize();
122 virtual void Update(float deltaTime);
123 virtual void FixedUpdate(float fixedDeltaTime);
124 virtual void LateUpdate(float deltaTime);
125
126 /// Enables or disables the object, firing OnEnable/OnDisable on its components.
127 void SetActive(bool active);
128 bool IsActive() const { return m_isActive; }
129
130 void SetTag(const std::string& tag) { m_tag = tag; }
131 const std::string& GetTag() const { return m_tag; }
132
133 /// Reparents this object, updating both the old and new parent's child lists.
134 void SetParent(GameObject* parent);
135 GameObject* GetParent() const { return m_parent; }
136 const List<GameObject*>& GetChildren() const { return m_children; }
137 void AddChild(GameObject* child);
138 void RemoveChild(GameObject* child);
139 bool HasParent() const { return m_parent != nullptr; }
140 bool HasChildren() const { return m_children.GetSize() > 0; }
141
142 virtual bool IsLight() const { return false; }
143
144 /// Flags the object for deferred destruction on the next scene pass.
145 void MarkForDestroy() { m_pendingDestroy = true; }
146 bool IsPendingDestroy() const { return m_pendingDestroy; }
147
148 /// Built-in primitive factories, mainly for prototyping and debug scenes.
149 static GameObject* CreatePlane(Math::Vector3D position, int width = 100, int height = 100);
150 static GameObject* CreateCube(Math::Vector3D position);
151 static GameObject* CreateSphere(Math::Vector3D position, int stack = 16, int slices = 16);
152 static GameObject* CreateCapsule(Math::Vector3D position, int segments = 16, int rings = 8, float height = 1, float radius = 0.5);
153 static GameObject* CreateCylinder(Math::Vector3D position, int segments = 16, float height = 1, float radius = 0.5);
154 static GameObject* CreateTorus(Math::Vector3D position, int segments = 16, int rings = 8, float innerRadius = 8, float outerRadius = 9);
155
156 protected:
158
159 private:
160 bool m_isActive;
161 bool m_pendingDestroy;
162 std::string m_tag = "Untagged";
163
164 List<RefPtr<Component>> Components;
165
166 // Hierarchy
167 GameObject* m_parent;
168 List<GameObject*> m_children;
169
170 /// Calls OnDestroy() on and drops every attached component.
171 void DestroyComponents();
172 };
173}
174
175#endif // _GAMEOBJECT_H_
int width
int height
#define SLEAK_WARN(...)
Definition Logger.hpp:21
bool HasComponent()
True if a component of type T is attached.
GameObject * GetParent() const
void AddComponent(Args &&... args)
Constructs and attaches a component of type T. Warns and no-ops if one already exists.
const List< GameObject * > & GetChildren() const
bool HasParent() const
void MarkForDestroy()
Flags the object for deferred destruction on the next scene pass.
bool IsActive() const
bool HasChildren() const
bool IsPendingDestroy() const
virtual bool IsLight() const
T * GetComponent()
Finds the first attached component of type T, or nullptr.
void SetTag(const std::string &tag)
GameObject(const std::string &name="GameObject")
static GameObject * CreateTorus(Math::Vector3D position, int segments=16, int rings=8, float innerRadius=8, float outerRadius=9)
const std::string & GetTag() const
void RemoveComponent()
Destroys and detaches the first component of type T, if present.
Implements a dynamic array-like list for storing and managing a collection of elements.
Definition List.hpp:20
Object(const std::string &name="Object")
Definition Object.hpp:14
Vectors, matrices, quaternions, colors, AABBs, and random helpers.
Root namespace for everything the engine exposes.
Definition Camera.hpp:10