refactor: ECS 迁移到 entt,移除自研 Registry
- Entity = entt::entity(内部自带版本号),Registry = entt::registry - Scene 对外接口不变(AddComponent/GetComponent/Each/延迟销毁/UUID), Renderer 与 App 层零改动完成迁移 - Each 转发 entt view(自动最小池遍历),延迟销毁队列保留 - 删除自研 Registry.h/Entity.h
This commit is contained in:
+1
-1
@@ -115,7 +115,7 @@ protected:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Entity m_CameraEntity{};
|
Entity m_CameraEntity{entt::null}; // 注意:entt::entity{} 是 0 号实体,空句柄要用 entt::null
|
||||||
std::vector<Entity> m_Spinners;
|
std::vector<Entity> m_Spinners;
|
||||||
f32 m_Time = 0.0f;
|
f32 m_Time = 0.0f;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ add_library(Engine STATIC ${ENGINE_SOURCES})
|
|||||||
|
|
||||||
target_include_directories(Engine PUBLIC
|
target_include_directories(Engine PUBLIC
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||||
|
${CMAKE_SOURCE_DIR}/Lib/entt/src
|
||||||
)
|
)
|
||||||
|
|
||||||
# 链接第三方库
|
# 链接第三方库
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by atdunbg on 2026/9/22.
|
|
||||||
//
|
|
||||||
// Entity:实体句柄 = (池内索引, 生成号)。
|
|
||||||
//
|
|
||||||
// 生成号用于甄别悬空引用:实体销毁后索引会回收复用,
|
|
||||||
// 旧的 Entity 句柄因 generation 不匹配会被 Registry 拒绝,
|
|
||||||
// 从机制上杜绝"引用到复用后的错误实体"这类隐式 bug。
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef HELLOVULKAN_ENTITY_H
|
|
||||||
#define HELLOVULKAN_ENTITY_H
|
|
||||||
|
|
||||||
#include <Engine/Core/Types.h>
|
|
||||||
|
|
||||||
namespace Engine {
|
|
||||||
|
|
||||||
struct Entity {
|
|
||||||
static constexpr u32 kInvalid = 0xFFFFFFFF;
|
|
||||||
|
|
||||||
u32 index = kInvalid; // 实体槽位索引
|
|
||||||
u32 generation = 0; // 槽位复用计数
|
|
||||||
|
|
||||||
bool Valid() const { return index != kInvalid; }
|
|
||||||
explicit operator bool() const { return Valid(); }
|
|
||||||
bool operator==(const Entity& o) const = default;
|
|
||||||
bool operator!=(const Entity& o) const = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Engine
|
|
||||||
|
|
||||||
#endif //HELLOVULKAN_ENTITY_H
|
|
||||||
@@ -1,312 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by atdunbg on 2026/9/22.
|
|
||||||
//
|
|
||||||
// Registry:稀疏集 ECS 核心。
|
|
||||||
//
|
|
||||||
// 数据结构(与 EnTT 同族,自研实现):
|
|
||||||
// - 每个组件类型一个 ComponentPool<T>:sparse 索引表 + dense 实体/组件数组
|
|
||||||
// - Add/Remove/Get 全部 O(1);Remove 用 swap-remove(O(1),代价是
|
|
||||||
// dense 顺序会变——渲染遍历只要求"每帧确定",不要求跨帧稳定)
|
|
||||||
// - Each<A,B,...> 以最小的池为主表遍历,其余池做 O(1) Contains 探测,
|
|
||||||
// 复杂度 O(min(|A|,|B|,...))——大场景下的关键保证
|
|
||||||
//
|
|
||||||
// 约束(引擎级正确性):
|
|
||||||
// - 悬空句柄:generation 不匹配直接 FATAL,不静默
|
|
||||||
// - 遍历中禁止结构变更(Add/Remove 组件、Create/Destroy 实体),
|
|
||||||
// 会使池失效——销毁走延迟队列(Scene::DestroyEntity → FlushDestroyQueue)
|
|
||||||
// - 全程无虚调用开销在热路径上(Get/Add 直接走模板池)
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef HELLOVULKAN_REGISTRY_H
|
|
||||||
#define HELLOVULKAN_REGISTRY_H
|
|
||||||
|
|
||||||
#include <Engine/Core/Types.h>
|
|
||||||
#include <Engine/Core/Log.h>
|
|
||||||
#include <Engine/ECS/Entity.h>
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <tuple>
|
|
||||||
#include <vector>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
namespace Engine::ECS {
|
|
||||||
|
|
||||||
// ---------- 组件类型 ID(编译期静态分配) ----------
|
|
||||||
|
|
||||||
using ComponentTypeId = u32;
|
|
||||||
|
|
||||||
namespace detail {
|
|
||||||
inline ComponentTypeId NextComponentTypeId()
|
|
||||||
{
|
|
||||||
static ComponentTypeId counter = 0;
|
|
||||||
return counter++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
ComponentTypeId ComponentTypeIdOf()
|
|
||||||
{
|
|
||||||
static const ComponentTypeId id = detail::NextComponentTypeId();
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------- 类型擦除池接口(Registry 管理生命周期 + 视图主表遍历用) ----------
|
|
||||||
|
|
||||||
class IPool {
|
|
||||||
public:
|
|
||||||
virtual ~IPool() = default;
|
|
||||||
virtual u32 Size() const = 0;
|
|
||||||
virtual Entity EntityAt(u32 denseIndex) const = 0;
|
|
||||||
virtual bool Remove(Entity e) = 0; // 不存在返回 false
|
|
||||||
};
|
|
||||||
|
|
||||||
// ---------- 稀疏集组件池 ----------
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
class ComponentPool final : public IPool {
|
|
||||||
public:
|
|
||||||
static constexpr u32 kInvalid = 0xFFFFFFFF;
|
|
||||||
|
|
||||||
T& Add(Entity e, T component)
|
|
||||||
{
|
|
||||||
if (Contains(e)) {
|
|
||||||
ENGINE_FATAL("ComponentPool: entity ({},{}) already has this component",
|
|
||||||
e.index, e.generation);
|
|
||||||
}
|
|
||||||
if (e.index >= m_Sparse.size()) {
|
|
||||||
m_Sparse.resize(e.index + 1, kInvalid);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_Sparse[e.index] = static_cast<u32>(m_Components.size());
|
|
||||||
m_Entities.push_back(e);
|
|
||||||
m_Components.push_back(std::move(component));
|
|
||||||
return m_Components.back();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Contains(Entity e) const
|
|
||||||
{
|
|
||||||
return e.index < m_Sparse.size() &&
|
|
||||||
m_Sparse[e.index] != kInvalid &&
|
|
||||||
m_Entities[m_Sparse[e.index]] == e;
|
|
||||||
}
|
|
||||||
|
|
||||||
T& Get(Entity e)
|
|
||||||
{
|
|
||||||
if (!Contains(e)) {
|
|
||||||
ENGINE_FATAL("ComponentPool: entity ({},{}) does not have this component",
|
|
||||||
e.index, e.generation);
|
|
||||||
}
|
|
||||||
return m_Components[m_Sparse[e.index]];
|
|
||||||
}
|
|
||||||
|
|
||||||
const T& Get(Entity e) const
|
|
||||||
{
|
|
||||||
return const_cast<ComponentPool*>(this)->Get(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Remove(Entity e) override
|
|
||||||
{
|
|
||||||
if (!Contains(e)) return false;
|
|
||||||
|
|
||||||
const u32 denseIdx = m_Sparse[e.index];
|
|
||||||
const u32 lastIdx = static_cast<u32>(m_Components.size()) - 1;
|
|
||||||
|
|
||||||
if (denseIdx != lastIdx) {
|
|
||||||
// swap-remove:尾元素补位
|
|
||||||
m_Entities[denseIdx] = m_Entities[lastIdx];
|
|
||||||
m_Components[denseIdx] = std::move(m_Components[lastIdx]);
|
|
||||||
m_Sparse[m_Entities[denseIdx].index] = denseIdx;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_Sparse[e.index] = kInvalid;
|
|
||||||
m_Entities.pop_back();
|
|
||||||
m_Components.pop_back();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 Size() const override { return static_cast<u32>(m_Components.size()); }
|
|
||||||
Entity EntityAt(u32 denseIndex) const override { return m_Entities[denseIndex]; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<u32> m_Sparse; // 实体索引 -> dense 下标(kInvalid = 无)
|
|
||||||
std::vector<Entity> m_Entities; // dense:与 m_Components 平行
|
|
||||||
std::vector<T> m_Components; // dense:组件数据(连续内存,缓存友好)
|
|
||||||
};
|
|
||||||
|
|
||||||
// ---------- Registry ----------
|
|
||||||
|
|
||||||
class Registry {
|
|
||||||
public:
|
|
||||||
Registry() = default;
|
|
||||||
~Registry() = default;
|
|
||||||
|
|
||||||
Registry(const Registry&) = delete;
|
|
||||||
Registry& operator=(const Registry&) = delete;
|
|
||||||
|
|
||||||
// ---- 实体生命周期 ----
|
|
||||||
|
|
||||||
Entity Create()
|
|
||||||
{
|
|
||||||
if (!m_FreeIndices.empty()) {
|
|
||||||
const u32 index = m_FreeIndices.back();
|
|
||||||
m_FreeIndices.pop_back();
|
|
||||||
m_Records[index].alive = true; // generation 已在 Destroy 时递增
|
|
||||||
return Entity{index, m_Records[index].generation};
|
|
||||||
}
|
|
||||||
|
|
||||||
m_Records.push_back({0, true});
|
|
||||||
return Entity{static_cast<u32>(m_Records.size() - 1), 0};
|
|
||||||
}
|
|
||||||
|
|
||||||
void Destroy(Entity e)
|
|
||||||
{
|
|
||||||
if (!IsAlive(e)) {
|
|
||||||
ENGINE_FATAL("Registry::Destroy: stale entity ({},{})",
|
|
||||||
e.index, e.generation);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto& pool : m_Pools) {
|
|
||||||
if (pool) pool->Remove(e); // 不在池中就跳过
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& record = m_Records[e.index];
|
|
||||||
record.alive = false;
|
|
||||||
++record.generation;
|
|
||||||
m_FreeIndices.push_back(e.index);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsAlive(Entity e) const
|
|
||||||
{
|
|
||||||
return e.Valid() && e.index < m_Records.size() &&
|
|
||||||
m_Records[e.index].alive &&
|
|
||||||
m_Records[e.index].generation == e.generation;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- 组件操作 ----
|
|
||||||
|
|
||||||
template<typename T, typename... Args>
|
|
||||||
T& Add(Entity e, Args&&... args)
|
|
||||||
{
|
|
||||||
if (!IsAlive(e)) {
|
|
||||||
ENGINE_FATAL("Registry::Add: stale entity ({},{})", e.index, e.generation);
|
|
||||||
}
|
|
||||||
return AssurePool<T>().Add(e, T{std::forward<Args>(args)...});
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
T& Get(Entity e)
|
|
||||||
{
|
|
||||||
if (!IsAlive(e)) {
|
|
||||||
ENGINE_FATAL("Registry::Get: stale entity ({},{})", e.index, e.generation);
|
|
||||||
}
|
|
||||||
auto* pool = GetPool<T>();
|
|
||||||
if (!pool) {
|
|
||||||
ENGINE_FATAL("Registry::Get: no component pool of this type exists");
|
|
||||||
}
|
|
||||||
return pool->Get(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
T* TryGet(Entity e)
|
|
||||||
{
|
|
||||||
if (!IsAlive(e)) return nullptr;
|
|
||||||
auto* pool = GetPool<T>();
|
|
||||||
return (pool && pool->Contains(e)) ? &pool->Get(e) : nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
bool Has(Entity e)
|
|
||||||
{
|
|
||||||
if (!IsAlive(e)) return false;
|
|
||||||
auto* pool = GetPool<T>();
|
|
||||||
return pool && pool->Contains(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
bool Remove(Entity e)
|
|
||||||
{
|
|
||||||
if (!IsAlive(e)) return false;
|
|
||||||
auto* pool = GetPool<T>();
|
|
||||||
return pool && pool->Remove(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- 遍历(视图)----
|
|
||||||
// fn(Entity, Ts&...);以最小的池为主表,复杂度 O(min pool size)。
|
|
||||||
// 遍历中禁止结构变更(见文件头约束)。
|
|
||||||
template<typename... Ts, typename Fn>
|
|
||||||
void Each(Fn&& fn)
|
|
||||||
{
|
|
||||||
static_assert(sizeof...(Ts) > 0, "Each requires at least one component type");
|
|
||||||
|
|
||||||
std::tuple<ComponentPool<Ts>*...> pools{GetPool<Ts>()...};
|
|
||||||
|
|
||||||
// 任一类型池不存在 → 空集
|
|
||||||
bool allExist = true;
|
|
||||||
std::apply([&allExist](auto*... p) {
|
|
||||||
((p || (allExist = false)) && ...);
|
|
||||||
}, pools);
|
|
||||||
if (!allExist) return;
|
|
||||||
|
|
||||||
// 主池:取最小者
|
|
||||||
const std::array<IPool*, sizeof...(Ts)> basePools{GetPool<Ts>()...};
|
|
||||||
IPool* primary = nullptr;
|
|
||||||
u32 minSize = 0xFFFFFFFF;
|
|
||||||
for (auto* p : basePools) {
|
|
||||||
if (p && p->Size() < minSize) {
|
|
||||||
minSize = p->Size();
|
|
||||||
primary = p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!primary) return;
|
|
||||||
|
|
||||||
for (u32 i = 0; i < primary->Size(); ++i) {
|
|
||||||
const Entity e = primary->EntityAt(i);
|
|
||||||
if (!IsAlive(e)) continue;
|
|
||||||
|
|
||||||
bool all = true;
|
|
||||||
std::apply([&](auto*... p) {
|
|
||||||
all = (p->Contains(e) && ...); // 短路折叠
|
|
||||||
}, pools);
|
|
||||||
if (!all) continue;
|
|
||||||
|
|
||||||
std::apply([&](auto*... p) {
|
|
||||||
fn(e, p->Get(e)...);
|
|
||||||
}, pools);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
template<typename T>
|
|
||||||
ComponentPool<T>* GetPool()
|
|
||||||
{
|
|
||||||
const ComponentTypeId id = ComponentTypeIdOf<T>();
|
|
||||||
if (id >= m_Pools.size() || !m_Pools[id]) return nullptr;
|
|
||||||
return static_cast<ComponentPool<T>*>(m_Pools[id].get());
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
ComponentPool<T>& AssurePool()
|
|
||||||
{
|
|
||||||
const ComponentTypeId id = ComponentTypeIdOf<T>();
|
|
||||||
if (id >= m_Pools.size()) {
|
|
||||||
m_Pools.resize(id + 1);
|
|
||||||
}
|
|
||||||
if (!m_Pools[id]) {
|
|
||||||
m_Pools[id] = std::make_unique<ComponentPool<T>>();
|
|
||||||
}
|
|
||||||
return static_cast<ComponentPool<T>&>(*m_Pools[id]);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct EntityRecord {
|
|
||||||
u32 generation = 0;
|
|
||||||
bool alive = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<EntityRecord> m_Records;
|
|
||||||
std::vector<u32> m_FreeIndices; // 可复用槽位
|
|
||||||
std::vector<std::unique_ptr<IPool>> m_Pools; // 按 ComponentTypeId 索引
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Engine::ECS
|
|
||||||
|
|
||||||
#endif //HELLOVULKAN_REGISTRY_H
|
|
||||||
@@ -1,24 +1,34 @@
|
|||||||
//
|
//
|
||||||
// Created by atdunbg on 2026/9/22.
|
// Created by atdunbg on 2026/9/22.
|
||||||
//
|
//
|
||||||
// Scene:世界容器 = Registry + 场景级设置 + 安全的生命周期封装。
|
// Scene:世界容器 = entt::registry + 场景级设置 + 安全的生命周期封装。
|
||||||
|
//
|
||||||
|
// ECS 基于 entt(skypjack/entt,header-only,稀疏集实现):
|
||||||
|
// - Entity 即 entt::entity(内部自带版本号,悬空句柄由 valid() 拦截)
|
||||||
|
// - 组件池为稀疏集,O(1) 增删查;view 遍历自动取最小池
|
||||||
//
|
//
|
||||||
// 销毁策略:DestroyEntity 只入队,FlushDestroyQueue 在帧尾统一执行,
|
// 销毁策略:DestroyEntity 只入队,FlushDestroyQueue 在帧尾统一执行,
|
||||||
// 保证遍历(Each)过程中组件池结构不变——这是 ECS 正确性的关键约束。
|
// 保证遍历过程中组件池结构不变——这是 ECS 正确性的关键约束。
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef HELLOVULKAN_SCENE_H
|
#ifndef HELLOVULKAN_SCENE_H
|
||||||
#define HELLOVULKAN_SCENE_H
|
#define HELLOVULKAN_SCENE_H
|
||||||
|
|
||||||
#include <Engine/Core/Types.h>
|
#include <Engine/Core/Types.h>
|
||||||
#include <Engine/ECS/Registry.h>
|
#include <Engine/Core/UUID.h>
|
||||||
#include <Engine/ECS/Components.h>
|
#include <Engine/ECS/Components.h>
|
||||||
|
|
||||||
|
#include <entt/entt.hpp>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace Engine::ECS {
|
namespace Engine::ECS {
|
||||||
|
|
||||||
|
// 实体句柄直接用 entt::entity:
|
||||||
|
// - 判空用 `e == entt::null`(不要用 Entity{} —— 那是 0 号实体!)
|
||||||
|
using Entity = entt::entity;
|
||||||
|
using Registry = entt::registry;
|
||||||
|
|
||||||
class Scene {
|
class Scene {
|
||||||
public:
|
public:
|
||||||
Scene() = default;
|
Scene() = default;
|
||||||
@@ -38,35 +48,38 @@ namespace Engine::ECS {
|
|||||||
Entity CreateEntity(std::string name = {});
|
Entity 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.IsAlive(e); }
|
bool IsAlive(Entity e) const { return m_Registry.valid(e); }
|
||||||
|
|
||||||
// 按稳定 UUID 查实体(序列化/跨引用场景);找不到返回无效句柄
|
// 按稳定 UUID 查实体(序列化/跨引用场景);找不到返回 entt::null
|
||||||
Entity FindEntityByUUID(UUID uuid);
|
Entity FindEntityByUUID(UUID uuid);
|
||||||
|
|
||||||
Registry& GetRegistry() { return m_Registry; }
|
Registry& GetRegistry() { return m_Registry; }
|
||||||
|
|
||||||
// ---- 组件便捷接口(转发 Registry)----
|
// ---- 组件便捷接口(转发 entt)----
|
||||||
|
|
||||||
template<typename T, typename... Args>
|
template<typename T, typename... Args>
|
||||||
T& AddComponent(Entity e, Args&&... args)
|
T& AddComponent(Entity e, Args&&... args)
|
||||||
{
|
{
|
||||||
return m_Registry.Add<T>(e, std::forward<Args>(args)...);
|
return m_Registry.emplace<T>(e, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
T& GetComponent(Entity e) { return m_Registry.Get<T>(e); }
|
T& GetComponent(Entity e) { return m_Registry.get<T>(e); }
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
T* TryGetComponent(Entity e) { return m_Registry.TryGet<T>(e); }
|
T* TryGetComponent(Entity e) { return m_Registry.try_get<T>(e); }
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
bool HasComponent(Entity e) { return m_Registry.Has<T>(e); }
|
bool HasComponent(Entity e) { return m_Registry.all_of<T>(e); }
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void RemoveComponent(Entity e) { m_Registry.Remove<T>(e); }
|
void RemoveComponent(Entity e) { m_Registry.remove<T>(e); }
|
||||||
|
|
||||||
|
// 遍历拥有全部 Ts 组件的实体:fn(Entity, Ts&...)
|
||||||
|
// entt view 自动以最小池为主表,复杂度 O(min pool)
|
||||||
|
// 注意:遍历中禁止结构变更(创建/销毁实体、增删组件)——销毁走延迟队列
|
||||||
template<typename... Ts, typename Fn>
|
template<typename... Ts, typename Fn>
|
||||||
void Each(Fn&& fn) { m_Registry.Each<Ts...>(std::forward<Fn>(fn)); }
|
void Each(Fn&& fn) { m_Registry.view<Ts...>().each(std::forward<Fn>(fn)); }
|
||||||
|
|
||||||
// ---- 常用查询 ----
|
// ---- 常用查询 ----
|
||||||
|
|
||||||
|
|||||||
+14
-13
@@ -9,32 +9,33 @@ namespace Engine::ECS {
|
|||||||
|
|
||||||
Entity Scene::CreateEntity(std::string name)
|
Entity Scene::CreateEntity(std::string name)
|
||||||
{
|
{
|
||||||
Entity e = m_Registry.Create();
|
Entity e = m_Registry.create();
|
||||||
// IdComponent 自动挂载:每个实体出生即有稳定 UUID
|
// IdComponent 自动挂载:每个实体出生即有稳定 UUID
|
||||||
m_Registry.Add<IdComponent>(e, IdComponent{UUID::New()});
|
m_Registry.emplace<IdComponent>(e, IdComponent{UUID::New()});
|
||||||
if (!name.empty()) {
|
if (!name.empty()) {
|
||||||
m_Registry.Add<NameComponent>(e, NameComponent{std::move(name)});
|
m_Registry.emplace<NameComponent>(e, NameComponent{std::move(name)});
|
||||||
}
|
}
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
Entity Scene::FindEntityByUUID(UUID uuid)
|
Entity Scene::FindEntityByUUID(UUID uuid)
|
||||||
{
|
{
|
||||||
if (!uuid.Valid()) return {};
|
if (!uuid.Valid()) return entt::null;
|
||||||
|
|
||||||
Entity found{};
|
Entity found = entt::null;
|
||||||
m_Registry.Each<IdComponent>([&](Entity e, IdComponent& id) {
|
m_Registry.view<IdComponent>().each([&](Entity e, IdComponent& id) {
|
||||||
if (!found && id.uuid == uuid) found = e;
|
if (found == entt::null && id.uuid == uuid) found = e;
|
||||||
});
|
});
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scene::DestroyEntity(Entity e)
|
void Scene::DestroyEntity(Entity e)
|
||||||
{
|
{
|
||||||
if (!m_Registry.IsAlive(e)) {
|
if (!m_Registry.valid(e)) {
|
||||||
ENGINE_FATAL("Scene::DestroyEntity: stale entity ({},{})",
|
ENGINE_FATAL("Scene::DestroyEntity: stale entity ({})",
|
||||||
e.index, e.generation);
|
static_cast<u32>(e));
|
||||||
}
|
}
|
||||||
|
// 延迟到帧尾,保证遍历期间池结构不变
|
||||||
m_PendingDestroy.push_back(e);
|
m_PendingDestroy.push_back(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,8 +44,8 @@ namespace Engine::ECS {
|
|||||||
if (m_PendingDestroy.empty()) return;
|
if (m_PendingDestroy.empty()) return;
|
||||||
|
|
||||||
for (Entity e : m_PendingDestroy) {
|
for (Entity e : m_PendingDestroy) {
|
||||||
if (m_Registry.IsAlive(e)) {
|
if (m_Registry.valid(e)) {
|
||||||
m_Registry.Destroy(e);
|
m_Registry.destroy(e); // entt 自动回收槽位并递增版本号
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_PendingDestroy.clear();
|
m_PendingDestroy.clear();
|
||||||
@@ -53,7 +54,7 @@ namespace Engine::ECS {
|
|||||||
CameraComponent* Scene::GetPrimaryCamera()
|
CameraComponent* Scene::GetPrimaryCamera()
|
||||||
{
|
{
|
||||||
CameraComponent* found = nullptr;
|
CameraComponent* found = nullptr;
|
||||||
m_Registry.Each<CameraComponent>([&](Entity, CameraComponent& cc) {
|
m_Registry.view<CameraComponent>().each([&](Entity, CameraComponent& cc) {
|
||||||
if (!found && cc.primary) found = &cc;
|
if (!found && cc.primary) found = &cc;
|
||||||
});
|
});
|
||||||
return found;
|
return found;
|
||||||
|
|||||||
Reference in New Issue
Block a user