diff --git a/Engine/include/Engine/ECS/Scene.h b/Engine/include/Engine/ECS/Scene.h index 3cb0f98..c77e639 100644 --- a/Engine/include/Engine/ECS/Scene.h +++ b/Engine/include/Engine/ECS/Scene.h @@ -97,6 +97,10 @@ namespace Engine::ECS { // 按稳定 UUID 查实体(序列化/跨引用场景);找不到返回 entt::null Entity FindEntityByUUID(UUID uuid); + // 全量重建 UUID → Entity 索引(序列化加载完调一次;运行期勿调, + // CreateEntity / FlushDestroyQueue 已增量维护) + void RebuildUuidIndex(); + Registry& GetRegistry() { return m_Registry; } const Registry& GetRegistry() const { return m_Registry; } // 序列化等只读遍历 @@ -137,6 +141,12 @@ namespace Engine::ECS { Registry m_Registry; std::vector m_PendingDestroy; Settings m_Settings; + + // UUID → Entity 索引(O(1) FindEntityByUUID;SyncConstraints 每帧调用) + // CreateEntity / FlushDestroyQueue 增量维护;序列化加载后由 + // RebuildUuidIndex() 全量重建一次(SceneSerializer 直接改 IdComponent.uuid + // 绕过 CreateEntity,仅靠增量会不同步) + std::unordered_map m_UUIDToEntity; }; } // namespace Engine::ECS diff --git a/Engine/include/Engine/Physics/PhysicsWorld.h b/Engine/include/Engine/Physics/PhysicsWorld.h index 74eac42..f686800 100644 --- a/Engine/include/Engine/Physics/PhysicsWorld.h +++ b/Engine/include/Engine/Physics/PhysicsWorld.h @@ -39,6 +39,7 @@ #include #include #include +#include #include namespace Engine { @@ -126,7 +127,7 @@ namespace Engine { ECS::TransformComponent& tr); // 约束对账:owner 孤儿清理 / body 重建传染失效 / 建缺失与变更重建 // invalidated = 本帧 SyncScene 中被销毁或重建 body 的实体位模式 - void SyncConstraints(const std::vector& invalidated); + void SyncConstraints(const std::unordered_set& invalidated); void CreateConstraint(u32 ownerBits, u32 targetBits, const ECS::JointComponent& jc); void RemoveConstraint(u32 ownerBits); diff --git a/Engine/src/ECS/Scene.cpp b/Engine/src/ECS/Scene.cpp index ad1b364..ace7570 100644 --- a/Engine/src/ECS/Scene.cpp +++ b/Engine/src/ECS/Scene.cpp @@ -17,21 +17,30 @@ namespace Engine::ECS { { Entity e = m_Registry.create(); // 出生即有:UUID(稳定身份)+ Transform(空间存在性,Unity 语义) - m_Registry.emplace(e, IdComponent{UUID::New()}); + const auto& id = m_Registry.emplace(e, IdComponent{UUID::New()}); m_Registry.emplace(e); m_Registry.emplace(e, NameComponent{std::move(name)}); + m_UUIDToEntity.emplace(id.uuid, e); return EntityHandle{this, e}; } Entity Scene::FindEntityByUUID(UUID uuid) { if (!uuid.Valid()) return entt::null; + const auto it = m_UUIDToEntity.find(uuid); + // 防御:实体可能已销毁(map 仍残留过时项),二次校验 valid + if (it == m_UUIDToEntity.end() || !m_Registry.valid(it->second)) { + return entt::null; + } + return it->second; + } - Entity found = entt::null; - m_Registry.view().each([&](Entity e, IdComponent& id) { - if (found == entt::null && id.uuid == uuid) found = e; - }); - return found; + void Scene::RebuildUuidIndex() { + m_UUIDToEntity.clear(); + m_Registry.view().each( + [&](Entity e, const IdComponent& id) { + m_UUIDToEntity.emplace(id.uuid, e); + }); } void Scene::DestroyEntity(Entity e) @@ -70,6 +79,12 @@ namespace Engine::ECS { std::remove(siblings.begin(), siblings.end(), e), siblings.end()); } + + // 销毁前先从 UUID 索引摘掉(销毁后实体不可访问) + if (const auto* id = m_Registry.try_get(e)) { + m_UUIDToEntity.erase(id->uuid); + } + m_Registry.destroy(e); // entt 自动回收槽位并递增版本号 } m_PendingDestroy.clear(); @@ -81,6 +96,7 @@ namespace Engine::ECS { //(registry.clear() 后队列里的句柄全部失效,直接丢弃即可) m_PendingDestroy.clear(); m_Registry.clear(); + m_UUIDToEntity.clear(); } EntityHandle Scene::GetEntityHandle(Entity e) diff --git a/Engine/src/Physics/PhysicsWorld.cpp b/Engine/src/Physics/PhysicsWorld.cpp index 735cc43..3b09e4f 100644 --- a/Engine/src/Physics/PhysicsWorld.cpp +++ b/Engine/src/Physics/PhysicsWorld.cpp @@ -343,7 +343,7 @@ namespace Engine { void PhysicsWorld::SyncScene() { auto ® = m_Scene.GetRegistry(); auto &bi = m_System->GetBodyInterface(); - std::vector invalidated; // 本帧销毁/重建 body → 约束失效传染 + std::unordered_set invalidated; // 本帧销毁/重建 body → 约束失效传染 // 1. 孤儿清理:实体销毁 / 组件被移除 → 移除 body for (auto it = m_Bodies.begin(); it != m_Bodies.end();) { @@ -353,7 +353,7 @@ namespace Engine { bi.RemoveBody(it->second.id); bi.DestroyBody(it->second.id); m_BodyToEntity.erase(it->second.id.GetIndexAndSequenceNumber()); - invalidated.push_back(it->first); + invalidated.insert(it->first); it = m_Bodies.erase(it); } else { ++it; @@ -373,7 +373,7 @@ namespace Engine { bi.DestroyBody(found->second.id); m_BodyToEntity.erase(found->second.id.GetIndexAndSequenceNumber()); m_Bodies.erase(found); - invalidated.push_back(bits); + invalidated.insert(bits); } rb.bodyValid = false; continue; @@ -385,7 +385,7 @@ namespace Engine { bi.DestroyBody(found->second.id); m_BodyToEntity.erase(found->second.id.GetIndexAndSequenceNumber()); m_Bodies.erase(found); - invalidated.push_back(bits); + invalidated.insert(bits); } CreateBody(bits, rb, tr); continue; @@ -407,7 +407,7 @@ namespace Engine { bi.DestroyBody(rec.id); m_BodyToEntity.erase(found->second.id.GetIndexAndSequenceNumber()); m_Bodies.erase(found); - invalidated.push_back(bits); + invalidated.insert(bits); rb.bodyValid = false; CreateBody(bits, rb, tr); continue; @@ -456,11 +456,10 @@ namespace Engine { // ---- 约束对账 ---- - void PhysicsWorld::SyncConstraints(const std::vector &invalidated) { + void PhysicsWorld::SyncConstraints(const std::unordered_set &invalidated) { auto ® = m_Scene.GetRegistry(); - const auto tainted = [&invalidated](u32 bits) { - return std::find(invalidated.begin(), invalidated.end(), bits) - != invalidated.end(); + const auto tainted = [&invalidated](const u32 bits) { + return invalidated.count(bits); }; // 1. owner 实体/组件没了(或被禁用),或任一端 body 本帧被销毁/重建 → 移除 @@ -901,6 +900,7 @@ namespace Engine { rb.bodyValid = true; } + void PhysicsWorld::RemoveBody(const u32 entityBits) { const auto found = m_Bodies.find(entityBits); if (found == m_Bodies.end()) return; diff --git a/Engine/src/Serialization/SceneSerializer.cpp b/Engine/src/Serialization/SceneSerializer.cpp index 83d8066..155687a 100644 --- a/Engine/src/Serialization/SceneSerializer.cpp +++ b/Engine/src/Serialization/SceneSerializer.cpp @@ -767,6 +767,10 @@ namespace Engine::Serialization { // 显式补算,否则首帧 World = 单位阵 scene.UpdateTransforms(); + // 序列化加载绕过了 CreateEntity 的 UUID 增量维护(565 行直接覆盖 + // IdComponent.uuid),全量重建一次索引,否则 FindEntityByUUID 会全失效 + scene.RebuildUuidIndex(); + ENGINE_INFO("SceneSerializer: loaded {} entities from '{}'", parsed.size(), sourceName); return true;