diff --git a/App/main.cpp b/App/main.cpp index c76b6c8..70d0c12 100644 --- a/App/main.cpp +++ b/App/main.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -42,56 +43,54 @@ protected: auto backpack = GetAssets().LoadModel("assets/models/backpack/backpack.obj"); // 相机实体(primary = 当前渲染视角) - m_CameraEntity = scene.CreateEntity("MainCamera"); - auto& camera = scene.AddComponent(m_CameraEntity); + m_Camera = scene.CreateEntity("MainCamera"); + auto& camera = m_Camera.AddComponent(); camera.primary = true; camera.camera.SetPosition(glm::vec3(0.0f, 0.0f, 7.0f)); // 背包实体 × 3 for (int i = 0; i < 3; ++i) { - Entity e = scene.CreateEntity("Backpack" + std::to_string(i)); + auto e = scene.CreateEntity("Backpack" + std::to_string(i)); - auto& transform = scene.AddComponent(e); - transform.world = glm::translate( + e.AddComponent().world = glm::translate( glm::mat4(1.0f), glm::vec3((i - 1) * 1.5f, 0.0f, 0.0f)); - scene.AddComponent(e, MeshRendererComponent{backpack}); + e.AddComponent(MeshRendererComponent{backpack}); m_Spinners.push_back(e); } // 灯光实体 - Entity keyLight = scene.CreateEntity("KeyLight"); - scene.AddComponent(keyLight, - LightComponent{glm::vec3(-0.5f, -1.0f, -0.3f), - glm::vec4(1.0f, 0.95f, 0.9f, 1.0f)}); + scene.CreateEntity("KeyLight") + .AddComponent( + LightComponent{glm::vec3(-0.5f, -1.0f, -0.3f), + glm::vec4(1.0f, 0.95f, 0.9f, 1.0f)}); - Entity fillLight = scene.CreateEntity("FillLight"); - scene.AddComponent(fillLight, - LightComponent{glm::vec3(0.5f, -0.5f, 0.3f), - glm::vec4(0.3f, 0.5f, 1.0f, 0.8f)}); + scene.CreateEntity("FillLight") + .AddComponent( + LightComponent{glm::vec3(0.5f, -0.5f, 0.3f), + glm::vec4(0.3f, 0.5f, 1.0f, 0.8f)}); } void OnUpdate(f32 deltaTime) override { - Scene& scene = GetScene(); m_Time += deltaTime; // 相机(WASD/Space/Shift 移动) - scene.GetComponent(m_CameraEntity) + m_Camera.GetComponent() .camera.Update(GetWindow(), deltaTime); // 旋转动画:直接写实体组件 for (size_t i = 0; i < m_Spinners.size(); ++i) { f32 angle = m_Time * glm::radians(60.0f) + static_cast(i) * 0.7f; - auto& transform = scene.GetComponent(m_Spinners[i]); + auto& transform = m_Spinners[i].GetComponent(); glm::mat4 t = glm::translate( glm::mat4(1.0f), glm::vec3((static_cast(i) - 1.0f) * 1.5f, 0.0f, 0.0f)); glm::mat4 r = glm::rotate(glm::mat4(1.0f), angle, glm::vec3(0.0f, 1.0f, 0.0f)); - glm::mat4 s = glm::scale(glm::mat4(1.0f), glm::vec3(0.5)); + glm::mat4 s = glm::scale(glm::mat4(1.0f), glm::vec3(0.5f)); transform.world = t * r * s; } @@ -115,8 +114,8 @@ protected: } private: - Entity m_CameraEntity{entt::null}; // 注意:entt::entity{} 是 0 号实体,空句柄要用 entt::null - std::vector m_Spinners; + EntityHandle m_Camera{}; // Scene::CreateEntity 之前为空 + std::vector m_Spinners; f32 m_Time = 0.0f; }; diff --git a/Engine/include/Engine/ECS/EntityHandle.h b/Engine/include/Engine/ECS/EntityHandle.h new file mode 100644 index 0000000..8b895fb --- /dev/null +++ b/Engine/include/Engine/ECS/EntityHandle.h @@ -0,0 +1,124 @@ +// +// Created by atdunbg on 2026/9/22. +// +// EntityHandle:实体句柄表皮——"实体管理自己的组件"的 API 形态。 +// +// 定位(重要): +// - 组件存储永远在 Scene 的 entt::registry 里(稀疏集池,集中连续内存), +// 这是 ECS 性能的根基;EntityHandle 只是转发表皮,零额外存储 +// - 应用层用 EntityHandle(e.AddComponent() 语义清晰); +// 渲染器/系统用 Scene::Each 批量遍历(拿原始 entt::entity) +// +// 生命周期: +// - 内部持有 Scene* + entt::entity;延迟销毁(Destroy 入队,帧尾 flush) +// - 实体被销毁后调用任何组件接口 → FATAL(generation 失配由 valid() 拦截) +// + +#ifndef HELLOVULKAN_ENTITYHANDLE_H +#define HELLOVULKAN_ENTITYHANDLE_H + +#include +#include + +#include +#include + +namespace Engine::ECS { + + class Scene; + + class EntityHandle { + public: + EntityHandle() = default; // 空(未创建/已失效),任何组件访问都会 FATAL + + // ---- 组件 ---- + + template + T& AddComponent(Args&&... args) + { + auto& reg = ValidRegistry("AddComponent"); + if (reg.all_of(m_Entity)) { + ENGINE_FATAL("EntityHandle: entity already has component '{}'", typeid(T).name()); + } + return reg.emplace(m_Entity, std::forward(args)...); + } + + template + T& GetComponent() + { + auto& reg = ValidRegistry("GetComponent"); + return reg.get(m_Entity); + } + + template + T* TryGetComponent() + { + auto& reg = ValidRegistry("TryGetComponent"); + return reg.try_get(m_Entity); + } + + template + bool HasComponent() + { + auto& reg = ValidRegistry("HasComponent"); + return reg.all_of(m_Entity); + } + + template + void RemoveComponent() + { + auto& reg = ValidRegistry("RemoveComponent"); + reg.remove(m_Entity); + } + + // ---- 身份 / 生命周期 ---- + + // 稳定 UUID(序列化/跨引用用) + UUID GetUUID() + { + return ValidRegistry("GetUUID").template get(m_Entity).uuid; + } + + std::string GetName() + { + auto* name = ValidRegistry("GetName").template try_get(m_Entity); + return name ? name->name : std::string{}; + } + + bool IsAlive() const + { + return m_Scene && m_Scene->GetRegistry().valid(m_Entity); + } + + // 延迟销毁:入队,帧尾统一执行(遍历安全) + void Destroy() + { + ValidRegistry("Destroy"); + m_Scene->DestroyEntity(m_Entity); + } + + // ---- 与裸 entt::entity 互操作(传给渲染器/遍历接口)---- + operator entt::entity() const { return m_Entity; } // NOLINT: 有意的隐式转换 + + bool operator==(const EntityHandle& o) const { return m_Scene == o.m_Scene && m_Entity == o.m_Entity; } + bool operator!=(const EntityHandle& o) const { return !(*this == o); } + + private: + friend class Scene; + EntityHandle(Scene* scene, entt::entity e) : m_Scene(scene), m_Entity(e) {} + + entt::registry& ValidRegistry(const char* caller) + { + if (!m_Scene || !m_Scene->GetRegistry().valid(m_Entity)) { + ENGINE_FATAL("EntityHandle::{}: stale or invalid entity", caller); + } + return m_Scene->GetRegistry(); + } + + Scene* m_Scene = nullptr; + entt::entity m_Entity{entt::null}; + }; + +} // namespace Engine::ECS + +#endif //HELLOVULKAN_ENTITYHANDLE_H diff --git a/Engine/include/Engine/ECS/Scene.h b/Engine/include/Engine/ECS/Scene.h index 801beb5..fd66e6c 100644 --- a/Engine/include/Engine/ECS/Scene.h +++ b/Engine/include/Engine/ECS/Scene.h @@ -29,6 +29,8 @@ namespace Engine::ECS { using Entity = entt::entity; using Registry = entt::registry; + class EntityHandle; // 表皮:e.AddComponent() 风格(见 EntityHandle.h) + class Scene { public: Scene() = default; @@ -45,7 +47,8 @@ namespace Engine::ECS { // ---- 实体 ---- - Entity CreateEntity(std::string name = {}); + // 返回句柄表皮:e.AddComponent() / e.GetComponent() / e.Destroy() + EntityHandle CreateEntity(std::string name = {}); void DestroyEntity(Entity e); // 延迟:帧尾统一销毁 void FlushDestroyQueue(); // 每帧末尾由 Application 调用 bool IsAlive(Entity e) const { return m_Registry.valid(e); } diff --git a/Engine/src/ECS/Scene.cpp b/Engine/src/ECS/Scene.cpp index ab1ed41..d687a0b 100644 --- a/Engine/src/ECS/Scene.cpp +++ b/Engine/src/ECS/Scene.cpp @@ -3,11 +3,12 @@ // #include +#include #include namespace Engine::ECS { - Entity Scene::CreateEntity(std::string name) + EntityHandle Scene::CreateEntity(std::string name) { Entity e = m_Registry.create(); // IdComponent 自动挂载:每个实体出生即有稳定 UUID @@ -15,7 +16,7 @@ namespace Engine::ECS { if (!name.empty()) { m_Registry.emplace(e, NameComponent{std::move(name)}); } - return e; + return EntityHandle{this, e}; } Entity Scene::FindEntityByUUID(UUID uuid)