Remade Entities

Former-commit-id: 25f7bc84279fdf58b44cf78e2d94b4cbb78a8410
This commit is contained in:
Lynix 2015-02-19 22:06:57 +01:00
parent 1d79efeb7f
commit 3a18035989
8 changed files with 361 additions and 107 deletions

View File

@ -8,57 +8,50 @@
#define NDK_ENTITY_HPP #define NDK_ENTITY_HPP
#include <NDK/Prerequesites.hpp> #include <NDK/Prerequesites.hpp>
#include <set>
namespace Ndk namespace Ndk
{ {
class EntityHandle;
class World; class World;
class NDK_API Entity class NDK_API Entity
{ {
friend EntityHandle;
friend World; friend World;
public: public:
class Id; using Id = nzUInt32;
Entity(); Entity(const Entity&) = delete;
Entity(const Entity&) = default; Entity(Entity&& entity);
~Entity() = default; ~Entity();
void Kill(); EntityHandle CreateHandle();
Id GetId() const; Id GetId() const;
World* GetWorld() const; World* GetWorld() const;
void Kill();
bool IsValid() const; bool IsValid() const;
Entity& operator=(const Entity&) = default; Entity& operator=(const Entity&) = delete;
Entity& operator=(Entity&&) = delete;
bool operator==(const Entity& other) const;
bool operator!=(const Entity& other) const;
// Identifiant
struct Id
{
struct Part
{
nzUInt32 counter, index;
};
union
{
Part part;
nzUInt64 value;
};
bool operator==(const Id& other) const;
bool operator!=(const Id& other) const;
};
private: private:
Entity(Id id, World* world); Entity(World& world, Id id);
void Create();
void Destroy();
void RegisterHandle(EntityHandle* handle);
void UnregisterHandle(EntityHandle* handle);
std::set<EntityHandle*> m_handles;
Id m_id; Id m_id;
World* m_world; World* m_world;
bool m_valid;
}; };
} }

View File

@ -6,15 +6,9 @@
namespace Ndk namespace Ndk
{ {
inline Entity::Entity() : inline Entity::Entity(World& world, Id id) :
m_world(nullptr)
{
m_id.value = 0;
}
inline Entity::Entity(Id id, World* world) :
m_id(id), m_id(id),
m_world(world) m_world(&world)
{ {
} }
@ -28,23 +22,13 @@ namespace Ndk
return m_world; return m_world;
} }
inline bool Entity::operator==(const Entity& other) const inline void Entity::RegisterHandle(EntityHandle* handle)
{ {
return m_world == other.m_world && m_id == other.m_id; m_handles.insert(handle);
} }
inline bool Entity::operator!=(const Entity& other) const inline void Entity::UnregisterHandle(EntityHandle* handle)
{ {
return !operator==(other); m_handles.erase(handle);
}
inline bool Entity::Id::operator==(const Id& other) const
{
return value == other.value;
}
inline bool Entity::Id::operator!=(const Id& other) const
{
return !operator==(other);
} }
} }

View File

@ -0,0 +1,71 @@
// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequesites.hpp
#pragma once
#ifndef NDK_ENTITYHANDLE_HPP
#define NDK_ENTITYHANDLE_HPP
#include <NDK/Prerequesites.hpp>
#include <NDK/Entity.hpp>
#include <ostream>
namespace Ndk
{
class Entity;
class EntityHandle
{
friend Entity;
public:
EntityHandle();
explicit EntityHandle(Entity* entity);
EntityHandle(const EntityHandle& handle);
EntityHandle(EntityHandle&& handle);
~EntityHandle();
Entity* GetEntity() const;
bool IsValid() const;
void Reset(Entity* entity = nullptr);
void Reset(const EntityHandle& handle);
void Reset(EntityHandle&& handle);
EntityHandle& Swap(EntityHandle& handle);
operator bool() const;
operator Entity*() const;
Entity* operator->() const;
EntityHandle& operator=(Entity* entity);
EntityHandle& operator=(const EntityHandle& handle);
EntityHandle& operator=(EntityHandle&& handle);
friend std::ostream& operator<<(std::ostream& out, const EntityHandle& handle);
friend bool operator==(const EntityHandle& lhs, const EntityHandle& rhs);
friend bool operator!=(const EntityHandle& lhs, const EntityHandle& rhs);
friend bool operator<(const EntityHandle& lhs, const EntityHandle& rhs);
friend bool operator<=(const EntityHandle& lhs, const EntityHandle& rhs);
friend bool operator>(const EntityHandle& lhs, const EntityHandle& rhs);
friend bool operator>=(const EntityHandle& lhs, const EntityHandle& rhs);
private:
void OnEntityDestroyed();
void OnEntityMoved(Entity* newEntity);
Entity* m_entity;
};
}
namespace std
{
void swap(Ndk::EntityHandle& lhs, Ndk::EntityHandle& rhs);
}
#include <NDK/EntityHandle.inl>
#endif // NDK_ENTITYHANDLE_HPP

