ecsgame/ECS/ECS.h

1198 lines
29 KiB
C++

#pragma once
/*
Copyright (c) 2016 Sam Bloomberg
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <unordered_map>
#include <functional>
#include <vector>
#include <algorithm>
#include <stdint.h>
#include <type_traits>
//////////////////////////////////////////////////////////////////////////
// SETTINGS //
//////////////////////////////////////////////////////////////////////////
// Define what you want to pass to the tick() function by defining ECS_TICK_TYPE before including this header,
// or leave it as default (float).
// This is really messy to do but the alternative is some sort of slow custom event setup for ticks, which is silly.
// Add this before including this header if you don't want to pass anything to tick()
//#define ECS_TICK_TYPE_VOID
#ifndef ECS_TICK_TYPE
#define ECS_TICK_TYPE ECS::DefaultTickData
#endif
// Define what kind of allocator you want the world to use. It should have a default constructor.
#ifndef ECS_ALLOCATOR_TYPE
#define ECS_ALLOCATOR_TYPE std::allocator<ECS::Entity>
#endif
// Define ECS_TICK_NO_CLEANUP if you don't want the world to automatically cleanup dead entities
// at the beginning of each tick. This will require you to call cleanup() manually to prevent memory
// leaks.
//#define ECS_TICK_NO_CLEANUP
// Define ECS_NO_RTTI to turn off RTTI. This requires using the ECS_DEFINE_TYPE and ECS_DECLARE_TYPE macros on all types
// that you wish to use as components or events. If you use ECS_NO_RTTI, also place ECS_TYPE_IMPLEMENTATION in a single cpp file.
//#define ECS_NO_RTTI
#ifndef ECS_NO_RTTI
#include <typeindex>
#include <typeinfo>
#define ECS_TYPE_IMPLEMENTATION
#else
#define ECS_TYPE_IMPLEMENTATION \
ECS::TypeIndex ECS::Internal::TypeRegistry::nextIndex = 1; \
\
ECS_DEFINE_TYPE(ECS::Events::OnEntityCreated);\
ECS_DEFINE_TYPE(ECS::Events::OnEntityDestroyed); \
#endif
//////////////////////////////////////////////////////////////////////////
// CODE //
//////////////////////////////////////////////////////////////////////////
namespace ECS
{
#ifndef ECS_NO_RTTI
typedef std::type_index TypeIndex;
#define ECS_DECLARE_TYPE
#define ECS_DEFINE_TYPE(name)
template<typename T>
TypeIndex getTypeIndex()
{
return std::type_index(typeid(T));
}
#else
typedef uint32_t TypeIndex;
namespace Internal
{
class TypeRegistry
{
public:
TypeRegistry()
{
index = nextIndex;
++nextIndex;
}
TypeIndex getIndex() const
{
return index;
}
private:
static TypeIndex nextIndex;
TypeIndex index;
};
}
#define ECS_DECLARE_TYPE public: static ECS::Internal::TypeRegistry __ecs_type_reg
#define ECS_DEFINE_TYPE(name) ECS::Internal::TypeRegistry name::__ecs_type_reg
template<typename T>
TypeIndex getTypeIndex()
{
return T::__ecs_type_reg.getIndex();
}
#endif
class World;
class Entity;
typedef float DefaultTickData;
typedef ECS_ALLOCATOR_TYPE Allocator;
// Do not use anything in the Internal namespace yourself.
namespace Internal
{
template<typename... Types>
class EntityComponentView;
class EntityView;
struct BaseComponentContainer
{
public:
virtual ~BaseComponentContainer() { }
// This should only ever be called by the entity itself.
virtual void destroy(World* world) = 0;
// This will be called by the entity itself
virtual void removed(Entity* ent) = 0;
};
class BaseEventSubscriber
{
public:
virtual ~BaseEventSubscriber() {};
};
template<typename... Types>
class EntityComponentIterator
{
public:
EntityComponentIterator(World* world, size_t index, bool bIsEnd, bool bIncludePendingDestroy);
size_t getIndex() const
{
return index;
}
bool isEnd() const;
bool includePendingDestroy() const
{
return bIncludePendingDestroy;
}
World* getWorld() const
{
return world;
}
Entity* get() const;
Entity* operator*() const
{
return get();
}
bool operator==(const EntityComponentIterator<Types...>& other) const
{
if (world != other.world)
return false;
if (isEnd())
return other.isEnd();
return index == other.index;
}
bool operator!=(const EntityComponentIterator<Types...>& other) const
{
if (world != other.world)
return true;
if (isEnd())
return !other.isEnd();
return index != other.index;
}
EntityComponentIterator<Types...>& operator++();
private:
bool bIsEnd = false;
size_t index;
class ECS::World* world;
bool bIncludePendingDestroy;
};
template<typename... Types>
class EntityComponentView
{
public:
EntityComponentView(const EntityComponentIterator<Types...>& first, const EntityComponentIterator<Types...>& last);
const EntityComponentIterator<Types...>& begin() const
{
return firstItr;
}
const EntityComponentIterator<Types...>& end() const
{
return lastItr;
}
private:
EntityComponentIterator<Types...> firstItr;
EntityComponentIterator<Types...> lastItr;
};
}
/**
* Think of this as a pointer to a component. Whenever you get a component from the world or an entity,
* it'll be wrapped in a ComponentHandle.
*/
template<typename T>
class ComponentHandle
{
public:
ComponentHandle()
: component(nullptr)
{
}
ComponentHandle(T* component)
: component(component)
{
}
T* operator->() const
{
return component;
}
operator bool() const
{
return isValid();
}
T& get()
{
return *component;
}
bool isValid() const
{
return component != nullptr;
}
private:
T* component;
};
/**
* A system that acts on entities. Generally, this will act on a subset of entities using World::each().
*
* Systems often will respond to events by subclassing EventSubscriber. You may use configure() to subscribe to events,
* but remember to unsubscribe in unconfigure().
*/
class EntitySystem
{
public:
virtual ~EntitySystem() {}
/**
* Called when this system is added to a world.
*/
virtual void configure(World* world)
{
}
/**
* Called when this system is being removed from a world.
*/
virtual void unconfigure(World* world)
{
}
/**
* Called when World::tick() is called. See ECS_TICK_TYPE at the top of this file for more
* information about passing data to tick.
*/
#ifdef ECS_TICK_TYPE_VOID
virtual void tick(World* world)
#else
virtual void tick(World* world, ECS_TICK_TYPE data)
#endif
{
}
};
/**
* Subclass this as EventSubscriber<EventType> and then call World::subscribe() in order to subscribe to events. Make sure
* to call World::unsubscribe() or World::unsubscribeAll() when your subscriber is deleted!
*/
template<typename T>
class EventSubscriber : public Internal::BaseEventSubscriber
{
public:
virtual ~EventSubscriber() {}
/**
* Called when an event is emitted by the world.
*/
virtual void receive(World* world, const T& event) = 0;
};
namespace Events
{
// Called when a new entity is created.
struct OnEntityCreated
{
ECS_DECLARE_TYPE;
Entity* entity;
};
// Called when an entity is about to be destroyed.
struct OnEntityDestroyed
{
ECS_DECLARE_TYPE;
Entity* entity;
};
// Called when a component is assigned (not necessarily created).
template<typename T>
struct OnComponentAssigned
{
ECS_DECLARE_TYPE;
Entity* entity;
ComponentHandle<T> component;
};
// Called when a component is removed
template<typename T>
struct OnComponentRemoved
{
ECS_DECLARE_TYPE;
Entity* entity;
ComponentHandle<T> component;
};
#ifdef ECS_NO_RTTI
template<typename T>
ECS_DEFINE_TYPE(ECS::Events::OnComponentAssigned<T>);
template<typename T>
ECS_DEFINE_TYPE(ECS::Events::OnComponentRemoved<T>);
#endif
}
/**
* A container for components. Entities do not have any logic of their own, except of that which to manage
* components. Components themselves are generally structs that contain data with which EntitySystems can
* act upon, but technically any data type may be used as a component, though only one of each data type
* may be on a single Entity at a time.
*/
class Entity
{
public:
friend class World;
const static size_t InvalidEntityId = 0;
// Do not create entities yourself, use World::create().
Entity(World* world, size_t id)
: world(world), id(id)
{
}
// Do not delete entities yourself, use World::destroy().
~Entity()
{
removeAll();
}
/**
* Get the world associated with this entity.
*/
World* getWorld() const
{
return world;
}
/**
* Does this entity have a component?
*/
template<typename T>
bool has() const
{
auto index = getTypeIndex<T>();
return components.find(index) != components.end();
}
/**
* Does this entity have this list of components? The order of components does not matter.
*/
template<typename T, typename V, typename... Types>
bool has() const
{
return has<T>() && has<V, Types...>();
}
/**
* Assign a new component (or replace an old one). All components must have a default constructor, though they
* may have additional constructors. You may pass arguments to this function the same way you would to a constructor.
*
* It is recommended that components be simple types (not const, not references, not pointers). If you need to store
* any of the above, wrap it in a struct.
*/
template<typename T, typename... Args>
ComponentHandle<T> assign(Args&&... args);
/**
* Remove a component of a specific type. Returns whether a component was removed.
*/
template<typename T>
bool remove()
{
auto found = components.find(getTypeIndex<T>());
if (found != components.end())
{
found->second->removed(this);
found->second->destroy(world);
components.erase(found);
return true;
}
return false;
}
/**
* Remove all components from this entity.
*/
void removeAll()
{
for (auto pair : components)
{
pair.second->removed(this);
pair.second->destroy(world);
}
components.clear();
}
/**
* Get a component from this entity.
*/
template<typename T>
ComponentHandle<T> get();
/**
* Call a function with components from this entity as arguments. This will return true if this entity has
* all specified components attached, and false if otherwise.
*/
template<typename... Types>
bool with(typename std::common_type<std::function<void(ComponentHandle<Types>...)>>::type view)
{
if (!has<Types...>())
return false;
view(get<Types>()...); // variadic template expansion is fun
return true;
}
/**
* Get this entity's id. Entity ids aren't too useful at the moment, but can be used to tell the difference between entities when debugging.
*/
size_t getEntityId() const
{
return id;
}
bool isPendingDestroy() const
{
return bPendingDestroy;
}
private:
std::unordered_map<TypeIndex, Internal::BaseComponentContainer*> components;
World* world;
size_t id;
bool bPendingDestroy = false;
};
/**
* The world creates, destroys, and manages entities. The lifetime of entities and _registered_ systems are handled by the world
* (don't delete a system without unregistering it from the world first!), while event subscribers have their own lifetimes
* (the world doesn't delete them automatically when the world is deleted).
*/
class World
{
public:
using WorldAllocator = std::allocator_traits<Allocator>::template rebind_alloc<World>;
using EntityAllocator = std::allocator_traits<Allocator>::template rebind_alloc<Entity>;
using SystemAllocator = std::allocator_traits<Allocator>::template rebind_alloc<EntitySystem>;
using EntityPtrAllocator = std::allocator_traits<Allocator>::template rebind_alloc<Entity*>;
using SystemPtrAllocator = std::allocator_traits<Allocator>::template rebind_alloc<EntitySystem*>;
using SubscriberPtrAllocator = std::allocator_traits<Allocator>::template rebind_alloc<Internal::BaseEventSubscriber*>;
using SubscriberPairAllocator = std::allocator_traits<Allocator>::template rebind_alloc<std::pair<const TypeIndex, std::vector<Internal::BaseEventSubscriber*, SubscriberPtrAllocator>>>;
/**
* Use this function to construct the world with a custom allocator.
*/
static World* createWorld(Allocator alloc)
{
WorldAllocator worldAlloc(alloc);
World* world = std::allocator_traits<WorldAllocator>::allocate(worldAlloc, 1);
std::allocator_traits<WorldAllocator>::construct(worldAlloc, world, alloc);
return world;
}
/**
* Use this function to construct the world with the default allocator.
*/
static World* createWorld()
{
return createWorld(Allocator());
}
// Use this to destroy the world instead of calling delete.
// This will emit OnEntityDestroyed events and call EntitySystem::unconfigure as appropriate.
void destroyWorld()
{
WorldAllocator alloc(entAlloc);
std::allocator_traits<WorldAllocator>::destroy(alloc, this);
std::allocator_traits<WorldAllocator>::deallocate(alloc, this, 1);
}
World(Allocator alloc)
: entAlloc(alloc), systemAlloc(alloc),
entities({}, EntityPtrAllocator(alloc)),
systems({}, SystemPtrAllocator(alloc)),
subscribers({}, 0, std::hash<TypeIndex>(), std::equal_to<TypeIndex>(), SubscriberPtrAllocator(alloc))
{
}
/**
* Destroying the world will emit OnEntityDestroyed events and call EntitySystem::unconfigure() as appropriate.
*
* Use World::destroyWorld to destroy and deallocate the world, do not manually delete the world!
*/
~World();
/**
* Create a new entity. This will emit the OnEntityCreated event.
*/
Entity* create()
{
++lastEntityId;
Entity* ent = std::allocator_traits<EntityAllocator>::allocate(entAlloc, 1);
std::allocator_traits<EntityAllocator>::construct(entAlloc, ent, this, lastEntityId);
entities.push_back(ent);
emit<Events::OnEntityCreated>({ ent });
return ent;
}
/**
* Destroy an entity. This will emit the OnEntityDestroy event.
*
* If immediate is false (recommended), then the entity won't be immediately
* deleted but instead will be removed at the beginning of the next tick() or
* when cleanup() is called. OnEntityDestroyed will still be called immediately.
*
* This function is safe to call multiple times on a single entity. Note that calling
* this once with immediate = false and then calling it with immediate = true will
* remove the entity from the pending destroy queue and will immediately destroy it
* _without_ emitting a second OnEntityDestroyed event.
*
* A warning: Do not set immediate to true if you are currently iterating through entities!
*/
void destroy(Entity* ent, bool immediate = false);
/**
* Delete all entities in the pending destroy queue. Returns true if any entities were cleaned up,
* false if there were no entities to clean up.
*/
bool cleanup();
/**
* Reset the world, destroying all entities. Entity ids will be reset as well.
*/
void reset();
/**
* Register a system. The world will manage the memory of the system unless you unregister the system.
*/
EntitySystem* registerSystem(EntitySystem* system)
{
systems.push_back(system);
system->configure(this);
return system;
}
/**
* Unregister a system.
*/
void unregisterSystem(EntitySystem* system)
{
systems.erase(std::remove(systems.begin(), systems.end(), system), systems.end());
system->unconfigure(this);
}
void enableSystem(EntitySystem* system)
{
auto it = std::find(disabledSystems.begin(), disabledSystems.end(), system);
if (it != disabledSystems.end())
{
disabledSystems.erase(it);
systems.push_back(system);
}
}
void disableSystem(EntitySystem* system)
{
auto it = std::find(systems.begin(), systems.end(), system);
if (it != systems.end())
{
systems.erase(it);
disabledSystems.push_back(system);
}
}
/**
* Subscribe to an event.
*/
template<typename T>
void subscribe(EventSubscriber<T>* subscriber)
{
auto index = getTypeIndex<T>();
auto found = subscribers.find(index);
if (found == subscribers.end())
{
std::vector<Internal::BaseEventSubscriber*, SubscriberPtrAllocator> subList(entAlloc);
subList.push_back(subscriber);
subscribers.insert({ index, subList });
}
else
{
found->second.push_back(subscriber);
}
}
/**
* Unsubscribe from an event.
*/
template<typename T>
void unsubscribe(EventSubscriber<T>* subscriber)
{
auto index = getTypeIndex<T>();
auto found = subscribers.find(index);
if (found != subscribers.end())
{
found->second.erase(std::remove(found->second.begin(), found->second.end(), subscriber), found->second.end());
if (found->second.size() == 0)
{
subscribers.erase(found);
}
}
}
/**
* Unsubscribe from all events. Don't be afraid of the void pointer, just pass in your subscriber as normal.
*/
void unsubscribeAll(void* subscriber)
{
for (auto kv : subscribers)
{
kv.second.erase(std::remove(kv.second.begin(), kv.second.end(), subscriber), kv.second.end());
if (kv.second.size() == 0)
{
subscribers.erase(kv.first);
}
}
}
/**
* Emit an event. This will do nothing if there are no subscribers for the event type.
*/
template<typename T>
void emit(const T& event)
{
auto found = subscribers.find(getTypeIndex<T>());
if (found != subscribers.end())
{
for (auto* base : found->second)
{
auto* sub = reinterpret_cast<EventSubscriber<T>*>(base);
sub->receive(this, event);
}
}
}
/**
* Run a function on each entity with a specific set of components. This is useful for implementing an EntitySystem.
*
* If you want to include entities that are pending destruction, set includePendingDestroy to true.
*/
template<typename... Types>
void each(typename std::common_type<std::function<void(Entity*, ComponentHandle<Types>...)>>::type viewFunc, bool bIncludePendingDestroy = false);
/**
* Run a function on all entities.
*/
void all(std::function<void(Entity*)> viewFunc, bool bIncludePendingDestroy = false);
/**
* Get a view for entities with a specific set of components. The list of entities is calculated on the fly, so this method itself
* has little overhead. This is mostly useful with a range based for loop.
*/
template<typename... Types>
Internal::EntityComponentView<Types...> each(bool bIncludePendingDestroy = false)
{
Internal::EntityComponentIterator<Types...> first(this, 0, false, bIncludePendingDestroy);
Internal::EntityComponentIterator<Types...> last(this, getCount(), true, bIncludePendingDestroy);
return Internal::EntityComponentView<Types...>(first, last);
}
Internal::EntityView all(bool bIncludePendingDestroy = false);
size_t getCount() const
{
return entities.size();
}
Entity* getByIndex(size_t idx)
{
if (idx >= getCount())
return nullptr;
return entities[idx];
}
/**
* Get an entity by an id. This is a slow process.
*/
Entity* getById(size_t id) const;
/**
* Tick the world. See the definition for ECS_TICK_TYPE at the top of this file for more information on
* passing data through tick().
*/
#ifdef ECS_TICK_TYPE_VOID
void tick()
#else
void tick(ECS_TICK_TYPE data)
#endif
{
#ifndef ECS_TICK_NO_CLEANUP
cleanup();
#endif
for (auto* system : systems)
{
#ifdef ECS_TICK_TYPE_VOID
system->tick(this);
#else
system->tick(this, data);
#endif
}
}
EntityAllocator& getPrimaryAllocator()
{
return entAlloc;
}
private:
EntityAllocator entAlloc;
SystemAllocator systemAlloc;
std::vector<Entity*, EntityPtrAllocator> entities;
std::vector<EntitySystem*, SystemPtrAllocator> systems;
std::vector<EntitySystem*> disabledSystems;
std::unordered_map<TypeIndex,
std::vector<Internal::BaseEventSubscriber*, SubscriberPtrAllocator>,
std::hash<TypeIndex>,
std::equal_to<TypeIndex>,
SubscriberPairAllocator> subscribers;
size_t lastEntityId = 0;
};
namespace Internal
{
class EntityIterator
{
public:
EntityIterator(World* world, size_t index, bool bIsEnd, bool bIncludePendingDestroy);
size_t getIndex() const
{
return index;
}
bool isEnd() const;
bool includePendingDestroy() const
{
return bIncludePendingDestroy;
}
World* getWorld() const
{
return world;
}
Entity* get() const;
Entity* operator*() const
{
return get();
}
bool operator==(const EntityIterator& other) const
{
if (world != other.world)
return false;
if (isEnd())
return other.isEnd();
return index == other.index;
}
bool operator!=(const EntityIterator& other) const
{
if (world != other.world)
return true;
if (isEnd())
return !other.isEnd();
return index != other.index;
}
EntityIterator& operator++();
private:
bool bIsEnd = false;
size_t index;
class ECS::World* world;
bool bIncludePendingDestroy;
};
class EntityView
{
public:
EntityView(const EntityIterator& first, const EntityIterator& last)
: firstItr(first), lastItr(last)
{
if (firstItr.get() == nullptr || (firstItr.get()->isPendingDestroy() && !firstItr.includePendingDestroy()))
{
++firstItr;
}
}
const EntityIterator& begin() const
{
return firstItr;
}
const EntityIterator& end() const
{
return lastItr;
}
private:
EntityIterator firstItr;
EntityIterator lastItr;
};
template<typename T>
struct ComponentContainer : public BaseComponentContainer
{
ComponentContainer() {}
ComponentContainer(const T& data) : data(data) {}
T data;
protected:
virtual void destroy(World* world)
{
using ComponentAllocator = std::allocator_traits<World::EntityAllocator>::template rebind_alloc<ComponentContainer<T>>;
ComponentAllocator alloc(world->getPrimaryAllocator());
std::allocator_traits<ComponentAllocator>::destroy(alloc, this);
std::allocator_traits<ComponentAllocator>::deallocate(alloc, this, 1);
}
virtual void removed(Entity* ent)
{
auto handle = ComponentHandle<T>(&data);
ent->getWorld()->emit<Events::OnComponentRemoved<T>>({ ent, handle });
}
};
}
inline World::~World()
{
for (auto* system : systems)
{
system->unconfigure(this);
}
for (auto* ent : entities)
{
if (!ent->isPendingDestroy())
{
ent->bPendingDestroy = true;
emit<Events::OnEntityDestroyed>({ ent });
}
std::allocator_traits<EntityAllocator>::destroy(entAlloc, ent);
std::allocator_traits<EntityAllocator>::deallocate(entAlloc, ent, 1);
}
for (auto* system : systems)
{
std::allocator_traits<SystemAllocator>::destroy(systemAlloc, system);
std::allocator_traits<SystemAllocator>::deallocate(systemAlloc, system, 1);
}
}
inline void World::destroy(Entity* ent, bool immediate)
{
if (ent == nullptr)
return;
if (ent->isPendingDestroy())
{
if (immediate)
{
entities.erase(std::remove(entities.begin(), entities.end(), ent), entities.end());
std::allocator_traits<EntityAllocator>::destroy(entAlloc, ent);
std::allocator_traits<EntityAllocator>::deallocate(entAlloc, ent, 1);
}
return;
}
ent->bPendingDestroy = true;
emit<Events::OnEntityDestroyed>({ ent });
if (immediate)
{
entities.erase(std::remove(entities.begin(), entities.end(), ent), entities.end());
std::allocator_traits<EntityAllocator>::destroy(entAlloc, ent);
std::allocator_traits<EntityAllocator>::deallocate(entAlloc, ent, 1);
}
}
inline bool World::cleanup()
{
size_t count = 0;
entities.erase(std::remove_if(entities.begin(), entities.end(), [&, this](Entity* ent) {
if (ent->isPendingDestroy())
{
std::allocator_traits<EntityAllocator>::destroy(entAlloc, ent);
std::allocator_traits<EntityAllocator>::deallocate(entAlloc, ent, 1);
++count;
return true;
}
return false;
}), entities.end());
return count > 0;
}
inline void World::reset()
{
for (auto* ent : entities)
{
if (!ent->isPendingDestroy())
{
ent->bPendingDestroy = true;
emit<Events::OnEntityDestroyed>({ ent });
}
std::allocator_traits<EntityAllocator>::destroy(entAlloc, ent);
std::allocator_traits<EntityAllocator>::deallocate(entAlloc, ent, 1);
}
entities.clear();
lastEntityId = 0;
}
inline void World::all(std::function<void(Entity*)> viewFunc, bool bIncludePendingDestroy)
{
for (auto* ent : all(bIncludePendingDestroy))
{
viewFunc(ent);
}
}
inline Internal::EntityView World::all(bool bIncludePendingDestroy)
{
Internal::EntityIterator first(this, 0, false, bIncludePendingDestroy);
Internal::EntityIterator last(this, getCount(), true, bIncludePendingDestroy);
return Internal::EntityView(first, last);
}
inline Entity* World::getById(size_t id) const
{
if (id == Entity::InvalidEntityId || id > lastEntityId)
return nullptr;
// We should likely store entities in a map of id -> entity so that this is faster.
for (auto* ent : entities)
{
if (ent->getEntityId() == id)
return ent;
}
return nullptr;
}
template<typename... Types>
void World::each(typename std::common_type<std::function<void(Entity*, ComponentHandle<Types>...)>>::type viewFunc, bool bIncludePendingDestroy)
{
for (auto* ent : each<Types...>(bIncludePendingDestroy))
{
viewFunc(ent, ent->template get<Types>()...);
}
}
template<typename T, typename... Args>
ComponentHandle<T> Entity::assign(Args&&... args)
{
using ComponentAllocator = std::allocator_traits<World::EntityAllocator>::template rebind_alloc<Internal::ComponentContainer<T>>;
auto found = components.find(getTypeIndex<T>());
if (found != components.end())
{
Internal::ComponentContainer<T>* container = reinterpret_cast<Internal::ComponentContainer<T>*>(found->second);
container->data = T(args...);
auto handle = ComponentHandle<T>(&container->data);
world->emit<Events::OnComponentAssigned<T>>({ this, handle });
return handle;
}
else
{
ComponentAllocator alloc(world->getPrimaryAllocator());
Internal::ComponentContainer<T>* container = std::allocator_traits<ComponentAllocator>::allocate(alloc, 1);
std::allocator_traits<ComponentAllocator>::construct(alloc, container, T(args...));
components.insert({ getTypeIndex<T>(), container });
auto handle = ComponentHandle<T>(&container->data);
world->emit<Events::OnComponentAssigned<T>>({ this, handle });
return handle;
}
}
template<typename T>
ComponentHandle<T> Entity::get()
{
auto found = components.find(getTypeIndex<T>());
if (found != components.end())
{
return ComponentHandle<T>(&reinterpret_cast<Internal::ComponentContainer<T>*>(found->second)->data);
}
return ComponentHandle<T>();
}
namespace Internal
{
inline EntityIterator::EntityIterator(class World* world, size_t index, bool bIsEnd, bool bIncludePendingDestroy)
: bIsEnd(bIsEnd), index(index), world(world), bIncludePendingDestroy(bIncludePendingDestroy)
{
if (index >= world->getCount())
this->bIsEnd = true;
}
inline bool EntityIterator::isEnd() const
{
return bIsEnd || index >= world->getCount();
}
inline Entity* EntityIterator::get() const
{
if (isEnd())
return nullptr;
return world->getByIndex(index);
}
inline EntityIterator& EntityIterator::operator++()
{
++index;
while (index < world->getCount() && (get() == nullptr || (get()->isPendingDestroy() && !bIncludePendingDestroy)))
{
++index;
}
if (index >= world->getCount())
bIsEnd = true;
return *this;
}
template<typename... Types>
EntityComponentIterator<Types...>::EntityComponentIterator(World* world, size_t index, bool bIsEnd, bool bIncludePendingDestroy)
: bIsEnd(bIsEnd), index(index), world(world), bIncludePendingDestroy(bIncludePendingDestroy)
{
if (index >= world->getCount())
this->bIsEnd = true;
}
template<typename... Types>
bool EntityComponentIterator<Types...>::isEnd() const
{
return bIsEnd || index >= world->getCount();
}
template<typename... Types>
Entity* EntityComponentIterator<Types...>::get() const
{
if (isEnd())
return nullptr;
return world->getByIndex(index);
}
template<typename... Types>
EntityComponentIterator<Types...>& EntityComponentIterator<Types...>::operator++()
{
++index;
while (index < world->getCount() && (get() == nullptr || !get()->template has<Types...>() || (get()->isPendingDestroy() && !bIncludePendingDestroy)))
{
++index;
}
if (index >= world->getCount())
bIsEnd = true;
return *this;
}
template<typename... Types>
EntityComponentView<Types...>::EntityComponentView(const EntityComponentIterator<Types...>& first, const EntityComponentIterator<Types...>& last)
: firstItr(first), lastItr(last)
{
if (firstItr.get() == nullptr || (firstItr.get()->isPendingDestroy() && !firstItr.includePendingDestroy())
|| !firstItr.get()->template has<Types...>())
{
++firstItr;
}
}
}
}