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:
2026-03-20 19:59:06 +08:00
parent 394bec9db6
commit 810b0861c5
11 changed files with 2893 additions and 0 deletions

View 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

View 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
View 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

View 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