Files
XCEngine/engine/src/Components/GameObject.cpp

365 lines
9.2 KiB
C++

#include "Components/GameObject.h"
#include "Components/TransformComponent.h"
#include "Scene/Scene.h"
namespace XCEngine {
namespace Components {
GameObject::ID GameObject::s_nextID = 1;
GameObject::GameObject()
: m_name("GameObject") {
m_id = s_nextID++;
static std::random_device rd;
static std::mt19937_64 gen(rd());
static std::uniform_int_distribution<uint64_t> dis(1, UINT64_MAX);
m_uuid = dis(gen);
m_transform = new TransformComponent();
m_transform->m_gameObject = this;
}
GameObject::GameObject(const std::string& name)
: m_name(name) {
m_id = s_nextID++;
static std::random_device rd;
static std::mt19937_64 gen(rd());
static std::uniform_int_distribution<uint64_t> dis(1, UINT64_MAX);
m_uuid = dis(gen);
m_transform = new TransformComponent();
m_transform->m_gameObject = this;
}
GameObject::~GameObject() {
GetGlobalRegistry().erase(m_id);
if (m_transform) {
delete m_transform;
m_transform = nullptr;
}
m_components.clear();
}
std::unordered_map<GameObject::ID, GameObject*>& GameObject::GetGlobalRegistry() {
static std::unordered_map<ID, GameObject*> registry;
return registry;
}
void GameObject::NotifyComponentsBecameActive() {
for (auto& comp : m_components) {
if (comp->IsEnabled()) {
comp->OnEnable();
}
}
}
void GameObject::NotifyComponentsBecameInactive() {
for (auto& comp : m_components) {
if (comp->IsEnabled()) {
comp->OnDisable();
}
}
}
void GameObject::PropagateActiveInHierarchyChange(bool oldParentActiveInHierarchy, bool newParentActiveInHierarchy) {
const bool wasActiveInHierarchy = oldParentActiveInHierarchy && m_activeSelf;
const bool isActiveInHierarchy = newParentActiveInHierarchy && m_activeSelf;
if (wasActiveInHierarchy != isActiveInHierarchy) {
if (isActiveInHierarchy) {
NotifyComponentsBecameActive();
} else {
NotifyComponentsBecameInactive();
}
}
for (auto* child : m_children) {
if (child) {
child->PropagateActiveInHierarchyChange(wasActiveInHierarchy, isActiveInHierarchy);
}
}
}
void GameObject::SetParent(GameObject* parent) {
SetParent(parent, true);
}
void GameObject::SetParent(GameObject* parent, bool worldPositionStays) {
if (m_parent == parent || parent == this) {
return;
}
const bool oldParentActiveInHierarchy = m_parent ? m_parent->IsActiveInHierarchy() : true;
const bool wasActiveInHierarchy = oldParentActiveInHierarchy && m_activeSelf;
if (m_parent) {
auto& siblings = m_parent->m_children;
siblings.erase(std::remove(siblings.begin(), siblings.end(), this), siblings.end());
} else if (m_scene) {
auto& roots = m_scene->m_rootGameObjects;
roots.erase(std::remove(roots.begin(), roots.end(), m_id), roots.end());
}
m_parent = parent;
if (m_parent) {
m_parent->m_children.push_back(this);
} else if (m_scene) {
auto& roots = m_scene->m_rootGameObjects;
if (std::find(roots.begin(), roots.end(), m_id) == roots.end()) {
roots.push_back(m_id);
}
}
GetTransform()->SetParent(parent ? parent->GetTransform() : nullptr, worldPositionStays);
const bool newParentActiveInHierarchy = m_parent ? m_parent->IsActiveInHierarchy() : true;
const bool isActiveInHierarchy = newParentActiveInHierarchy && m_activeSelf;
if (wasActiveInHierarchy != isActiveInHierarchy) {
if (isActiveInHierarchy) {
NotifyComponentsBecameActive();
} else {
NotifyComponentsBecameInactive();
}
for (auto* child : m_children) {
if (child) {
child->PropagateActiveInHierarchyChange(wasActiveInHierarchy, isActiveInHierarchy);
}
}
}
}
GameObject* GameObject::GetChild(size_t index) const {
if (index < m_children.size()) {
return m_children[index];
}
return nullptr;
}
std::vector<GameObject*> GameObject::GetChildren() const {
return m_children;
}
void GameObject::DetachChildren() {
auto children = m_children;
for (auto* child : children) {
if (child) {
child->SetParent(nullptr, true);
}
}
}
void GameObject::DetachFromParent() {
if (m_parent) {
SetParent(nullptr, true);
}
}
void GameObject::SetActive(bool active) {
if (m_activeSelf == active) {
return;
}
const bool parentActiveInHierarchy = m_parent ? m_parent->IsActiveInHierarchy() : true;
const bool wasActiveInHierarchy = parentActiveInHierarchy && m_activeSelf;
m_activeSelf = active;
const bool isActiveInHierarchy = parentActiveInHierarchy && m_activeSelf;
if (wasActiveInHierarchy != isActiveInHierarchy) {
if (isActiveInHierarchy) {
NotifyComponentsBecameActive();
} else {
NotifyComponentsBecameInactive();
}
for (auto* child : m_children) {
if (child) {
child->PropagateActiveInHierarchyChange(wasActiveInHierarchy, isActiveInHierarchy);
}
}
}
}
bool GameObject::IsActiveInHierarchy() const {
if (!m_activeSelf) {
return false;
}
if (m_parent) {
return m_parent->IsActiveInHierarchy();
}
return true;
}
GameObject* GameObject::Find(const std::string& name) {
auto& registry = GetGlobalRegistry();
for (auto& pair : registry) {
if (pair.second->GetName() == name) {
return pair.second;
}
}
return nullptr;
}
std::vector<GameObject*> GameObject::FindObjectsOfType() {
auto& registry = GetGlobalRegistry();
std::vector<GameObject*> result;
for (auto& pair : registry) {
result.push_back(pair.second);
}
return result;
}
std::vector<GameObject*> GameObject::FindGameObjectsWithTag(const std::string& tag) {
auto& registry = GetGlobalRegistry();
std::vector<GameObject*> result;
for (auto& pair : registry) {
if (pair.second->GetName() == tag) {
result.push_back(pair.second);
}
}
return result;
}
void GameObject::Awake() {
for (auto& comp : m_components) {
comp->Awake();
}
}
void GameObject::Start() {
if (!IsActiveInHierarchy()) {
return;
}
if (!m_started) {
for (auto& comp : m_components) {
if (comp->IsEnabled()) {
comp->Start();
}
}
m_started = true;
}
for (auto* child : m_children) {
if (child) {
child->Start();
}
}
}
void GameObject::Update(float deltaTime) {
if (!IsActiveInHierarchy()) {
return;
}
for (auto& comp : m_components) {
if (comp->IsEnabled()) {
comp->Update(deltaTime);
}
}
for (auto* child : m_children) {
if (child) {
child->Update(deltaTime);
}
}
}
void GameObject::FixedUpdate() {
if (!IsActiveInHierarchy()) {
return;
}
for (auto& comp : m_components) {
if (comp->IsEnabled()) {
comp->FixedUpdate();
}
}
for (auto* child : m_children) {
if (child) {
child->FixedUpdate();
}
}
}
void GameObject::LateUpdate(float deltaTime) {
if (!IsActiveInHierarchy()) {
return;
}
for (auto& comp : m_components) {
if (comp->IsEnabled()) {
comp->LateUpdate(deltaTime);
}
}
for (auto* child : m_children) {
if (child) {
child->LateUpdate(deltaTime);
}
}
}
void GameObject::OnDestroy() {
for (auto& comp : m_components) {
comp->OnDestroy();
}
}
void GameObject::Destroy() {
if (m_scene) {
m_scene->DestroyGameObject(this);
} else {
OnDestroy();
}
}
void GameObject::Serialize(std::ostream& os) const {
os << "name=" << m_name << ";";
os << "active=" << (m_activeSelf ? "1" : "0") << ";";
os << "id=" << m_id << ";";
os << "uuid=" << m_uuid << ";";
os << "transform=";
m_transform->Serialize(os);
os << ";";
}
void GameObject::Deserialize(std::istream& is) {
std::string token;
while (is.peek() != -1 && is.peek() != '\n' && is.peek() != '\r') {
if (is.peek() == ';') {
is.get();
if (is.peek() == ';') break;
continue;
}
char key[64];
is.get(key, 64, '=');
if (key[0] == '\0') break;
is.get();
if (strcmp(key, "name") == 0) {
std::getline(is, m_name, ';');
} else if (strcmp(key, "active") == 0) {
char val;
is.get(val);
m_activeSelf = (val == '1');
if (is.peek() == ';') is.get();
} else if (strcmp(key, "id") == 0) {
is >> m_id;
if (is.peek() == ';') is.get();
} else if (strcmp(key, "uuid") == 0) {
is >> m_uuid;
if (is.peek() == ';') is.get();
} else if (strcmp(key, "transform") == 0) {
m_transform->Deserialize(is);
if (is.peek() == ';') is.get();
} else {
std::getline(is, token, ';');
}
}
}
} // namespace Components
} // namespace XCEngine