View File

@ -0,0 +1,177 @@
// Copyright (C) 2015 Jérôme Leclercq
// This file is part of the "Nazara Development Kit"
// For conditions of distribution and use, see copyright notice in Prerequesites.hpp
#include <functional>
namespace Ndk
{
inline EntityHandle::EntityHandle() :
m_entity(nullptr)
{
}
inline EntityHandle::EntityHandle(Entity* entity) :
EntityHandle()
{
Reset(entity);
}
inline EntityHandle::EntityHandle(const EntityHandle& handle) :
EntityHandle()
{
Reset(handle);
}
inline EntityHandle::EntityHandle(EntityHandle&& handle) :
EntityHandle()
{
Reset(handle);
}
inline EntityHandle::~EntityHandle()
{
Reset(nullptr);
}
inline Entity* EntityHandle::GetEntity() const
{
return m_entity;
}
inline bool EntityHandle::IsValid() const
{
return m_entity != nullptr;
}
inline void EntityHandle::Reset(Entity* entity)
{
// Si nous avions déjà une entité, nous devons l'informer que nous ne pointons plus vers elle
if (m_entity)
m_entity->UnregisterHandle(this);
m_entity = entity;
if (m_entity)
// On informe la nouvelle entité que nous pointons vers elle
m_entity->RegisterHandle(this);
}
inline void EntityHandle::Reset(const EntityHandle& handle)
{
Reset(handle.GetEntity());
}
inline void EntityHandle::Reset(EntityHandle&& handle)
{
Reset(handle.GetEntity());
}
inline EntityHandle& EntityHandle::Swap(EntityHandle& handle)
{
std::swap(m_entity, handle.m_entity);
}
inline EntityHandle::operator bool() const
{
return IsValid();
}
inline EntityHandle::operator Entity*() const
{
return m_entity;
}
inline Entity* EntityHandle::operator->() const
{
return m_entity;
}
inline EntityHandle& EntityHandle::operator=(Entity* entity)
{
Reset(entity);
}
inline EntityHandle& EntityHandle::operator=(const EntityHandle& handle)
{
Reset(handle);
}
inline EntityHandle& EntityHandle::operator=(EntityHandle&& handle)
{
Reset(handle);
}
inline void EntityHandle::OnEntityDestroyed()
{
// Un raccourci, un appel à Reset nous enlèverait de la liste des handles que nous ne pouvons pas modifier
// maintenant car elle est actuellement parcourue
m_entity = nullptr;
}
inline void EntityHandle::OnEntityMoved(Entity* newEntity)
{
// L'entité a été déplacée (peut arriver lors d'un changement de taille du conteneur du monde)
// nous mettons à jour notre pointeur
m_entity = newEntity;
}
inline std::ostream& operator<<(std::ostream& out, const EntityHandle& handle)
{
out << "EntityHandle(";
if (handle.IsValid())
out << "Entity(" << handle->GetId() << ")";
else
out << "Null entity";
out << ')';
return out;
}
inline bool operator==(const EntityHandle& lhs, const EntityHandle& rhs)
{
return lhs.m_entity == rhs.m_entity;
}
inline bool operator!=(const EntityHandle& lhs, const EntityHandle& rhs)
{
return !(lhs == rhs);
}
inline bool operator<(const EntityHandle& lhs, const EntityHandle& rhs)
{
return lhs.m_entity < rhs.m_entity;
}
inline bool operator<=(const EntityHandle& lhs, const EntityHandle& rhs)
{
return !(lhs > rhs);
}
inline bool operator>(const EntityHandle& lhs, const EntityHandle& rhs)
{
return rhs < lhs;
}
inline bool operator>=(const EntityHandle& lhs, const EntityHandle& rhs)
{
return !(lhs < rhs);
}
}
namespace std
{
template<>
struct hash<Ndk::EntityHandle>
{
size_t operator()(const Ndk::EntityHandle& handle) const
{
return hash<Ndk::Entity*>()(handle.GetEntity());
}
};
inline void swap(Ndk::EntityHandle& lhs, Ndk::EntityHandle& rhs)
{
lhs.Swap(rhs);
}
}

