/** * @file entities.hpp * @brief A simply-written entity component system. */ #ifndef ENTITIES_HPP_ #define ENTITIES_HPP_ #include <algorithm> // std::find_if #include <forward_list> #include <functional> #include <map> #include <type_traits> // std::is_convertible #include <vector> /** * @class Component * A base class for all components to inherit. */ class Component { /** * Constructs the class using the given XML element(s). */ virtual void fromXML(/*TODO*/) {} }; /** An ID number for entities. */ using Id = unsigned int; /** * @struct EntityData * Contains all components and an ID, to form an entity. */ struct EntityData { /** The entity's ID. */ Id id; /** A vector of all components. */ std::forward_list<size_t> componentHash; std::forward_list<Component*> components; /** Constructs an entity with the given ID. */ EntityData(Id _id = -1) : id (_id) {} /** Compares two entities through their IDs. */ bool operator==(const EntityData& e) { return id == e.id; } }; /** * @struct Entity * Allows access to an entity and it's components. * Note that this is not the actual entity's data, that is stored in the * EntityManager's EntityData array. */ struct Entity { std::vector<EntityData>::iterator pos; /** Constructs an entity object to handle the given entity data. */ Entity(std::vector<EntityData>::iterator p) : pos(p) {} Entity(EntityData& d) : pos(&d) {} /** * Assigns a component to the entity. * @param args arguments to pass to the component's constructor. * @return a pointer to the new component */ template<class T, typename... Args> T* assign(Args... args) { static_assert(std::is_convertible<T*, Component*>::value, "components must inherit Component base class"); auto comp = new T(args...); (*pos).components.push_front(comp); (*pos).componentHash.emplace_front(typeid(T).hash_code()); return comp; } /** * Removes a component of the given type from the entity. */ template<class T> void remove(void) { static_assert(std::is_convertible<T*, Component*>::value, "components must inherit Component base class"); auto it = (*pos).componentHash.begin(); auto ic = (*pos).components.before_begin(); for (; it != (*pos).componentHash.end(); ic++, it++) { if (*it == typeid(T).hash_code()) { (*pos).components.erase_after(ic); break; } } (*pos).componentHash.remove(typeid(T).hash_code()); } /** * Tests if the entity has a component of the given type. * @return true if the entity has the component */ template<class T> bool hasComponent(void) const { static_assert(std::is_convertible<T*, Component*>::value, "components must inherit Component base class"); return std::binary_search((*pos).componentHash.begin(), (*pos).componentHash.end(), typeid(T).hash_code()); } /** * Fetches a component from the entity. * @return the component, nullptr if the entity does not have it */ template<class T> T* component(void) { static_assert(std::is_convertible<T*, Component*>::value, "components must inherit Component base class"); auto it = (*pos).componentHash.begin(); auto ic = (*pos).components.begin(); for (; it != (*pos).componentHash.end(); ic++, it++) { if (*it == typeid(T).hash_code()) return dynamic_cast<T*>(*ic); } return nullptr; } }; /** * @class EntityManager * Manages a group of entities. */ class EntityManager { private: /** The array of all entities. */ std::vector<EntityData> entities; public: // max is not enforced EntityManager() /*: entities()*/ {} ~EntityManager(void) { entities.clear(); } /** * Creates a new entity. * @return an Entity object for the new entity */ Entity create(void) { static Id newId = 0; entities.emplace_back(newId); return Entity(entities.begin() + entities.size() - 1); } /** * Kills (removes) an entity. * @param e the entity to remove */ void kill(const Entity& e) { if (e.pos > entities.begin() && e.pos < entities.end()) entities.erase(e.pos); } /** * Destroys all entities. */ void reset(void) { entities.clear(); } /** * Runs a function through all entities. * @param f the function to run through */ void each(std::function<void(Entity e)> f) { for (auto i = entities.begin(); i < entities.end(); ++i) f(Entity(i)); } /** * Runs a function through all entities with the given components. * @param f the function to run through */ template<class T1> void each(std::function<void(Entity e)> f) { for (auto i = entities.begin(); i < entities.end(); ++i) { Entity en (i); if (en.hasComponent<T1>()) f(en); } } template<class T1, class T2> void each(std::function<void(Entity e)> f) { for (auto i = entities.begin(); i < entities.end(); ++i) { Entity en (i); if (en.hasComponent<T1>() && en.hasComponent<T2>()) f(en); } } }; using DeltaTime = int; class System { public: virtual void update(EntityManager& em, DeltaTime dt) = 0; }; class SystemManager { private: std::map<size_t, System*> systems; EntityManager& entities; public: SystemManager(EntityManager& em) : entities(em) {} template<class T, typename... Args> void add(Args... args) { static_assert(std::is_convertible<T*, System*>::value, "systems must inherit System base class"); systems.try_emplace(typeid(T).hash_code(), new T(args...)); } template<class T> void update(DeltaTime dt) { static_assert(std::is_convertible<T*, System*>::value, "systems must inherit System base class"); systems.at(typeid(T).hash_code())->update(entities, dt); } }; #endif // ENTITIES_HPP_