From 3694857d30f6d410adca1f45180e685513c7d83c Mon Sep 17 00:00:00 2001 From: Lynix Date: Mon, 16 Mar 2015 22:14:18 +0100 Subject: [PATCH] (World) Optimized entities handling a lot Former-commit-id: f05f3bb7bf321d30fd51f504ace95aa0ea9f7f8d --- SDK/include/NDK/World.hpp | 21 ++++++++++--- SDK/include/NDK/World.inl | 5 +-- SDK/src/NDK/World.cpp | 65 +++++++++++++++++++++------------------ 3 files changed, 55 insertions(+), 36 deletions(-) diff --git a/SDK/include/NDK/World.hpp b/SDK/include/NDK/World.hpp index cb208adc9..a7715754d 100644 --- a/SDK/include/NDK/World.hpp +++ b/SDK/include/NDK/World.hpp @@ -7,9 +7,11 @@ #ifndef NDK_WORLD_HPP #define NDK_WORLD_HPP +#include #include #include #include +#include #include namespace Ndk @@ -27,21 +29,32 @@ namespace Ndk void Clear(); + EntityHandle GetEntity(Entity::Id id); + void KillEntity(const EntityHandle& entity); void KillEntities(const EntityList& list); - EntityHandle GetEntity(Entity::Id id); - bool IsEntityValid(const EntityHandle& entity) const; bool IsEntityIdValid(Entity::Id id) const; void Update(); private: + struct EntityBlock + { + EntityBlock(Entity&& e) : + entity(std::move(e)) + { + } + + Entity entity; + unsigned int aliveIndex; + }; + std::vector m_freeIdList; - std::vector m_entities; + std::vector m_entities; EntityList m_aliveEntities; - EntityList m_killedEntities; + NzBitset m_killedEntities; }; } diff --git a/SDK/include/NDK/World.inl b/SDK/include/NDK/World.inl index 0f275872a..c252f5e57 100644 --- a/SDK/include/NDK/World.inl +++ b/SDK/include/NDK/World.inl @@ -2,6 +2,8 @@ // This file is part of the "Nazara Development Kit" // For conditions of distribution and use, see copyright notice in Prerequesites.hpp +#include + namespace Ndk { inline World::EntityList World::CreateEntities(unsigned int count) @@ -17,7 +19,6 @@ namespace Ndk inline void World::KillEntities(const EntityList& list) { - m_killedEntities.reserve(m_killedEntities.size() + list.size()); for (const EntityHandle& entity : list) KillEntity(entity); } @@ -29,6 +30,6 @@ namespace Ndk inline bool World::IsEntityIdValid(Entity::Id id) const { - return id < m_entities.size() && m_entities[id].IsValid(); + return id < m_entities.size() && m_entities[id].entity.IsValid(); } } diff --git a/SDK/src/NDK/World.cpp b/SDK/src/NDK/World.cpp index 14befe738..10256a785 100644 --- a/SDK/src/NDK/World.cpp +++ b/SDK/src/NDK/World.cpp @@ -31,13 +31,15 @@ namespace Ndk m_entities.push_back(Entity(*this, id)); } - EntityHandle entity = m_entities[id].CreateHandle(); - // On initialise l'entité et on l'ajoute à la liste des entités vivantes - entity->Create(); - m_aliveEntities.push_back(entity); + Entity& entity = m_entities[id].entity; + entity.Create(); - return entity; + EntityHandle handle = entity.CreateHandle(); + m_aliveEntities.push_back(handle); + m_entities[id].aliveIndex = m_aliveEntities.size()-1; + + return handle; } void World::Clear() @@ -49,7 +51,7 @@ namespace Ndk m_entities.clear(); m_aliveEntities.clear(); - m_killedEntities.clear(); + m_killedEntities.Clear(); } void World::KillEntity(const EntityHandle& entity) @@ -57,13 +59,13 @@ namespace Ndk ///DOC: Ignoré si l'entité est invalide if (IsEntityValid(entity)) - m_killedEntities.emplace_back(entity); + m_killedEntities.UnboundedSet(entity->GetId(), true); } EntityHandle World::GetEntity(Entity::Id id) { if (IsEntityIdValid(id)) - return m_entities[id].CreateHandle(); + return m_aliveEntities[m_entities[id].aliveIndex]; else { NazaraError("Invalid ID"); @@ -73,33 +75,36 @@ namespace Ndk void World::Update() { - if (!m_killedEntities.empty()) + for (unsigned int i = m_killedEntities.FindFirst(); i != m_killedEntities.npos; i = m_killedEntities.FindNext(i)) { - for (unsigned int i = 0; i < m_killedEntities.size(); ++i) + EntityBlock& block = m_entities[i]; + Entity& entity = block.entity; + + NazaraAssert(entity.IsValid(), "Entity must be valid"); + + // Remise en file d'attente de l'identifiant d'entité + m_freeIdList.push_back(entity.GetId()); + + // Destruction de l'entité (invalidation du handle par la même occasion) + entity.Destroy(); + + // Nous allons sortir le handle de la liste des entités vivantes + // en swappant le handle avec le dernier handle, avant de pop + + NazaraAssert(block.aliveIndex < m_aliveEntities.size(), "Alive index out of range"); + + if (block.aliveIndex < m_aliveEntities.size()-1) // S'il ne s'agit pas du dernier handle { - const EntityHandle& entity = m_killedEntities[i]; + EntityHandle& lastHandle = m_aliveEntities.back(); + EntityHandle& myHandle = m_aliveEntities[block.aliveIndex]; - for (unsigned int j = 0; j < m_aliveEntities.size(); ++j) - { - if (entity == m_aliveEntities[j]) - { - // Remise en file d'attente de l'identifiant d'entité - m_freeIdList.push_back(entity->GetId()); + myHandle = std::move(lastHandle); - // Destruction de l'entité (invalidation du handle par la même occasion) - entity->Destroy(); - - // Suppression de l'entité des deux tableaux - m_aliveEntities.erase(m_aliveEntities.begin() + j); - m_killedEntities.erase(m_killedEntities.begin() + i); - - // Correction des indices (pour ne pas sauter une case) - i--; - j--; - break; - } - } + // On n'oublie pas de corriger l'indice associé à l'entité + m_entities[myHandle->GetId()].aliveIndex = block.aliveIndex; } + m_aliveEntities.pop_back(); } + m_killedEntities.Reset(); } }