View File

@ -10,39 +10,41 @@
#include <Nazara/Core/NonCopyable.hpp> #include <Nazara/Core/NonCopyable.hpp>
#include <NDK/Prerequesites.hpp> #include <NDK/Prerequesites.hpp>
#include <NDK/Entity.hpp> #include <NDK/Entity.hpp>
#include <NDK/EntityHandle.hpp>
#include <vector> #include <vector>
namespace Ndk namespace Ndk
{ {
class EntityHandle;
class NDK_API World : NzNonCopyable class NDK_API World : NzNonCopyable
{ {
public: public:
using EntityList = std::vector<Entity>; using EntityList = std::vector<EntityHandle>;
World(); World() = default;
~World() = default; ~World();
Entity CreateEntity(); EntityHandle CreateEntity();
EntityList CreateEntities(unsigned int count); EntityList CreateEntities(unsigned int count);
void Clear(); void Clear();
void KillEntity(Entity& entity); void KillEntity(Entity* entity);
void KillEntities(EntityList& list); void KillEntities(const EntityList& list);
Entity GetEntity(Entity::Id id); Entity* GetEntity(Entity::Id id);
bool IsEntityValid(const Entity& entity) const; bool IsEntityValid(Entity* entity) const;
bool IsEntityIdValid(Entity::Id id) const; bool IsEntityIdValid(Entity::Id id) const;
void Update(); void Update();
private: private:
std::vector<nzUInt32> m_entitiesCounter;
std::vector<Entity::Id> m_freeIdList; std::vector<Entity::Id> m_freeIdList;
std::vector<Entity> m_entities;
EntityList m_aliveEntities; EntityList m_aliveEntities;
EntityList m_killedEntities; EntityList m_killedEntities;
nzUInt32 m_nextIndex;
}; };
} }

View File

@ -4,40 +4,31 @@
namespace Ndk namespace Ndk
{ {
inline World::World() :
m_nextIndex(0)
{
}
inline World::EntityList World::CreateEntities(unsigned int count) inline World::EntityList World::CreateEntities(unsigned int count)
{ {
EntityList list; EntityList list;
list.reserve(count); list.reserve(count);
for (unsigned int i = 0; i < count; ++i) for (unsigned int i = 0; i < count; ++i)
list.push_back(CreateEntity()); list.emplace_back(CreateEntity());
return list; return list;
} }
inline void World::KillEntities(EntityList& list) inline void World::KillEntities(const EntityList& list)
{ {
m_killedEntities.reserve(m_killedEntities.size() + list.size()); m_killedEntities.reserve(m_killedEntities.size() + list.size());
for (Entity& entity : list) for (const EntityHandle& entity : list)
KillEntity(entity); KillEntity(entity);
} }
inline bool World::IsEntityValid(const Entity& entity) const inline bool World::IsEntityValid(Entity* entity) const
{ {
///DOC: Cette méthode vérifie également l'appartenance de l'entité au monde (et est donc plus sûre) return entity != nullptr && entity->GetWorld() == this && IsEntityIdValid(entity->GetId());
return entity.GetWorld() == this && IsEntityIdValid(entity.GetId());
} }
inline bool World::IsEntityIdValid(Entity::Id id) const inline bool World::IsEntityIdValid(Entity::Id id) const
{ {
///DOC: Il est possible que si l'identifiant vienne d'un autre monde, il soit considéré valide return id < m_entities.size() && m_entities[id].IsValid();
/// alors qu'aucune entité de ce monde-ci ne l'utilise (encore)
return m_entitiesCounter[id.part.index] == id.part.counter;
} }
} }

View File

