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:
+15
-16
@@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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 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,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)
|
||||||
|
|||||||
Reference in New Issue
Block a user