diff options
-rw-r--r-- | README.md | 14 | ||||
-rw-r--r-- | entityx/Components.h | 2 | ||||
-rw-r--r-- | entityx/Components_test.cc | 2 | ||||
-rw-r--r-- | entityx/Entity.h | 150 | ||||
-rw-r--r-- | entityx/Entity_test.cc | 67 | ||||
-rw-r--r-- | entityx/System_test.cc | 4 |
6 files changed, 147 insertions, 92 deletions
@@ -50,18 +50,18 @@ struct Direction : Component<Direction> { #### Assigning components to entities -To associate a component with a previously created entity call ``EntityManager::assign<C>()`` with the component type, the entity, and any component constructor arguments: +To associate a component with a previously created entity call ``Entity::assign<C>()`` with the component type, and any component constructor arguments: ```c++ // Assign a Position with x=1.0f and y=2.0f to "entity" -entities.assign<Position>(entity, 1.0f, 2.0f); +entity.assign<Position>(1.0f, 2.0f); ``` You can also assign existing instances of components: ```c++ boost::shared_ptr<Position> position = boost::make_shared<Position>(1.0f, 2.0f); -entities.assign(entity, position); +entity.assign(position); ``` #### Querying entities and their components @@ -76,10 +76,10 @@ for (auto entity : entities.entities_with_components(position, direction)) { } ``` -To retrieve a component associated with an entity use ``EntityManager::component()``: +To retrieve a component associated with an entity use ``Entity::component<C>()``: ```c++ -boost::shared_ptr<Position> position = entities.component<Position>(); +boost::shared_ptr<Position> position = entity.component<Position>(); if (position) { // Do stuff with position } @@ -186,8 +186,8 @@ class GameManager : public Manager { // Create some entities in random locations heading in random directions for (int i = 0; i < 100; ++i) { Entity entity = entity_manager.create(); - entity_manager.assign<Position>(entity, rand() % 100, rand() % 100); - entity_manager.assign<Direction>(entity, (rand() % 10) - 5, (rand() % 10) - 5); + entity.assign<Position>(rand() % 100, rand() % 100); + entity.assign<Direction>((rand() % 10) - 5, (rand() % 10) - 5); } } diff --git a/entityx/Components.h b/entityx/Components.h index cbc74d9..c963302 100644 --- a/entityx/Components.h +++ b/entityx/Components.h @@ -23,7 +23,7 @@ class TagsComponent : public Component<TagsComponent> { struct TagsPredicate { TagsPredicate(const std::string &tag) : tag(tag) {} - bool operator () (EntityManager &manager, Entity id) { + bool operator () (EntityManager &manager, Entity::Id id) { auto tags = manager.component<TagsComponent>(id); return tags != nullptr && tags->tags.find(tag) != tags->tags.end(); } diff --git a/entityx/Components_test.cc b/entityx/Components_test.cc index bb9ae5d..8fe05ec 100644 --- a/entityx/Components_test.cc +++ b/entityx/Components_test.cc @@ -40,7 +40,7 @@ TEST(TagsComponentTest, TestVariadicConstruction) { TEST(TagsComponentTest, TestEntitiesWithTag) { EventManager ev; EntityManager en(ev); - Entity a = en.create(); + Entity::Id a = en.create(); en.assign<Position>(a); for (int i = 0; i < 99; ++i) { auto e = en.create(); diff --git a/entityx/Entity.h b/entityx/Entity.h index b90abe7..e763214 100644 --- a/entityx/Entity.h +++ b/entityx/Entity.h @@ -27,13 +27,42 @@ namespace entityx { + +class EntityManager; + + /** - * Entity handle. + * A convenience handle around an Entity::Id. */ -typedef uint64_t Entity; +class Entity { + public: + typedef uint64_t Id; + Id id() const { return id_; } -class EntityManager; + operator Id () { return id_; } + + template <typename C> + boost::shared_ptr<C> assign(boost::shared_ptr<C> component); + template <typename C, typename ... Args> + boost::shared_ptr<C> assign(Args && ... args); + + template <typename C> + boost::shared_ptr<C> component(); + + template <typename A> + void unpack(boost::shared_ptr<A> &a); + template <typename A, typename B, typename ... Args> + void unpack(boost::shared_ptr<A> &a, boost::shared_ptr<B> &b, Args && ... args); + + private: + friend class EntityManager; + + Entity(EntityManager &entities, Entity::Id id) : entities_(entities), id_(id) {} + + EntityManager &entities_; + Entity::Id id_; +}; /** @@ -54,7 +83,7 @@ struct BaseComponent { * Component implementations should inherit from this. * * Components MUST provide a no-argument constructor. - * Components SHOULD provide convenience constructors for initializing on assignment to an Entity. + * Components SHOULD provide convenience constructors for initializing on assignment to an Entity::Id. * * This is a struct to imply that components should be data-only. * @@ -85,20 +114,20 @@ struct Component : public BaseComponent { * Emitted when an entity is added to the system. */ struct EntityCreatedEvent : public Event<EntityCreatedEvent> { - EntityCreatedEvent(EntityManager &manager, Entity entity) : + EntityCreatedEvent(EntityManager &manager, Entity::Id entity) : manager(manager), entity(entity) {} EntityManager &manager; - Entity entity; + Entity::Id entity; }; struct EntityDestroyedEvent : public Event<EntityDestroyedEvent> { - EntityDestroyedEvent(EntityManager &manager, Entity entity) : + EntityDestroyedEvent(EntityManager &manager, Entity::Id entity) : manager(manager), entity(entity) {} EntityManager &manager; - Entity entity; + Entity::Id entity; }; @@ -107,27 +136,27 @@ struct EntityDestroyedEvent : public Event<EntityDestroyedEvent> { */ template <typename T> struct ComponentAddedEvent : public Event<ComponentAddedEvent<T>> { - ComponentAddedEvent(EntityManager &manager, Entity entity, boost::shared_ptr<T> component) : + ComponentAddedEvent(EntityManager &manager, Entity::Id entity, boost::shared_ptr<T> component) : manager(manager), entity(entity), component(component) {} EntityManager &manager; - Entity entity; + Entity::Id entity; boost::shared_ptr<T> component; }; /** - * Manages Entity creation and component assignment. + * Manages Entity::Id creation and component assignment. * * eg. * EntityManager e; * * Entity player = e.create(); * - * e.assign<Movable>(player); - * e.assign<Physical>(player); - * e.assign<Scriptable>(player); - * shared_ptr<Controllable> controllable = e.assign<Controllable>(player); + * player.assign<Movable>(); + * player.assign<Physical>(); + * player.assign<Scriptable>(); + * shared_ptr<Controllable> controllable = player.assign<Controllable>(); */ class EntityManager : boost::noncopyable { private: @@ -138,14 +167,14 @@ class EntityManager : boost::noncopyable { class View { public: - typedef boost::function<bool (EntityManager &, Entity)> Predicate; + typedef boost::function<bool (EntityManager &, Entity::Id)> Predicate; /// A predicate that excludes entities that don't match the given component mask. class ComponentMaskPredicate { public: ComponentMaskPredicate(const std::vector<uint64_t> &entity_bits, uint64_t mask) : entity_bits_(entity_bits), mask_(mask) {} - bool operator () (EntityManager &, Entity entity) { + bool operator () (EntityManager &, Entity::Id entity) { return (entity_bits_.at(entity) & mask_) == mask_; } @@ -155,7 +184,7 @@ class EntityManager : boost::noncopyable { }; /// An iterator over a view of the entities in an EntityManager. - class Iterator : public std::iterator<std::input_iterator_tag, Entity> { + class Iterator : public std::iterator<std::input_iterator_tag, Entity::Id> { public: Iterator &operator ++ () { ++i_; @@ -164,14 +193,14 @@ class EntityManager : boost::noncopyable { } bool operator == (const Iterator& rhs) const { return i_ == rhs.i_; } bool operator != (const Iterator& rhs) const { return i_ != rhs.i_; } - Entity & operator * () { return i_; } - const Entity & operator * () const { return i_; } + Entity operator * () { return Entity(manager_, i_); } + const Entity operator * () const { return Entity(manager_, i_); } private: friend class View; Iterator(EntityManager &manager, const std::vector<Predicate> &predicates, - const std::vector<boost::function<void (Entity)>> &unpackers, Entity entity) + const std::vector<boost::function<void (Entity::Id)>> &unpackers, Entity::Id entity) : manager_(manager), predicates_(predicates), unpackers_(unpackers), i_(entity) { next(); } @@ -198,8 +227,8 @@ class EntityManager : boost::noncopyable { EntityManager &manager_; const std::vector<Predicate> predicates_; - std::vector<boost::function<void (Entity)>> unpackers_; - Entity i_; + std::vector<boost::function<void (Entity::Id)>> unpackers_; + Entity::Id i_; }; // Create a sub-view with an additional predicate. @@ -216,7 +245,7 @@ class EntityManager : boost::noncopyable { View &unpack_to(boost::shared_ptr<A> &a) { unpackers_.push_back(Unpacker<A>(manager_, a)); // This resulted in a segfault under clang 4.1 on OSX. No idea why. - /*unpackers_.push_back([&a, this](Entity id) { + /*unpackers_.push_back([&a, this](Entity::Id id) { a = manager_.component<A>(id); });*/ return *this; @@ -234,7 +263,7 @@ class EntityManager : boost::noncopyable { struct Unpacker { Unpacker(EntityManager &manager, boost::shared_ptr<T> &c) : manager(manager), c(c) {} - void operator () (Entity id) { + void operator () (Entity::Id id) { c = manager.component<T>(id); } @@ -249,7 +278,7 @@ class EntityManager : boost::noncopyable { EntityManager &manager_; std::vector<Predicate> predicates_; - std::vector<boost::function<void (Entity)>> unpackers_; + std::vector<boost::function<void (Entity::Id)>> unpackers_; }; /** @@ -258,12 +287,12 @@ class EntityManager : boost::noncopyable { size_t size() const { return entity_component_mask_.size(); } /** - * Create a new Entity. + * Create a new Entity::Id. * * Emits EntityCreatedEvent. */ Entity create() { - Entity id; + Entity::Id id; if (free_list_.empty()) { id = id_counter_++; accomodate_entity(id); @@ -271,16 +300,16 @@ class EntityManager : boost::noncopyable { id = *free_list_.erase(free_list_.begin()); } event_manager_.emit<EntityCreatedEvent>(*this, id); - return id; + return Entity(*this, id); } /** - * Destroy an existing Entity and its associated Components. + * Destroy an existing Entity::Id and its associated Components. * * Emits EntityDestroyedEvent. */ - void destroy(Entity entity) { - CHECK(entity < entity_component_mask_.size()) << "Entity ID outside entity vector range"; + void destroy(Entity::Id entity) { + CHECK(entity < entity_component_mask_.size()) << "Entity::Id ID outside entity vector range"; event_manager_.emit<EntityDestroyedEvent>(*this, entity); for (auto &components : entity_components_) { components.at(entity).reset(); @@ -290,9 +319,9 @@ class EntityManager : boost::noncopyable { } /** - * Check if an Entity is registered. + * Check if an Entity::Id is registered. */ - bool exists(Entity entity) { + bool exists(Entity::Id entity) { if (entity_component_mask_.empty() || entity >= id_counter_) { return false; } @@ -300,12 +329,12 @@ class EntityManager : boost::noncopyable { } /** - * Assigns a previously constructed Component to an Entity. + * Assigns a previously constructed Component to an Entity::Id. * * @returns component */ template <typename C> - boost::shared_ptr<C> assign(Entity entity, boost::shared_ptr<C> component) { + boost::shared_ptr<C> assign(Entity::Id entity, boost::shared_ptr<C> component) { boost::shared_ptr<BaseComponent> base = boost::static_pointer_cast<BaseComponent>(component); accomodate_component(C::family()); entity_components_.at(C::family()).at(entity) = base; @@ -316,24 +345,24 @@ class EntityManager : boost::noncopyable { } /** - * Assign a Component to an Entity, optionally passing through Component constructor arguments. + * Assign a Component to an Entity::Id, optionally passing through Component constructor arguments. * * shared_ptr<Position> position = em.assign<Position>(e, x, y); * * @returns Newly created component. */ template <typename C, typename ... Args> - boost::shared_ptr<C> assign(Entity entity, Args && ... args) { + boost::shared_ptr<C> assign(Entity::Id entity, Args && ... args) { return assign<C>(entity, boost::make_shared<C>(args ...)); } /** - * Retrieve a Component assigned to an Entity. + * Retrieve a Component assigned to an Entity::Id. * - * @returns Component instance, or empty shared_ptr<> if the Entity does not have that Component. + * @returns Component instance, or empty shared_ptr<> if the Entity::Id does not have that Component. */ template <typename C> - boost::shared_ptr<C> component(Entity id) { + boost::shared_ptr<C> component(Entity::Id id) { // We don't bother checking the component mask, as we return a nullptr anyway. if (C::family() >= entity_components_.size()) { return boost::shared_ptr<C>(); @@ -374,7 +403,7 @@ class EntityManager : boost::noncopyable { * unpack<Position, Direction>(e, p, d); */ template <typename A> - void unpack(Entity id, boost::shared_ptr<A> &a) { + void unpack(Entity::Id id, boost::shared_ptr<A> &a) { a = component<A>(id); } @@ -390,7 +419,7 @@ class EntityManager : boost::noncopyable { * unpack<Position, Direction>(e, p, d); */ template <typename A, typename B, typename ... Args> - void unpack(Entity id, boost::shared_ptr<A> &a, boost::shared_ptr<B> &b, Args && ... args) { + void unpack(Entity::Id id, boost::shared_ptr<A> &a, boost::shared_ptr<B> &b, Args && ... args) { unpack<A>(id, a); unpack<B, Args ...>(id, b, args ...); } @@ -416,7 +445,7 @@ class EntityManager : boost::noncopyable { return component_mask<C1>(c1) | component_mask<C2, Components ...>(c2, args...); } - inline void accomodate_entity(Entity entity) { + inline void accomodate_entity(Entity::Id entity) { if (entity_component_mask_.size() <= entity) { entity_component_mask_.resize(entity + 1); for (auto &components : entity_components_) { @@ -434,15 +463,40 @@ class EntityManager : boost::noncopyable { } } - Entity id_counter_ = 0; + Entity::Id id_counter_ = 0; EventManager &event_manager_; // A nested array of: components = entity_components_[family][entity] std::vector<EntitiesComponent> entity_components_; - // Bitmask of components associated with each entity. Index into the vector is the Entity. + // Bitmask of components associated with each entity. Index into the vector is the Entity::Id. std::vector<uint64_t> entity_component_mask_; - // List of available Entity IDs. - boost::unordered_set<Entity> free_list_; + // List of available Entity::Id IDs. + boost::unordered_set<Entity::Id> free_list_; }; +template <typename C> +boost::shared_ptr<C> Entity::assign(boost::shared_ptr<C> component) { + return entities_.assign<C>(id_, component); +} + +template <typename C, typename ... Args> +boost::shared_ptr<C> Entity::assign(Args && ... args) { + return entities_.assign<C>(id_, args ...); +} + +template <typename C> +boost::shared_ptr<C> Entity::component() { + return entities_.component<C>(id_); +} + +template <typename A> +void Entity::unpack(boost::shared_ptr<A> &a) { + entities_.unpack(id_, a); +} + +template <typename A, typename B, typename ... Args> +void Entity::unpack(boost::shared_ptr<A> &a, boost::shared_ptr<B> &b, Args && ... args) { + entities_.unpack(id_, a, b, args ...); +} + } diff --git a/entityx/Entity_test.cc b/entityx/Entity_test.cc index 1433b26..55da405 100644 --- a/entityx/Entity_test.cc +++ b/entityx/Entity_test.cc @@ -79,8 +79,9 @@ TEST_F(EntityManagerTest, TestCreateEntity) { TEST_F(EntityManagerTest, TestComponentConstruction) { auto e = em.create(); - auto p = em.assign<Position>(e, 1, 2); - auto cp = em.component<Position>(e); + auto p = e.assign<Position>(1, 2); + //auto p = em.assign<Position>(e, 1, 2); + auto cp = e.component<Position>(); ASSERT_EQ(p, cp); ASSERT_FLOAT_EQ(1.0, cp->x); ASSERT_FLOAT_EQ(2.0, cp->y); @@ -88,8 +89,8 @@ TEST_F(EntityManagerTest, TestComponentConstruction) { TEST_F(EntityManagerTest, TestComponentCreationWithObject) { auto e = em.create(); - auto p = em.assign(e, make_shared<Position>(1.0, 2.0)); - auto cp = em.component<Position>(e); + auto p = e.assign(make_shared<Position>(1.0, 2.0)); + auto cp = e.component<Position>(); ASSERT_EQ(p, cp); ASSERT_FLOAT_EQ(1.0, cp->x); ASSERT_FLOAT_EQ(2.0, cp->y); @@ -98,27 +99,27 @@ TEST_F(EntityManagerTest, TestComponentCreationWithObject) { TEST_F(EntityManagerTest, TestDestroyEntity) { Entity e = em.create(); Entity f = em.create(); - auto ep = em.assign<Position>(e); - em.assign<Position>(f); - em.assign<Direction>(e); - em.assign<Direction>(f); + auto ep = e.assign<Position>(); + f.assign<Position>(); + e.assign<Direction>(e); + f.assign<Direction>(); ASSERT_EQ(2, ep.use_count()); ASSERT_TRUE(em.exists(e)); ASSERT_TRUE(em.exists(f)); - ASSERT_TRUE(em.component<Position>(e)); - ASSERT_TRUE(em.component<Direction>(e)); - ASSERT_TRUE(em.component<Position>(f)); - ASSERT_TRUE(em.component<Direction>(f)); + ASSERT_TRUE(e.component<Position>()); + ASSERT_TRUE(e.component<Direction>()); + ASSERT_TRUE(f.component<Position>()); + ASSERT_TRUE(f.component<Direction>()); em.destroy(e); ASSERT_FALSE(em.exists(e)); ASSERT_TRUE(em.exists(f)); - ASSERT_FALSE(em.component<Position>(e)); - ASSERT_FALSE(em.component<Direction>(e)); - ASSERT_TRUE(em.component<Position>(f)); - ASSERT_TRUE(em.component<Direction>(f)); + ASSERT_FALSE(e.component<Position>()); + ASSERT_FALSE(e.component<Direction>()); + ASSERT_TRUE(f.component<Position>()); + ASSERT_TRUE(f.component<Direction>()); ASSERT_EQ(1, ep.use_count()); } @@ -126,23 +127,23 @@ TEST_F(EntityManagerTest, TestGetEntitiesWithComponent) { Entity e = em.create(); Entity f = em.create(); Entity g = em.create(); - em.assign<Position>(e); - em.assign<Direction>(e); - em.assign<Position>(f); - em.assign<Position>(g); + e.assign<Position>(); + e.assign<Direction>(); + f.assign<Position>(); + g.assign<Position>(); ASSERT_EQ(3, size(em.entities_with_components<Position>())); ASSERT_EQ(1, size(em.entities_with_components<Direction>())); } TEST_F(EntityManagerTest, TestGetEntitiesWithIntersectionOfComponents) { - vector<Entity> entities; + vector<Entity::Id> entities; for (int i = 0; i < 150; ++i) { Entity e = em.create(); entities.push_back(e); if (i % 2 == 0) - em.assign<Position>(e); + e.assign<Position>(); if (i % 3 == 0) - em.assign<Direction>(e); + e.assign<Direction>(); } ASSERT_EQ(50, size(em.entities_with_components<Direction>())); @@ -151,10 +152,10 @@ TEST_F(EntityManagerTest, TestGetEntitiesWithIntersectionOfComponents) { } TEST_F(EntityManagerTest, TestGetEntitiesWithComponentAndUnpacking) { - vector<Entity> entities; - Entity e = em.create(); - Entity f = em.create(); - Entity g = em.create(); + vector<Entity::Id> entities; + Entity::Id e = em.create(); + Entity::Id f = em.create(); + Entity::Id g = em.create(); std::vector<std::pair<shared_ptr<Position>, shared_ptr<Direction>>> position_directions; position_directions.push_back(std::make_pair( em.assign<Position>(e, 1.0f, 2.0f), @@ -180,7 +181,7 @@ TEST_F(EntityManagerTest, TestGetEntitiesWithComponentAndUnpacking) { } TEST_F(EntityManagerTest, TestUnpack) { - Entity e = em.create(); + Entity::Id e = em.create(); auto p = em.assign<Position>(e); auto d = em.assign<Direction>(e); @@ -192,7 +193,7 @@ TEST_F(EntityManagerTest, TestUnpack) { } TEST_F(EntityManagerTest, TestUnpackNullMissing) { - Entity e = em.create(); + Entity::Id e = em.create(); auto p = em.assign<Position>(e); struct NullDeleter {template<typename T> void operator()(T*) {} }; @@ -213,7 +214,7 @@ TEST_F(EntityManagerTest, TestEntityCreatedEvent) { created.push_back(event.entity); } - vector<Entity> created; + vector<Entity::Id> created; }; EntityCreatedEventReceiver receiver; @@ -232,14 +233,14 @@ TEST_F(EntityManagerTest, TestEntityDestroyedEvent) { destroyed.push_back(event.entity); } - vector<Entity> destroyed; + vector<Entity::Id> destroyed; }; EntityDestroyedEventReceiver receiver; ev.subscribe<EntityDestroyedEvent>(receiver); ASSERT_EQ(0, receiver.destroyed.size()); - vector<Entity> entities; + vector<Entity::Id> entities; for (int i = 0; i < 10; ++i) { entities.push_back(em.create()); } @@ -282,7 +283,7 @@ TEST_F(EntityManagerTest, TestComponentAddedEvent) { ASSERT_EQ(0, receiver.position_events); ASSERT_EQ(0, receiver.direction_events); for (int i = 0; i < 10; ++i) { - Entity e = em.create(); + Entity::Id e = em.create(); em.assign<Position>(e, float(i), float(i)); em.assign<Direction>(e, float(-i), float(-i)); } diff --git a/entityx/System_test.cc b/entityx/System_test.cc index 42312d3..63da01f 100644 --- a/entityx/System_test.cc +++ b/entityx/System_test.cc @@ -54,7 +54,7 @@ class MovementSystem : public System<MovementSystem> { class TestManager : public entityx::Manager { public: - std::vector<Entity> entities; + std::vector<Entity::Id> entities; SystemManager &sm() { return system_manager; } EntityManager &em() { return entity_manager; } @@ -65,7 +65,7 @@ class TestManager : public entityx::Manager { void initialize() override { for (int i = 0; i < 150; ++i) { - Entity e = entity_manager.create(); + Entity::Id e = entity_manager.create(); entities.push_back(e); if (i % 2 == 0) entity_manager.assign<Position>(e, 1, 2); |