@ -1,17 +1,52 @@
// This file was automatically generated on 26 May 2014 at 01:05:31 // This file was automatically generated on 26 May 2014 at 01:05:31
#include <NDK/Entity.hpp> #include <NDK/Entity.hpp>
#include <NDK/EntityHandle.hpp>
#include <NDK/World.hpp> #include <NDK/World.hpp>
namespace Ndk namespace Ndk
{ {
Entity::Entity(Entity&& entity) :
m_handles(std::move(entity.m_handles)),
m_id(entity.m_id),
m_world(entity.m_world),
m_valid(entity.m_valid)
{
for (EntityHandle* handle : m_handles)
handle->OnEntityMoved(this);
}
Entity::~Entity()
{
Destroy();
}
EntityHandle Entity::CreateHandle()
{
return EntityHandle(this);
}
void Entity::Kill() void Entity::Kill()
{ {
m_world->KillEntity(*this); m_world->KillEntity(this);
} }
bool Entity::IsValid() const bool Entity::IsValid() const
{ {
return m_world != nullptr && m_world->IsEntityIdValid(m_id); return m_valid;
}
void Entity::Create()
{
m_valid = true;
}
void Entity::Destroy()
{
m_valid = false;
// On informe chaque handle de notre destruction pour éviter qu'il ne continue de pointer sur nous
for (EntityHandle* handle : m_handles)
handle->OnEntityDestroyed();
} }
} }

View File

@ -5,7 +5,13 @@
namespace Ndk namespace Ndk
{ {
Entity World::CreateEntity() World::~World()
{
// La destruction doit se faire dans un ordre précis
Clear();
}
EntityHandle World::CreateEntity()
{ {
Entity::Id id; Entity::Id id;
if (!m_freeIdList.empty()) if (!m_freeIdList.empty())
@ -16,19 +22,17 @@ namespace Ndk
} }
else else
{ {
// On alloue un nouvel identifiant // On alloue une nouvelle entité
m_entitiesCounter.resize(m_entitiesCounter.size() + 1); id = m_entities.size();
auto& counter = m_entitiesCounter.back(); // Impossible d'utiliser emplace_back à cause de la portée
counter = 1; m_entities.push_back(Entity(*this, id));
id.part.counter = counter;
id.part.index = m_nextIndex;
m_nextIndex++;
} }
Entity entity(id, this); 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); m_aliveEntities.push_back(entity);
return entity; return entity;
@ -36,33 +40,32 @@ namespace Ndk
void World::Clear() void World::Clear()
{ {
///DOC: Les handles existants avant Clear ne sont plus garantis de ne pas être réutilisés ///DOC: Tous les handles sont correctement invalidés
/// et devraient être détruits avant la création d'une nouvelle entité.
// Destruction des entités d'abord, et des handles ensuite
// ceci pour éviter que les handles n'informent les entités inutilement lors de leur destruction
m_entities.clear();
m_aliveEntities.clear(); m_aliveEntities.clear();
m_entitiesCounter.clear();
m_freeIdList.clear();
m_killedEntities.clear(); m_killedEntities.clear();
m_nextIndex = 0;
} }
void World::KillEntity(Entity& entity) void World::KillEntity(Entity* entity)
{ {
///DOC: Ignoré si l'entité est invalide ///DOC: Ignoré si l'entité est invalide
if (IsEntityValid(entity)) if (IsEntityValid(entity))
m_killedEntities.push_back(entity); m_killedEntities.emplace_back(entity);
} }
Entity World::GetEntity(Entity::Id id) Entity* World::GetEntity(Entity::Id id)
{ {
if (IsEntityIdValid(id)) if (IsEntityIdValid(id))
return Entity(id, this); return &m_entities[id];
else else
{ {
NazaraError("Invalid ID"); NazaraError("Invalid ID");
return Entity(); return nullptr;
} }
} }
@ -72,19 +75,17 @@ namespace Ndk
{ {
for (unsigned int i = 0; i < m_killedEntities.size(); ++i) for (unsigned int i = 0; i < m_killedEntities.size(); ++i)
{ {
Entity::Id e1 = m_aliveEntities[i].GetId(); EntityHandle entity = m_killedEntities[i];
for (unsigned int j = 0; j < m_aliveEntities.size(); ++j) for (unsigned int j = 0; j < m_aliveEntities.size(); ++j)
{ {
Entity::Id e2 = m_killedEntities[j].GetId(); if (entity == m_aliveEntities[j])
if (e1 == e2)
{ {
// Remise en file de l'identifiant d'entité // Remise en file d'attente de l'identifiant d'entité
nzUInt32& counter = m_entitiesCounter[e1.part.index]; m_freeIdList.push_back(entity->GetId());
counter++;
e1.part.counter = counter; // Destruction de l'entité (invalidation du handle par la même occasion)
m_freeIdList.push_back(e1); entity->Destroy();
// Suppression de l'entité des deux tableaux // Suppression de l'entité des deux tableaux
m_aliveEntities.erase(m_aliveEntities.begin() + j); m_aliveEntities.erase(m_aliveEntities.begin() + j);