2026-03-20 19:59:06 +08:00
|
|
|
#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++;
|
2026-03-25 01:23:08 +08:00
|
|
|
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);
|
2026-03-20 19:59:06 +08:00
|
|
|
m_transform = new TransformComponent();
|
|
|
|
|
m_transform->m_gameObject = this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GameObject::GameObject(const std::string& name)
|
|
|
|
|
: m_name(name) {
|
|
|
|
|
m_id = s_nextID++;
|
2026-03-25 01:23:08 +08:00
|
|
|
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);
|
2026-03-20 19:59:06 +08:00
|
|
|
m_transform = new TransformComponent();
|
|
|
|
|
m_transform->m_gameObject = this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GameObject::~GameObject() {
|
2026-03-22 02:10:32 +08:00
|
|
|
GetGlobalRegistry().erase(m_id);
|
2026-03-20 19:59:06 +08:00
|
|
|
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::SetParent(GameObject* parent) {
|
|
|
|
|
SetParent(parent, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameObject::SetParent(GameObject* parent, bool worldPositionStays) {
|
|
|
|
|
if (m_parent == parent) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Math::Vector3 worldPos = worldPositionStays ? GetTransform()->GetPosition() : GetTransform()->GetLocalPosition();
|
|
|
|
|
Math::Quaternion worldRot = worldPositionStays ? GetTransform()->GetRotation() : GetTransform()->GetLocalRotation();
|
|
|
|
|
Math::Vector3 worldScale = worldPositionStays ? GetTransform()->GetScale() : GetTransform()->GetLocalScale();
|
|
|
|
|
|
|
|
|
|
if (m_parent) {
|
|
|
|
|
auto& siblings = m_parent->m_children;
|
|
|
|
|
siblings.erase(std::remove(siblings.begin(), siblings.end(), this), siblings.end());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_parent = parent;
|
|
|
|
|
|
|
|
|
|
if (m_parent) {
|
|
|
|
|
m_parent->m_children.push_back(this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (worldPositionStays) {
|
|
|
|
|
GetTransform()->SetPosition(worldPos);
|
|
|
|
|
GetTransform()->SetRotation(worldRot);
|
|
|
|
|
GetTransform()->SetScale(worldScale);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GetTransform()->SetDirty();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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() {
|
|
|
|
|
for (auto* child : m_children) {
|
|
|
|
|
if (child) {
|
|
|
|
|
child->m_parent = nullptr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
m_children.clear();
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-25 01:23:08 +08:00
|
|
|
void GameObject::DetachFromParent() {
|
|
|
|
|
if (m_parent) {
|
|
|
|
|
Math::Vector3 worldPos = GetTransform()->GetPosition();
|
|
|
|
|
Math::Quaternion worldRot = GetTransform()->GetRotation();
|
|
|
|
|
Math::Vector3 worldScale = GetTransform()->GetScale();
|
|
|
|
|
|
|
|
|
|
auto& siblings = m_parent->m_children;
|
|
|
|
|
siblings.erase(std::remove(siblings.begin(), siblings.end(), this), siblings.end());
|
|
|
|
|
m_parent = nullptr;
|
|
|
|
|
|
|
|
|
|
if (m_scene) {
|
|
|
|
|
m_scene->m_rootGameObjects.push_back(m_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GetTransform()->SetPosition(worldPos);
|
|
|
|
|
GetTransform()->SetRotation(worldRot);
|
|
|
|
|
GetTransform()->SetScale(worldScale);
|
|
|
|
|
GetTransform()->SetDirty();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-20 19:59:06 +08:00
|
|
|
void GameObject::SetActive(bool active) {
|
|
|
|
|
if (m_activeSelf != active) {
|
|
|
|
|
m_activeSelf = active;
|
|
|
|
|
if (m_parent == nullptr || m_parent->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() {
|
|
|
|
|
for (auto& comp : m_components) {
|
|
|
|
|
if (comp->IsEnabled()) {
|
|
|
|
|
comp->Start();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameObject::Update(float deltaTime) {
|
|
|
|
|
for (auto& comp : m_components) {
|
|
|
|
|
if (comp->IsEnabled()) {
|
|
|
|
|
comp->Update(deltaTime);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameObject::FixedUpdate() {
|
|
|
|
|
for (auto& comp : m_components) {
|
|
|
|
|
if (comp->IsEnabled()) {
|
|
|
|
|
comp->FixedUpdate();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameObject::LateUpdate(float deltaTime) {
|
|
|
|
|
for (auto& comp : m_components) {
|
|
|
|
|
if (comp->IsEnabled()) {
|
|
|
|
|
comp->LateUpdate(deltaTime);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameObject::OnDestroy() {
|
|
|
|
|
for (auto& comp : m_components) {
|
|
|
|
|
comp->OnDestroy();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameObject::Destroy() {
|
|
|
|
|
OnDestroy();
|
|
|
|
|
if (m_scene) {
|
|
|
|
|
m_scene->DestroyGameObject(this);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-22 03:42:40 +08:00
|
|
|
void GameObject::Serialize(std::ostream& os) const {
|
|
|
|
|
os << "name=" << m_name << ";";
|
|
|
|
|
os << "active=" << (m_activeSelf ? "1" : "0") << ";";
|
|
|
|
|
os << "id=" << m_id << ";";
|
|
|
|
|
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, "transform") == 0) {
|
|
|
|
|
m_transform->Deserialize(is);
|
|
|
|
|
if (is.peek() == ';') is.get();
|
|
|
|
|
} else {
|
|
|
|
|
std::getline(is, token, ';');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-20 19:59:06 +08:00
|
|
|
} // namespace Components
|
|
|
|
|
} // namespace XCEngine
|