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:
@@ -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
|
||||
@@ -29,6 +29,8 @@ namespace Engine::ECS {
|
||||
using Entity = entt::entity;
|
||||
using Registry = entt::registry;
|
||||
|
||||
class EntityHandle; // 表皮:e.AddComponent<T>() 风格(见 EntityHandle.h)
|
||||
|
||||
class Scene {
|
||||
public:
|
||||
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 FlushDestroyQueue(); // 每帧末尾由 Application 调用
|
||||
bool IsAlive(Entity e) const { return m_Registry.valid(e); }
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
//
|
||||
|
||||
#include <Engine/ECS/Scene.h>
|
||||
#include <Engine/ECS/EntityHandle.h>
|
||||
#include <Engine/Core/Log.h>
|
||||
|
||||
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<NameComponent>(e, NameComponent{std::move(name)});
|
||||
}
|
||||
return e;
|
||||
return EntityHandle{this, e};
|
||||
}
|
||||
|
||||
Entity Scene::FindEntityByUUID(UUID uuid)
|
||||
|
||||
Reference in New Issue
Block a user