refactor: 实体句柄表皮——实体自己管理组件的 API 形态

- 新增 EntityHandle:e.AddComponent<T>() / e.GetComponent<T>() /
  e.Destroy() / e.GetUUID(),转发 entt registry,零存储开销
- Scene::CreateEntity 返回 EntityHandle;Scene 层组件接口保留
  (给渲染器/系统批量遍历用)
- 语义分层:应用层"实体管组件"(句柄表皮),
  数据层组件存储仍集中在 registry(ECS 性能根基)
This commit is contained in:
2026-09-22 13:59:18 +08:00
parent b73e197783
commit 394bfd3d0c
4 changed files with 150 additions and 23 deletions
+19 -20
View File
@@ -12,6 +12,7 @@
#include <Engine/Core/Log.h> #include <Engine/Core/Log.h>
#include <Engine/Core/Stats.h> #include <Engine/Core/Stats.h>
#include <Engine/ECS/Scene.h> #include <Engine/ECS/Scene.h>
#include <Engine/ECS/EntityHandle.h>
#include <Engine/ECS/Components.h> #include <Engine/ECS/Components.h>
#include <Engine/Assets/AssetManager.h> #include <Engine/Assets/AssetManager.h>
@@ -42,56 +43,54 @@ protected:
auto backpack = GetAssets().LoadModel("assets/models/backpack/backpack.obj"); auto backpack = GetAssets().LoadModel("assets/models/backpack/backpack.obj");
// 相机实体(primary = 当前渲染视角) // 相机实体(primary = 当前渲染视角)
m_CameraEntity = scene.CreateEntity("MainCamera"); m_Camera = scene.CreateEntity("MainCamera");
auto& camera = scene.AddComponent<CameraComponent>(m_CameraEntity); auto& camera = m_Camera.AddComponent<CameraComponent>();
camera.primary = true; camera.primary = true;
camera.camera.SetPosition(glm::vec3(0.0f, 0.0f, 7.0f)); camera.camera.SetPosition(glm::vec3(0.0f, 0.0f, 7.0f));
// 背包实体 × 3 // 背包实体 × 3
for (int i = 0; i < 3; ++i) { 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<TransformComponent>(e); e.AddComponent<TransformComponent>().world = glm::translate(
transform.world = glm::translate(
glm::mat4(1.0f), glm::mat4(1.0f),
glm::vec3((i - 1) * 1.5f, 0.0f, 0.0f)); glm::vec3((i - 1) * 1.5f, 0.0f, 0.0f));
scene.AddComponent<MeshRendererComponent>(e, MeshRendererComponent{backpack}); e.AddComponent<MeshRendererComponent>(MeshRendererComponent{backpack});
m_Spinners.push_back(e); m_Spinners.push_back(e);
} }
// 灯光实体 // 灯光实体
Entity keyLight = scene.CreateEntity("KeyLight"); scene.CreateEntity("KeyLight")
scene.AddComponent<LightComponent>(keyLight, .AddComponent<LightComponent>(
LightComponent{glm::vec3(-0.5f, -1.0f, -0.3f), LightComponent{glm::vec3(-0.5f, -1.0f, -0.3f),
glm::vec4(1.0f, 0.95f, 0.9f, 1.0f)}); glm::vec4(1.0f, 0.95f, 0.9f, 1.0f)});
Entity fillLight = scene.CreateEntity("FillLight"); scene.CreateEntity("FillLight")
scene.AddComponent<LightComponent>(fillLight, .AddComponent<LightComponent>(
LightComponent{glm::vec3(0.5f, -0.5f, 0.3f), LightComponent{glm::vec3(0.5f, -0.5f, 0.3f),
glm::vec4(0.3f, 0.5f, 1.0f, 0.8f)}); glm::vec4(0.3f, 0.5f, 1.0f, 0.8f)});
} }
void OnUpdate(f32 deltaTime) override void OnUpdate(f32 deltaTime) override
{ {
Scene& scene = GetScene();
m_Time += deltaTime; m_Time += deltaTime;
// 相机(WASD/Space/Shift 移动) // 相机(WASD/Space/Shift 移动)
scene.GetComponent<CameraComponent>(m_CameraEntity) m_Camera.GetComponent<CameraComponent>()
.camera.Update(GetWindow(), deltaTime); .camera.Update(GetWindow(), deltaTime);
// 旋转动画:直接写实体组件 // 旋转动画:直接写实体组件
for (size_t i = 0; i < m_Spinners.size(); ++i) { for (size_t i = 0; i < m_Spinners.size(); ++i) {
f32 angle = m_Time * glm::radians(60.0f) + static_cast<f32>(i) * 0.7f; f32 angle = m_Time * glm::radians(60.0f) + static_cast<f32>(i) * 0.7f;
auto& transform = scene.GetComponent<TransformComponent>(m_Spinners[i]); auto& transform = m_Spinners[i].GetComponent<TransformComponent>();
glm::mat4 t = glm::translate( glm::mat4 t = glm::translate(
glm::mat4(1.0f), glm::mat4(1.0f),
glm::vec3((static_cast<f32>(i) - 1.0f) * 1.5f, 0.0f, 0.0f)); glm::vec3((static_cast<f32>(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 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; transform.world = t * r * s;
} }
@@ -115,8 +114,8 @@ protected:
} }
private: private:
Entity m_CameraEntity{entt::null}; // 注意:entt::entity{} 是 0 号实体,空句柄要用 entt::null EntityHandle m_Camera{}; // Scene::CreateEntity 之前为空
std::vector<Entity> m_Spinners; std::vector<EntityHandle> m_Spinners;
f32 m_Time = 0.0f; f32 m_Time = 0.0f;
}; };
+124
View File
@@ -0,0 +1,124 @@
//
// Created by atdunbg on 2026/9/22.
//
// EntityHandle:实体句柄表皮——"实体管理自己的组件"的 API 形态。
//
// 定位(重要):
// - 组件存储永远在 Scene 的 entt::registry 里(稀疏集池,集中连续内存),
// 这是 ECS 性能的根基;EntityHandle 只是转发表皮,零额外存储
// - 应用层用 EntityHandle(e.AddComponent<T>() 语义清晰);
// 渲染器/系统用 Scene::Each 批量遍历(拿原始 entt::entity)
//
// 生命周期:
// - 内部持有 Scene* + entt::entity;延迟销毁(Destroy 入队,帧尾 flush)
// - 实体被销毁后调用任何组件接口 → FATAL(generation 失配由 valid() 拦截)
//
#ifndef HELLOVULKAN_ENTITYHANDLE_H
#define HELLOVULKAN_ENTITYHANDLE_H
#include <Engine/Core/Log.h>
#include <Engine/ECS/Components.h>
#include <entt/entt.hpp>
#include <string>
namespace Engine::ECS {
class Scene;
class EntityHandle {
public:
EntityHandle() = default; // 空(未创建/已失效),任何组件访问都会 FATAL
// ---- 组件 ----
template<typename T, typename... Args>
T& AddComponent(Args&&... args)
{
auto& reg = ValidRegistry("AddComponent");
if (reg.all_of<T>(m_Entity)) {
ENGINE_FATAL("EntityHandle: entity already has component '{}'", typeid(T).name());
}
return reg.emplace<T>(m_Entity, std::forward<Args>(args)...);
}
template<typename T>
T& GetComponent()
{
auto& reg = ValidRegistry("GetComponent");
return reg.get<T>(m_Entity);
}
template<typename T>
T* TryGetComponent()
{
auto& reg = ValidRegistry("TryGetComponent");
return reg.try_get<T>(m_Entity);
}
template<typename T>
bool HasComponent()
{
auto& reg = ValidRegistry("HasComponent");
return reg.all_of<T>(m_Entity);
}
template<typename T>
void RemoveComponent()
{
auto& reg = ValidRegistry("RemoveComponent");
reg.remove<T>(m_Entity);
}
// ---- 身份 / 生命周期 ----
// 稳定 UUID(序列化/跨引用用)
UUID GetUUID()
{
return ValidRegistry("GetUUID").template get<IdComponent>(m_Entity).uuid;
}
std::string GetName()
{
auto* name = ValidRegistry("GetName").template try_get<NameComponent>(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
+4 -1
View File
@@ -29,6 +29,8 @@ namespace Engine::ECS {
using Entity = entt::entity; using Entity = entt::entity;
using Registry = entt::registry; using Registry = entt::registry;
class EntityHandle; // 表皮:e.AddComponent<T>() 风格(见 EntityHandle.h)
class Scene { class Scene {
public: public:
Scene() = default; Scene() = default;
@@ -45,7 +47,8 @@ namespace Engine::ECS {
// ---- 实体 ---- // ---- 实体 ----
Entity CreateEntity(std::string name = {}); // 返回句柄表皮:e.AddComponent<T>() / e.GetComponent<T>() / e.Destroy()
EntityHandle CreateEntity(std::string name = {});
void DestroyEntity(Entity e); // 延迟:帧尾统一销毁 void DestroyEntity(Entity e); // 延迟:帧尾统一销毁
void FlushDestroyQueue(); // 每帧末尾由 Application 调用 void FlushDestroyQueue(); // 每帧末尾由 Application 调用
bool IsAlive(Entity e) const { return m_Registry.valid(e); } bool IsAlive(Entity e) const { return m_Registry.valid(e); }
+3 -2
View File
@@ -3,11 +3,12 @@
// //
#include <Engine/ECS/Scene.h> #include <Engine/ECS/Scene.h>
#include <Engine/ECS/EntityHandle.h>
#include <Engine/Core/Log.h> #include <Engine/Core/Log.h>
namespace Engine::ECS { namespace Engine::ECS {
Entity Scene::CreateEntity(std::string name) EntityHandle Scene::CreateEntity(std::string name)
{ {
Entity e = m_Registry.create(); Entity e = m_Registry.create();
// IdComponent 自动挂载:每个实体出生即有稳定 UUID // IdComponent 自动挂载:每个实体出生即有稳定 UUID
@@ -15,7 +16,7 @@ namespace Engine::ECS {
if (!name.empty()) { if (!name.empty()) {
m_Registry.emplace<NameComponent>(e, NameComponent{std::move(name)}); m_Registry.emplace<NameComponent>(e, NameComponent{std::move(name)});
} }
return e; return EntityHandle{this, e};
} }
Entity Scene::FindEntityByUUID(UUID uuid) Entity Scene::FindEntityByUUID(UUID uuid)