(World) Optimized entities handling a lot

Former-commit-id: f05f3bb7bf321d30fd51f504ace95aa0ea9f7f8d
This commit is contained in:
Lynix 2015-03-16 22:14:18 +01:00
parent b30298b6ab
commit 3694857d30
3 changed files with 55 additions and 36 deletions

View File

@ -7,9 +7,11 @@
#ifndef NDK_WORLD_HPP #ifndef NDK_WORLD_HPP
#define NDK_WORLD_HPP #define NDK_WORLD_HPP
#include <Nazara/Core/Bitset.hpp>
#include <Nazara/Core/NonCopyable.hpp> #include <Nazara/Core/NonCopyable.hpp>
#include <NDK/Entity.hpp> #include <NDK/Entity.hpp>
#include <NDK/EntityHandle.hpp> #include <NDK/EntityHandle.hpp>
#include <algorithm>
#include <vector> #include <vector>
namespace Ndk namespace Ndk
@ -27,21 +29,32 @@ namespace Ndk
void Clear(); void Clear();
EntityHandle GetEntity(Entity::Id id);
void KillEntity(const EntityHandle& entity); void KillEntity(const EntityHandle& entity);
void KillEntities(const EntityList& list); void KillEntities(const EntityList& list);
EntityHandle GetEntity(Entity::Id id);
bool IsEntityValid(const EntityHandle& entity) const; bool IsEntityValid(const EntityHandle& entity) const;
bool IsEntityIdValid(Entity::Id id) const; bool IsEntityIdValid(Entity::Id id) const;
void Update(); void Update();
private: private:
struct EntityBlock
{
EntityBlock(Entity&& e) :
entity(std::move(e))
{
}
Entity entity;
unsigned int aliveIndex;
};
std::vector<Entity::Id> m_freeIdList; std::vector<Entity::Id> m_freeIdList;
std::vector<Entity> m_entities; std::vector<EntityBlock> m_entities;
EntityList m_aliveEntities; EntityList m_aliveEntities;
EntityList m_killedEntities; NzBitset<nzUInt64> m_killedEntities;
}; };
} }

View File

@ -2,6 +2,8 @@
// This file is part of the "Nazara Development Kit" // This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequesites.hpp // For conditions of distribution and use, see copyright notice in Prerequesites.hpp
#include <Nazara/Core/Error.hpp>
namespace Ndk namespace Ndk
{ {
inline World::EntityList World::CreateEntities(unsigned int count) inline World::EntityList World::CreateEntities(unsigned int count)
@ -17,7 +19,6 @@ namespace Ndk
inline void World::KillEntities(const EntityList& list) inline void World::KillEntities(const EntityList& list)
{ {
m_killedEntities.reserve(m_killedEntities.size() + list.size());
for (const EntityHandle& entity : list) for (const EntityHandle& entity : list)
KillEntity(entity); KillEntity(entity);
} }
@ -29,6 +30,6 @@ namespace Ndk
inline bool World::IsEntityIdValid(Entity::Id id) const 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();
} }
} }

View File

@ -31,13 +31,15 @@ namespace Ndk
m_entities.push_back(Entity(*this, id)); 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 // On initialise l'entité et on l'ajoute à la liste des entités vivantes
entity->Create(); Entity& entity = m_entities[id].entity;
m_aliveEntities.push_back(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() void World::Clear()
@ -49,7 +51,7 @@ namespace Ndk
m_entities.clear(); m_entities.clear();
m_aliveEntities.clear(); m_aliveEntities.clear();
m_killedEntities.clear(); m_killedEntities.Clear();
} }
void World::KillEntity(const EntityHandle& entity) void World::KillEntity(const EntityHandle& entity)
@ -57,13 +59,13 @@ namespace Ndk
///DOC: Ignoré si l'entité est invalide ///DOC: Ignoré si l'entité est invalide
if (IsEntityValid(entity)) if (IsEntityValid(entity))
m_killedEntities.emplace_back(entity); m_killedEntities.UnboundedSet(entity->GetId(), true);
} }
EntityHandle World::GetEntity(Entity::Id id) EntityHandle World::GetEntity(Entity::Id id)
{ {
if (IsEntityIdValid(id)) if (IsEntityIdValid(id))
return m_entities[id].CreateHandle(); return m_aliveEntities[m_entities[id].aliveIndex];
else else
{ {
NazaraError("Invalid ID"); NazaraError("Invalid ID");
@ -73,33 +75,36 @@ namespace Ndk
void World::Update() 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) myHandle = std::move(lastHandle);
{
if (entity == m_aliveEntities[j])
{
// 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) // On n'oublie pas de corriger l'indice associé à l'entité
entity->Destroy(); m_entities[myHandle->GetId()].aliveIndex = block.aliveIndex;
// 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;
}
}
} }
m_aliveEntities.pop_back();
} }
m_killedEntities.Reset();
} }
} }