Docs: Add audio module architecture design document
- Add XCEngine音频模块架构设计.md - Design audio system following Unity-style architecture - Include AudioSourceComponent, AudioListenerComponent, AudioClip, AudioMixer - Document DSP effect system (FFT, Reverb, EQ, Compressor) - Document 3D spatial audio with HRTF support - Define IAudioBackend abstraction layer with WASAPI/OpenAL backends - Outline 5-phase implementation priorities
This commit is contained in:
1640
docs/plan/XCEngine音频模块架构设计.md
Normal file
1640
docs/plan/XCEngine音频模块架构设计.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -207,6 +207,19 @@ add_library(XCEngine STATIC
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/ResourcePackage.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/ResourcePackage.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/ResourceDependencyGraph.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/ResourceDependencyGraph.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/ResourcePath.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Resources/ResourcePath.cpp
|
||||||
|
|
||||||
|
# Components
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Components/Component.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Components/TransformComponent.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Components/GameObject.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Components/TransformComponent.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Components/GameObject.cpp
|
||||||
|
|
||||||
|
# Scene
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Scene/Scene.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/include/XCEngine/Scene/SceneManager.h
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Scene/Scene.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/Scene/SceneManager.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(XCEngine PUBLIC
|
target_include_directories(XCEngine PUBLIC
|
||||||
|
|||||||
57
engine/include/XCEngine/Components/Component.h
Normal file
57
engine/include/XCEngine/Components/Component.h
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace XCEngine {
|
||||||
|
namespace Components {
|
||||||
|
|
||||||
|
class GameObject;
|
||||||
|
class TransformComponent;
|
||||||
|
class Scene;
|
||||||
|
|
||||||
|
class Component {
|
||||||
|
public:
|
||||||
|
virtual ~Component() = default;
|
||||||
|
|
||||||
|
virtual void Awake() {}
|
||||||
|
virtual void Start() {}
|
||||||
|
virtual void Update(float deltaTime) {}
|
||||||
|
virtual void FixedUpdate() {}
|
||||||
|
virtual void LateUpdate(float deltaTime) {}
|
||||||
|
virtual void OnEnable() {}
|
||||||
|
virtual void OnDisable() {}
|
||||||
|
virtual void OnDestroy() {}
|
||||||
|
|
||||||
|
virtual std::string GetName() const = 0;
|
||||||
|
|
||||||
|
GameObject* GetGameObject() const { return m_gameObject; }
|
||||||
|
TransformComponent& transform() const;
|
||||||
|
Scene* GetScene() const;
|
||||||
|
|
||||||
|
bool IsEnabled() const { return m_enabled; }
|
||||||
|
void SetEnabled(bool enabled) {
|
||||||
|
if (m_enabled != enabled) {
|
||||||
|
m_enabled = enabled;
|
||||||
|
if (m_enabled) {
|
||||||
|
OnEnable();
|
||||||
|
} else {
|
||||||
|
OnDisable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Component() = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_enabled = true;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
GameObject* m_gameObject = nullptr;
|
||||||
|
|
||||||
|
friend class GameObject;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Components
|
||||||
|
} // namespace XCEngine
|
||||||
174
engine/include/XCEngine/Components/GameObject.h
Normal file
174
engine/include/XCEngine/Components/GameObject.h
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Component.h"
|
||||||
|
#include "TransformComponent.h"
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace XCEngine {
|
||||||
|
namespace Components {
|
||||||
|
|
||||||
|
class Scene;
|
||||||
|
|
||||||
|
class GameObject {
|
||||||
|
public:
|
||||||
|
using ID = uint64_t;
|
||||||
|
static constexpr ID INVALID_ID = 0;
|
||||||
|
|
||||||
|
GameObject();
|
||||||
|
explicit GameObject(const std::string& name);
|
||||||
|
~GameObject();
|
||||||
|
|
||||||
|
ID GetID() const { return m_id; }
|
||||||
|
const std::string& GetName() const { return m_name; }
|
||||||
|
void SetName(const std::string& name) { m_name = name; }
|
||||||
|
|
||||||
|
Scene* GetScene() const { return m_scene; }
|
||||||
|
|
||||||
|
TransformComponent* GetTransform() { return m_transform; }
|
||||||
|
const TransformComponent* GetTransform() const { return m_transform; }
|
||||||
|
|
||||||
|
template<typename T, typename... Args>
|
||||||
|
T* AddComponent(Args&&... args) {
|
||||||
|
auto component = std::make_unique<T>(std::forward<Args>(args)...);
|
||||||
|
component->m_gameObject = this;
|
||||||
|
T* ptr = component.get();
|
||||||
|
m_components.emplace_back(std::move(component));
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T* GetComponent() {
|
||||||
|
for (auto& comp : m_components) {
|
||||||
|
if (T* casted = dynamic_cast<T*>(comp.get())) {
|
||||||
|
return casted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
const T* GetComponent() const {
|
||||||
|
for (auto& comp : m_components) {
|
||||||
|
if (T* casted = dynamic_cast<T*>(comp.get())) {
|
||||||
|
return casted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
std::vector<T*> GetComponents() {
|
||||||
|
std::vector<T*> result;
|
||||||
|
for (auto& comp : m_components) {
|
||||||
|
if (T* casted = dynamic_cast<T*>(comp.get())) {
|
||||||
|
result.push_back(casted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
std::vector<const T*> GetComponents() const {
|
||||||
|
std::vector<const T*> result;
|
||||||
|
for (auto& comp : m_components) {
|
||||||
|
if (const T* casted = dynamic_cast<const T*>(comp.get())) {
|
||||||
|
result.push_back(casted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T* GetComponentInChildren() {
|
||||||
|
T* comp = GetComponent<T>();
|
||||||
|
if (comp) {
|
||||||
|
return comp;
|
||||||
|
}
|
||||||
|
for (auto* child : m_children) {
|
||||||
|
comp = child->GetComponentInChildren<T>();
|
||||||
|
if (comp) {
|
||||||
|
return comp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T* GetComponentInParent() {
|
||||||
|
if (m_parent) {
|
||||||
|
T* comp = m_parent->GetComponent<T>();
|
||||||
|
if (comp) {
|
||||||
|
return comp;
|
||||||
|
}
|
||||||
|
return m_parent->GetComponentInParent<T>();
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
std::vector<T*> GetComponentsInChildren() {
|
||||||
|
std::vector<T*> result;
|
||||||
|
std::vector<T*> comps = GetComponents<T>();
|
||||||
|
result.insert(result.end(), comps.begin(), comps.end());
|
||||||
|
|
||||||
|
for (auto* child : m_children) {
|
||||||
|
std::vector<T*> childComps = child->GetComponentsInChildren<T>();
|
||||||
|
result.insert(result.end(), childComps.begin(), childComps.end());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hierarchy
|
||||||
|
GameObject* GetParent() const { return m_parent; }
|
||||||
|
void SetParent(GameObject* parent);
|
||||||
|
void SetParent(GameObject* parent, bool worldPositionStays);
|
||||||
|
|
||||||
|
size_t GetChildCount() const { return m_children.size(); }
|
||||||
|
GameObject* GetChild(size_t index) const;
|
||||||
|
std::vector<GameObject*> GetChildren() const;
|
||||||
|
void DetachChildren();
|
||||||
|
|
||||||
|
// Active state
|
||||||
|
bool IsActive() const { return m_activeSelf; }
|
||||||
|
void SetActive(bool active);
|
||||||
|
bool IsActiveInHierarchy() const;
|
||||||
|
|
||||||
|
// Static find
|
||||||
|
static GameObject* Find(const std::string& name);
|
||||||
|
static std::vector<GameObject*> FindObjectsOfType();
|
||||||
|
static std::vector<GameObject*> FindGameObjectsWithTag(const std::string& tag);
|
||||||
|
|
||||||
|
// Lifecycle
|
||||||
|
void Awake();
|
||||||
|
void Start();
|
||||||
|
void Update(float deltaTime);
|
||||||
|
void FixedUpdate();
|
||||||
|
void LateUpdate(float deltaTime);
|
||||||
|
void OnDestroy();
|
||||||
|
|
||||||
|
void Destroy();
|
||||||
|
|
||||||
|
private:
|
||||||
|
ID m_id = INVALID_ID;
|
||||||
|
std::string m_name;
|
||||||
|
bool m_activeSelf = true;
|
||||||
|
|
||||||
|
GameObject* m_parent = nullptr;
|
||||||
|
std::vector<GameObject*> m_children;
|
||||||
|
|
||||||
|
Scene* m_scene = nullptr;
|
||||||
|
TransformComponent* m_transform = nullptr;
|
||||||
|
std::vector<std::unique_ptr<Component>> m_components;
|
||||||
|
|
||||||
|
static ID s_nextID;
|
||||||
|
static std::unordered_map<ID, GameObject*>& GetGlobalRegistry();
|
||||||
|
|
||||||
|
friend class Scene;
|
||||||
|
friend class SceneManager;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Components
|
||||||
|
} // namespace XCEngine
|
||||||
114
engine/include/XCEngine/Components/TransformComponent.h
Normal file
114
engine/include/XCEngine/Components/TransformComponent.h
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Component.h"
|
||||||
|
#include <XCEngine/Math/Vector3.h>
|
||||||
|
#include <XCEngine/Math/Quaternion.h>
|
||||||
|
#include <XCEngine/Math/Matrix4.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace XCEngine {
|
||||||
|
namespace Components {
|
||||||
|
|
||||||
|
class GameObject;
|
||||||
|
class Scene;
|
||||||
|
|
||||||
|
enum class Space {
|
||||||
|
Self,
|
||||||
|
World
|
||||||
|
};
|
||||||
|
|
||||||
|
class TransformComponent : public Component {
|
||||||
|
public:
|
||||||
|
TransformComponent();
|
||||||
|
~TransformComponent() override = default;
|
||||||
|
|
||||||
|
std::string GetName() const override { return "Transform"; }
|
||||||
|
|
||||||
|
// Local space getters/setters
|
||||||
|
const Math::Vector3& GetLocalPosition() const { return m_localPosition; }
|
||||||
|
void SetLocalPosition(const Math::Vector3& position) { m_localPosition = position; SetDirty(); }
|
||||||
|
|
||||||
|
const Math::Quaternion& GetLocalRotation() const { return m_localRotation; }
|
||||||
|
void SetLocalRotation(const Math::Quaternion& rotation) { m_localRotation = rotation; SetDirty(); }
|
||||||
|
|
||||||
|
const Math::Vector3& GetLocalScale() const { return m_localScale; }
|
||||||
|
void SetLocalScale(const Math::Vector3& scale) { m_localScale = scale; SetDirty(); }
|
||||||
|
|
||||||
|
Math::Vector3 GetLocalEulerAngles() const;
|
||||||
|
void SetLocalEulerAngles(const Math::Vector3& eulers);
|
||||||
|
|
||||||
|
// World space getters/setters
|
||||||
|
Math::Vector3 GetPosition() const;
|
||||||
|
void SetPosition(const Math::Vector3& position);
|
||||||
|
|
||||||
|
Math::Quaternion GetRotation() const;
|
||||||
|
void SetRotation(const Math::Quaternion& rotation);
|
||||||
|
|
||||||
|
Math::Vector3 GetScale() const;
|
||||||
|
void SetScale(const Math::Vector3& scale);
|
||||||
|
|
||||||
|
// Direction vectors
|
||||||
|
Math::Vector3 GetForward() const;
|
||||||
|
Math::Vector3 GetRight() const;
|
||||||
|
Math::Vector3 GetUp() const;
|
||||||
|
|
||||||
|
// Matrix operations
|
||||||
|
const Math::Matrix4x4& GetLocalToWorldMatrix() const;
|
||||||
|
Math::Matrix4x4 GetWorldToLocalMatrix() const;
|
||||||
|
|
||||||
|
// Hierarchy - parent
|
||||||
|
TransformComponent* GetParent() const { return m_parent; }
|
||||||
|
void SetParent(TransformComponent* parent, bool worldPositionStays = true);
|
||||||
|
void SetParent(TransformComponent* parent) { SetParent(parent, true); }
|
||||||
|
|
||||||
|
// Hierarchy - children
|
||||||
|
size_t GetChildCount() const { return m_children.size(); }
|
||||||
|
TransformComponent* GetChild(size_t index) const;
|
||||||
|
TransformComponent* Find(const std::string& name) const;
|
||||||
|
void DetachChildren();
|
||||||
|
|
||||||
|
// Hierarchy - sibling
|
||||||
|
int GetSiblingIndex() const { return m_siblingIndex; }
|
||||||
|
void SetSiblingIndex(int index);
|
||||||
|
void SetAsFirstSibling();
|
||||||
|
void SetAsLastSibling();
|
||||||
|
|
||||||
|
// Transform operations
|
||||||
|
void LookAt(const Math::Vector3& target);
|
||||||
|
void LookAt(const Math::Vector3& target, const Math::Vector3& up);
|
||||||
|
void Rotate(const Math::Vector3& eulers, Space relativeTo = Space::Self);
|
||||||
|
void Rotate(const Math::Vector3& axis, float angle, Space relativeTo = Space::Self);
|
||||||
|
void Translate(const Math::Vector3& translation, Space relativeTo = Space::Self);
|
||||||
|
|
||||||
|
// Point/direction transforms
|
||||||
|
Math::Vector3 TransformPoint(const Math::Vector3& point) const;
|
||||||
|
Math::Vector3 InverseTransformPoint(const Math::Vector3& point) const;
|
||||||
|
Math::Vector3 TransformDirection(const Math::Vector3& direction) const;
|
||||||
|
Math::Vector3 InverseTransformDirection(const Math::Vector3& direction) const;
|
||||||
|
|
||||||
|
// Internal
|
||||||
|
void SetDirty();
|
||||||
|
void UpdateWorldTransform() const;
|
||||||
|
void NotifyHierarchyChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Math::Vector3 m_localPosition = Math::Vector3::Zero();
|
||||||
|
Math::Quaternion m_localRotation = Math::Quaternion::Identity();
|
||||||
|
Math::Vector3 m_localScale = Math::Vector3::One();
|
||||||
|
|
||||||
|
TransformComponent* m_parent = nullptr;
|
||||||
|
std::vector<TransformComponent*> m_children;
|
||||||
|
|
||||||
|
mutable Math::Matrix4x4 m_localToWorldMatrix;
|
||||||
|
mutable Math::Vector3 m_worldPosition;
|
||||||
|
mutable Math::Quaternion m_worldRotation;
|
||||||
|
mutable Math::Vector3 m_worldScale;
|
||||||
|
mutable bool m_dirty = true;
|
||||||
|
int m_siblingIndex = 0;
|
||||||
|
|
||||||
|
friend class GameObject;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Components
|
||||||
|
} // namespace XCEngine
|
||||||
113
engine/include/XCEngine/Scene/Scene.h
Normal file
113
engine/include/XCEngine/Scene/Scene.h
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <XCEngine/Core/Event.h>
|
||||||
|
#include <XCEngine/Components/GameObject.h>
|
||||||
|
|
||||||
|
namespace XCEngine {
|
||||||
|
namespace Components {
|
||||||
|
|
||||||
|
class Scene {
|
||||||
|
public:
|
||||||
|
using GameObjectID = uint64_t;
|
||||||
|
static constexpr GameObjectID INVALID_GAMEOBJECT_ID = 0;
|
||||||
|
|
||||||
|
Scene();
|
||||||
|
explicit Scene(const std::string& name);
|
||||||
|
~Scene();
|
||||||
|
|
||||||
|
const std::string& GetName() const { return m_name; }
|
||||||
|
void SetName(const std::string& name) { m_name = name; }
|
||||||
|
|
||||||
|
bool IsActive() const { return m_active; }
|
||||||
|
void SetActive(bool active) { m_active = active; }
|
||||||
|
|
||||||
|
GameObject* CreateGameObject(const std::string& name, GameObject* parent = nullptr);
|
||||||
|
void DestroyGameObject(GameObject* gameObject);
|
||||||
|
|
||||||
|
GameObject* Find(const std::string& name) const;
|
||||||
|
GameObject* FindGameObjectWithTag(const std::string& tag) const;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T* FindObjectOfType() const {
|
||||||
|
for (auto* go : GetRootGameObjects()) {
|
||||||
|
if (T* comp = go->GetComponent<T>()) {
|
||||||
|
return comp;
|
||||||
|
}
|
||||||
|
if (T* comp = FindInChildren<T>(go)) {
|
||||||
|
return comp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
std::vector<T*> FindObjectsOfType() const {
|
||||||
|
std::vector<T*> results;
|
||||||
|
for (auto* go : GetRootGameObjects()) {
|
||||||
|
if (T* comp = go->GetComponent<T>()) {
|
||||||
|
results.push_back(comp);
|
||||||
|
}
|
||||||
|
FindInChildren<T>(go, results);
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<GameObject*> GetRootGameObjects() const;
|
||||||
|
|
||||||
|
void Update(float deltaTime);
|
||||||
|
void FixedUpdate(float fixedDeltaTime);
|
||||||
|
void LateUpdate(float deltaTime);
|
||||||
|
|
||||||
|
void Load(const std::string& filePath);
|
||||||
|
void Save(const std::string& filePath);
|
||||||
|
|
||||||
|
Core::Event<GameObject*>& OnGameObjectCreated() { return m_onGameObjectCreated; }
|
||||||
|
Core::Event<GameObject*>& OnGameObjectDestroyed() { return m_onGameObjectDestroyed; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
GameObject* FindInChildren(GameObject* parent, const std::string& name) const;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T* FindInChildren(GameObject* parent) const {
|
||||||
|
for (size_t i = 0; i < parent->GetChildCount(); ++i) {
|
||||||
|
GameObject* child = parent->GetChild(i);
|
||||||
|
if (T* comp = child->GetComponent<T>()) {
|
||||||
|
return comp;
|
||||||
|
}
|
||||||
|
if (T* comp = FindInChildren<T>(child)) {
|
||||||
|
return comp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void FindInChildren(GameObject* parent, std::vector<T*>& results) const {
|
||||||
|
for (size_t i = 0; i < parent->GetChildCount(); ++i) {
|
||||||
|
GameObject* child = parent->GetChild(i);
|
||||||
|
if (T* comp = child->GetComponent<T>()) {
|
||||||
|
results.push_back(comp);
|
||||||
|
}
|
||||||
|
FindInChildren<T>(child, results);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string m_name;
|
||||||
|
bool m_active = true;
|
||||||
|
std::unordered_map<GameObjectID, std::unique_ptr<GameObject>> m_gameObjects;
|
||||||
|
std::vector<GameObjectID> m_rootGameObjects;
|
||||||
|
std::unordered_set<GameObjectID> m_gameObjectIDs;
|
||||||
|
|
||||||
|
Core::Event<GameObject*> m_onGameObjectCreated;
|
||||||
|
Core::Event<GameObject*> m_onGameObjectDestroyed;
|
||||||
|
|
||||||
|
friend class GameObject;
|
||||||
|
friend class SceneManager;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Components
|
||||||
|
} // namespace XCEngine
|
||||||
48
engine/include/XCEngine/Scene/SceneManager.h
Normal file
48
engine/include/XCEngine/Scene/SceneManager.h
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Scene.h"
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace XCEngine {
|
||||||
|
namespace Components {
|
||||||
|
|
||||||
|
class SceneManager {
|
||||||
|
public:
|
||||||
|
static SceneManager& Get();
|
||||||
|
|
||||||
|
Scene* CreateScene(const std::string& name);
|
||||||
|
void LoadScene(const std::string& filePath);
|
||||||
|
void LoadSceneAsync(const std::string& filePath, std::function<void(Scene*)> callback);
|
||||||
|
void UnloadScene(Scene* scene);
|
||||||
|
void UnloadScene(const std::string& sceneName);
|
||||||
|
|
||||||
|
void SetActiveScene(Scene* scene);
|
||||||
|
void SetActiveScene(const std::string& sceneName);
|
||||||
|
Scene* GetActiveScene() const { return m_activeScene; }
|
||||||
|
|
||||||
|
Scene* GetScene(const std::string& name) const;
|
||||||
|
std::vector<Scene*> GetAllScenes() const;
|
||||||
|
|
||||||
|
Core::Event<Scene*>& OnSceneLoaded() { return m_onSceneLoaded; }
|
||||||
|
Core::Event<Scene*>& OnSceneUnloaded() { return m_onSceneUnloaded; }
|
||||||
|
Core::Event<Scene*>& OnActiveSceneChanged() { return m_onActiveSceneChanged; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
SceneManager() = default;
|
||||||
|
~SceneManager() = default;
|
||||||
|
SceneManager(const SceneManager&) = delete;
|
||||||
|
SceneManager& operator=(const SceneManager&) = delete;
|
||||||
|
|
||||||
|
Scene* m_activeScene = nullptr;
|
||||||
|
std::unordered_map<std::string, std::unique_ptr<Scene>> m_scenes;
|
||||||
|
|
||||||
|
Core::Event<Scene*> m_onSceneLoaded;
|
||||||
|
Core::Event<Scene*> m_onSceneUnloaded;
|
||||||
|
Core::Event<Scene*> m_onActiveSceneChanged;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Components
|
||||||
|
} // namespace XCEngine
|
||||||
190
engine/src/Components/GameObject.cpp
Normal file
190
engine/src/Components/GameObject.cpp
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
#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++;
|
||||||
|
m_transform = new TransformComponent();
|
||||||
|
m_transform->m_gameObject = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
GameObject::GameObject(const std::string& name)
|
||||||
|
: m_name(name) {
|
||||||
|
m_id = s_nextID++;
|
||||||
|
m_transform = new TransformComponent();
|
||||||
|
m_transform->m_gameObject = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
GameObject::~GameObject() {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Components
|
||||||
|
} // namespace XCEngine
|
||||||
277
engine/src/Components/TransformComponent.cpp
Normal file
277
engine/src/Components/TransformComponent.cpp
Normal file
@@ -0,0 +1,277 @@
|
|||||||
|
#include "Components/TransformComponent.h"
|
||||||
|
#include "Components/GameObject.h"
|
||||||
|
#include "Scene/Scene.h"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace XCEngine {
|
||||||
|
namespace Components {
|
||||||
|
|
||||||
|
TransformComponent::TransformComponent() = default;
|
||||||
|
|
||||||
|
void TransformComponent::SetDirty() {
|
||||||
|
m_dirty = true;
|
||||||
|
for (auto* child : m_children) {
|
||||||
|
if (child) {
|
||||||
|
child->SetDirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransformComponent::UpdateWorldTransform() const {
|
||||||
|
if (!m_dirty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_parent) {
|
||||||
|
const Math::Matrix4x4& parentMatrix = m_parent->GetLocalToWorldMatrix();
|
||||||
|
Math::Matrix4x4 localMatrix = Math::Matrix4x4::TRS(m_localPosition, m_localRotation, m_localScale);
|
||||||
|
m_localToWorldMatrix = parentMatrix * localMatrix;
|
||||||
|
} else {
|
||||||
|
m_localToWorldMatrix = Math::Matrix4x4::TRS(m_localPosition, m_localRotation, m_localScale);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_localToWorldMatrix.Decompose(m_worldPosition, m_worldRotation, m_worldScale);
|
||||||
|
m_dirty = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Math::Matrix4x4& TransformComponent::GetLocalToWorldMatrix() const {
|
||||||
|
UpdateWorldTransform();
|
||||||
|
return m_localToWorldMatrix;
|
||||||
|
}
|
||||||
|
|
||||||
|
Math::Matrix4x4 TransformComponent::GetWorldToLocalMatrix() const {
|
||||||
|
return GetLocalToWorldMatrix().Inverse();
|
||||||
|
}
|
||||||
|
|
||||||
|
Math::Vector3 TransformComponent::GetPosition() const {
|
||||||
|
UpdateWorldTransform();
|
||||||
|
return m_worldPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransformComponent::SetPosition(const Math::Vector3& position) {
|
||||||
|
if (m_parent) {
|
||||||
|
Math::Matrix4x4 worldToParent = m_parent->GetWorldToLocalMatrix();
|
||||||
|
m_localPosition = worldToParent.MultiplyPoint(position);
|
||||||
|
} else {
|
||||||
|
m_localPosition = position;
|
||||||
|
}
|
||||||
|
SetDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
Math::Quaternion TransformComponent::GetRotation() const {
|
||||||
|
UpdateWorldTransform();
|
||||||
|
return m_worldRotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransformComponent::SetRotation(const Math::Quaternion& rotation) {
|
||||||
|
if (m_parent) {
|
||||||
|
Math::Quaternion parentInverse = m_parent->GetRotation().Inverse();
|
||||||
|
m_localRotation = parentInverse * rotation;
|
||||||
|
} else {
|
||||||
|
m_localRotation = rotation;
|
||||||
|
}
|
||||||
|
SetDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
Math::Vector3 TransformComponent::GetScale() const {
|
||||||
|
UpdateWorldTransform();
|
||||||
|
return m_worldScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransformComponent::SetScale(const Math::Vector3& scale) {
|
||||||
|
if (m_parent) {
|
||||||
|
const Math::Vector3& parentScale = m_parent->GetScale();
|
||||||
|
m_localScale = Math::Vector3(
|
||||||
|
parentScale.x != 0.0f ? scale.x / parentScale.x : 0.0f,
|
||||||
|
parentScale.y != 0.0f ? scale.y / parentScale.y : 0.0f,
|
||||||
|
parentScale.z != 0.0f ? scale.z / parentScale.z : 0.0f
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
m_localScale = scale;
|
||||||
|
}
|
||||||
|
SetDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
Math::Vector3 TransformComponent::GetLocalEulerAngles() const {
|
||||||
|
return m_localRotation.ToEulerAngles() * Math::RAD_TO_DEG;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransformComponent::SetLocalEulerAngles(const Math::Vector3& eulers) {
|
||||||
|
m_localRotation = Math::Quaternion::FromEulerAngles(eulers * Math::DEG_TO_RAD);
|
||||||
|
SetDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
Math::Vector3 TransformComponent::GetForward() const {
|
||||||
|
return GetRotation() * Math::Vector3::Forward();
|
||||||
|
}
|
||||||
|
|
||||||
|
Math::Vector3 TransformComponent::GetRight() const {
|
||||||
|
return GetRotation() * Math::Vector3::Right();
|
||||||
|
}
|
||||||
|
|
||||||
|
Math::Vector3 TransformComponent::GetUp() const {
|
||||||
|
return GetRotation() * Math::Vector3::Up();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransformComponent::SetParent(TransformComponent* parent, bool worldPositionStays) {
|
||||||
|
if (m_parent == parent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Math::Vector3 worldPos = worldPositionStays ? GetPosition() : m_localPosition;
|
||||||
|
Math::Quaternion worldRot = worldPositionStays ? GetRotation() : m_localRotation;
|
||||||
|
Math::Vector3 worldScale = worldPositionStays ? GetScale() : m_localScale;
|
||||||
|
|
||||||
|
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) {
|
||||||
|
SetPosition(worldPos);
|
||||||
|
Math::Quaternion newLocalRot = m_parent ? m_parent->GetRotation().Inverse() * worldRot : worldRot;
|
||||||
|
m_localRotation = newLocalRot;
|
||||||
|
SetScale(worldScale);
|
||||||
|
}
|
||||||
|
|
||||||
|
SetDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
TransformComponent* TransformComponent::GetChild(size_t index) const {
|
||||||
|
if (index < m_children.size()) {
|
||||||
|
return m_children[index];
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
TransformComponent* FindInHierarchy(GameObject* go, const std::string& name);
|
||||||
|
|
||||||
|
TransformComponent* TransformComponent::Find(const std::string& name) const {
|
||||||
|
GameObject* go = m_gameObject;
|
||||||
|
if (!go) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Scene* scene = go->GetScene();
|
||||||
|
if (!scene) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto* rootGO : scene->GetRootGameObjects()) {
|
||||||
|
TransformComponent* found = FindInHierarchy(rootGO, name);
|
||||||
|
if (found) {
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
TransformComponent* FindInHierarchy(GameObject* go, const std::string& name) {
|
||||||
|
if (!go) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (go->GetName() == name) {
|
||||||
|
return go->GetTransform();
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < go->GetChildCount(); ++i) {
|
||||||
|
if (TransformComponent* found = FindInHierarchy(go->GetChild(i), name)) {
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransformComponent::DetachChildren() {
|
||||||
|
for (auto* child : m_children) {
|
||||||
|
if (child) {
|
||||||
|
child->m_parent = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_children.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransformComponent::SetSiblingIndex(int index) {
|
||||||
|
m_siblingIndex = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransformComponent::SetAsFirstSibling() {
|
||||||
|
if (m_parent) {
|
||||||
|
m_parent->SetSiblingIndex(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransformComponent::SetAsLastSibling() {
|
||||||
|
if (m_parent) {
|
||||||
|
m_parent->SetSiblingIndex(static_cast<int>(m_parent->GetChildCount()) - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransformComponent::LookAt(const Math::Vector3& target) {
|
||||||
|
LookAt(target, Math::Vector3::Up());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransformComponent::LookAt(const Math::Vector3& target, const Math::Vector3& up) {
|
||||||
|
Math::Vector3 forward = Math::Vector3::Normalize(target - GetPosition());
|
||||||
|
if (Math::Vector3::Magnitude(forward) < Math::EPSILON) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SetRotation(Math::Quaternion::LookRotation(forward, up));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransformComponent::Rotate(const Math::Vector3& eulers, Space relativeTo) {
|
||||||
|
Math::Quaternion rotation = Math::Quaternion::FromEulerAngles(eulers * Math::DEG_TO_RAD);
|
||||||
|
if (relativeTo == Space::Self) {
|
||||||
|
m_localRotation = m_localRotation * rotation;
|
||||||
|
} else {
|
||||||
|
m_localRotation = rotation * m_localRotation;
|
||||||
|
}
|
||||||
|
SetDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransformComponent::Rotate(const Math::Vector3& axis, float angle, Space relativeTo) {
|
||||||
|
Math::Quaternion rotation = Math::Quaternion::FromAxisAngle(axis, angle * Math::DEG_TO_RAD);
|
||||||
|
if (relativeTo == Space::Self) {
|
||||||
|
m_localRotation = m_localRotation * rotation;
|
||||||
|
} else {
|
||||||
|
m_localRotation = rotation * m_localRotation;
|
||||||
|
}
|
||||||
|
SetDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransformComponent::Translate(const Math::Vector3& translation, Space relativeTo) {
|
||||||
|
if (relativeTo == Space::Self) {
|
||||||
|
m_localPosition = m_localPosition + translation;
|
||||||
|
} else {
|
||||||
|
m_localPosition = m_localPosition + translation;
|
||||||
|
}
|
||||||
|
SetDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
Math::Vector3 TransformComponent::TransformPoint(const Math::Vector3& point) const {
|
||||||
|
return GetLocalToWorldMatrix().MultiplyPoint(point);
|
||||||
|
}
|
||||||
|
|
||||||
|
Math::Vector3 TransformComponent::InverseTransformPoint(const Math::Vector3& point) const {
|
||||||
|
return GetWorldToLocalMatrix().MultiplyPoint(point);
|
||||||
|
}
|
||||||
|
|
||||||
|
Math::Vector3 TransformComponent::TransformDirection(const Math::Vector3& direction) const {
|
||||||
|
return GetRotation() * direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
Math::Vector3 TransformComponent::InverseTransformDirection(const Math::Vector3& direction) const {
|
||||||
|
return GetRotation().Inverse() * direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TransformComponent::NotifyHierarchyChanged() {
|
||||||
|
SetDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Components
|
||||||
|
} // namespace XCEngine
|
||||||
151
engine/src/Scene/Scene.cpp
Normal file
151
engine/src/Scene/Scene.cpp
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
#include "Scene/Scene.h"
|
||||||
|
#include "Components/GameObject.h"
|
||||||
|
|
||||||
|
namespace XCEngine {
|
||||||
|
namespace Components {
|
||||||
|
|
||||||
|
Scene::Scene()
|
||||||
|
: m_name("Untitled") {
|
||||||
|
}
|
||||||
|
|
||||||
|
Scene::Scene(const std::string& name)
|
||||||
|
: m_name(name) {
|
||||||
|
}
|
||||||
|
|
||||||
|
Scene::~Scene() {
|
||||||
|
m_gameObjects.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
GameObject* Scene::CreateGameObject(const std::string& name, GameObject* parent) {
|
||||||
|
auto gameObject = std::make_unique<GameObject>(name);
|
||||||
|
GameObject* ptr = gameObject.get();
|
||||||
|
|
||||||
|
m_gameObjectIDs.insert(ptr->m_id);
|
||||||
|
m_gameObjects.emplace(ptr->m_id, std::move(gameObject));
|
||||||
|
|
||||||
|
if (parent) {
|
||||||
|
ptr->SetParent(parent);
|
||||||
|
} else {
|
||||||
|
m_rootGameObjects.push_back(ptr->m_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr->m_scene = this;
|
||||||
|
ptr->Awake();
|
||||||
|
|
||||||
|
m_onGameObjectCreated.Invoke(ptr);
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Scene::DestroyGameObject(GameObject* gameObject) {
|
||||||
|
if (!gameObject || gameObject->m_scene != this) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto* child : gameObject->GetChildren()) {
|
||||||
|
DestroyGameObject(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gameObject->m_parent) {
|
||||||
|
auto& siblings = gameObject->m_parent->m_children;
|
||||||
|
siblings.erase(std::remove(siblings.begin(), siblings.end(), gameObject), siblings.end());
|
||||||
|
} else {
|
||||||
|
m_rootGameObjects.erase(
|
||||||
|
std::remove(m_rootGameObjects.begin(), m_rootGameObjects.end(), gameObject->m_id),
|
||||||
|
m_rootGameObjects.end()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_onGameObjectDestroyed.Invoke(gameObject);
|
||||||
|
|
||||||
|
gameObject->OnDestroy();
|
||||||
|
|
||||||
|
m_gameObjectIDs.erase(gameObject->m_id);
|
||||||
|
m_gameObjects.erase(gameObject->m_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
GameObject* Scene::Find(const std::string& name) const {
|
||||||
|
for (auto* go : GetRootGameObjects()) {
|
||||||
|
if (go->GetName() == name) {
|
||||||
|
return go;
|
||||||
|
}
|
||||||
|
GameObject* found = FindInChildren(go, name);
|
||||||
|
if (found) {
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
GameObject* Scene::FindInChildren(GameObject* parent, const std::string& name) const {
|
||||||
|
for (size_t i = 0; i < parent->GetChildCount(); ++i) {
|
||||||
|
GameObject* child = parent->GetChild(i);
|
||||||
|
if (child->GetName() == name) {
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
GameObject* found = FindInChildren(child, name);
|
||||||
|
if (found) {
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
GameObject* Scene::FindGameObjectWithTag(const std::string& tag) const {
|
||||||
|
for (auto* go : GetRootGameObjects()) {
|
||||||
|
if (go->GetName() == tag) {
|
||||||
|
return go;
|
||||||
|
}
|
||||||
|
GameObject* found = FindInChildren(go, tag);
|
||||||
|
if (found) {
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<GameObject*> Scene::GetRootGameObjects() const {
|
||||||
|
std::vector<GameObject*> result;
|
||||||
|
for (auto id : m_rootGameObjects) {
|
||||||
|
auto it = m_gameObjects.find(id);
|
||||||
|
if (it != m_gameObjects.end()) {
|
||||||
|
result.push_back(it->second.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Scene::Update(float deltaTime) {
|
||||||
|
for (auto* go : GetRootGameObjects()) {
|
||||||
|
if (go->IsActiveInHierarchy()) {
|
||||||
|
go->Update(deltaTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Scene::FixedUpdate(float fixedDeltaTime) {
|
||||||
|
for (auto* go : GetRootGameObjects()) {
|
||||||
|
if (go->IsActiveInHierarchy()) {
|
||||||
|
go->FixedUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Scene::LateUpdate(float deltaTime) {
|
||||||
|
for (auto* go : GetRootGameObjects()) {
|
||||||
|
if (go->IsActiveInHierarchy()) {
|
||||||
|
go->LateUpdate(deltaTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Scene::Load(const std::string& filePath) {
|
||||||
|
m_gameObjects.clear();
|
||||||
|
m_rootGameObjects.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Scene::Save(const std::string& filePath) {
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Components
|
||||||
|
} // namespace XCEngine
|
||||||
116
engine/src/Scene/SceneManager.cpp
Normal file
116
engine/src/Scene/SceneManager.cpp
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
#include "Scene/SceneManager.h"
|
||||||
|
|
||||||
|
namespace XCEngine {
|
||||||
|
namespace Components {
|
||||||
|
|
||||||
|
SceneManager& SceneManager::Get() {
|
||||||
|
static SceneManager instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
Scene* SceneManager::CreateScene(const std::string& name) {
|
||||||
|
auto scene = std::make_unique<Scene>(name);
|
||||||
|
Scene* ptr = scene.get();
|
||||||
|
m_scenes[name] = std::move(scene);
|
||||||
|
|
||||||
|
if (!m_activeScene) {
|
||||||
|
m_activeScene = ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_onSceneLoaded.Invoke(ptr);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneManager::LoadScene(const std::string& filePath) {
|
||||||
|
auto scene = std::make_unique<Scene>();
|
||||||
|
scene->Load(filePath);
|
||||||
|
Scene* ptr = scene.get();
|
||||||
|
|
||||||
|
std::string name = filePath;
|
||||||
|
size_t pos = name.find_last_of("/\\");
|
||||||
|
if (pos != std::string::npos) {
|
||||||
|
name = name.substr(pos + 1);
|
||||||
|
}
|
||||||
|
pos = name.find_last_of('.');
|
||||||
|
if (pos != std::string::npos) {
|
||||||
|
name = name.substr(0, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_scenes[name] = std::move(scene);
|
||||||
|
m_onSceneLoaded.Invoke(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneManager::LoadSceneAsync(const std::string& filePath, std::function<void(Scene*)> callback) {
|
||||||
|
LoadScene(filePath);
|
||||||
|
if (callback) {
|
||||||
|
Scene* scene = GetScene(filePath);
|
||||||
|
callback(scene);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneManager::UnloadScene(Scene* scene) {
|
||||||
|
if (!scene) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_activeScene == scene) {
|
||||||
|
m_activeScene = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = m_scenes.begin();
|
||||||
|
while (it != m_scenes.end()) {
|
||||||
|
if (it->second.get() == scene) {
|
||||||
|
m_onSceneUnloaded.Invoke(scene);
|
||||||
|
it = m_scenes.erase(it);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_activeScene == nullptr && !m_scenes.empty()) {
|
||||||
|
m_activeScene = m_scenes.begin()->second.get();
|
||||||
|
m_onActiveSceneChanged.Invoke(m_activeScene);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneManager::UnloadScene(const std::string& sceneName) {
|
||||||
|
auto it = m_scenes.find(sceneName);
|
||||||
|
if (it != m_scenes.end()) {
|
||||||
|
UnloadScene(it->second.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneManager::SetActiveScene(Scene* scene) {
|
||||||
|
if (m_activeScene == scene) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_activeScene = scene;
|
||||||
|
m_onActiveSceneChanged.Invoke(scene);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneManager::SetActiveScene(const std::string& sceneName) {
|
||||||
|
auto it = m_scenes.find(sceneName);
|
||||||
|
if (it != m_scenes.end()) {
|
||||||
|
SetActiveScene(it->second.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Scene* SceneManager::GetScene(const std::string& name) const {
|
||||||
|
auto it = m_scenes.find(name);
|
||||||
|
if (it != m_scenes.end()) {
|
||||||
|
return it->second.get();
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Scene*> SceneManager::GetAllScenes() const {
|
||||||
|
std::vector<Scene*> result;
|
||||||
|
for (auto& pair : m_scenes) {
|
||||||
|
result.push_back(pair.second.get());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Components
|
||||||
|
} // namespace XCEngine
|
||||||
Reference in New Issue
Block a user