From b462654ab6d1345b0d256ccddd74794f311f8c7b Mon Sep 17 00:00:00 2001 From: Alec Thomas Date: Wed, 24 Oct 2012 19:54:30 -0400 Subject: Add Entity class helper. This largely supplants the use of entity IDs. --- README.md | 14 ++--- entityx/Components.h | 2 +- entityx/Components_test.cc | 2 +- entityx/Entity.h | 150 ++++++++++++++++++++++++++++++--------------- entityx/Entity_test.cc | 67 ++++++++++---------- entityx/System_test.cc | 4 +- 6 files changed, 147 insertions(+), 92 deletions(-) diff --git a/README.md b/README.md index 0ca2918..611dc98 100644 --- a/README.md +++ b/README.md @@ -50,18 +50,18 @@ struct Direction : Component { #### Assigning components to entities -To associate a component with a previously created entity call ``EntityManager::assign()`` with the component type, the entity, and any component constructor arguments: +To associate a component with a previously created entity call ``Entity::assign()`` 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(entity, 1.0f, 2.0f); +entity.assign(1.0f, 2.0f); ``` You can also assign existing instances of components: ```c++ boost::shared_ptr position = boost::make_shared(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++ -boost::shared_ptr position = entities.component(); +boost::shared_ptr position = entity.component(); 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(entity, rand() % 100, rand() % 100); - entity_manager.assign(entity, (rand() % 10) - 5, (rand() % 10) - 5); + entity.assign(rand() % 100, rand() % 100); + entity.assign((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 { 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(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(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 + boost::shared_ptr assign(boost::shared_ptr component); + template + boost::shared_ptr assign(Args && ... args); + + template + boost::shared_ptr component(); + + template + void unpack(boost::shared_ptr &a); + template + void unpack(boost::shared_ptr &a, boost::shared_ptr &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(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(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 { */ template struct ComponentAddedEvent : public Event> { - ComponentAddedEvent(EntityManager &manager, Entity entity, boost::shared_ptr component) : + ComponentAddedEvent(EntityManager &manager, Entity::Id entity, boost::shared_ptr component) : manager(manager), entity(entity), component(component) {} EntityManager &manager; - Entity entity; + Entity::Id entity; boost::shared_ptr component; }; /** - * Manages Entity creation and component assignment. + * Manages Entity::Id creation and component assignment. * * eg. * EntityManager e; * * Entity player = e.create(); * - * e.assign(player); - * e.assign(player); - * e.assign(player); - * shared_ptr controllable = e.assign(player); + * player.assign(); + * player.assign(); + * player.assign(); + * shared_ptr controllable = player.assign(); */ class EntityManager : boost::noncopyable { private: @@ -138,14 +167,14 @@ class EntityManager : boost::noncopyable { class View { public: - typedef boost::function Predicate; + typedef boost::function Predicate; /// A predicate that excludes entities that don't match the given component mask. class ComponentMaskPredicate { public: ComponentMaskPredicate(const std::vector &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 { + class Iterator : public std::iterator { 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 &predicates, - const std::vector> &unpackers, Entity entity) + const std::vector> &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 predicates_; - std::vector> unpackers_; - Entity i_; + std::vector> 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) { unpackers_.push_back(Unpacker(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(id); });*/ return *this; @@ -234,7 +263,7 @@ class EntityManager : boost::noncopyable { struct Unpacker { Unpacker(EntityManager &manager, boost::shared_ptr &c) : manager(manager), c(c) {} - void operator () (Entity id) { + void operator () (Entity::Id id) { c = manager.component(id); } @@ -249,7 +278,7 @@ class EntityManager : boost::noncopyable { EntityManager &manager_; std::vector predicates_; - std::vector> unpackers_; + std::vector> 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(*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(*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 - boost::shared_ptr assign(Entity entity, boost::shared_ptr component) { + boost::shared_ptr assign(Entity::Id entity, boost::shared_ptr component) { boost::shared_ptr base = boost::static_pointer_cast(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 = em.assign(e, x, y); * * @returns Newly created component. */ template - boost::shared_ptr assign(Entity entity, Args && ... args) { + boost::shared_ptr assign(Entity::Id entity, Args && ... args) { return assign(entity, boost::make_shared(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 - boost::shared_ptr component(Entity id) { + boost::shared_ptr 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(); @@ -374,7 +403,7 @@ class EntityManager : boost::noncopyable { * unpack(e, p, d); */ template - void unpack(Entity id, boost::shared_ptr &a) { + void unpack(Entity::Id id, boost::shared_ptr &a) { a = component(id); } @@ -390,7 +419,7 @@ class EntityManager : boost::noncopyable { * unpack(e, p, d); */ template - void unpack(Entity id, boost::shared_ptr &a, boost::shared_ptr &b, Args && ... args) { + void unpack(Entity::Id id, boost::shared_ptr &a, boost::shared_ptr &b, Args && ... args) { unpack(id, a); unpack(id, b, args ...); } @@ -416,7 +445,7 @@ class EntityManager : boost::noncopyable { return component_mask(c1) | component_mask(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 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 entity_component_mask_; - // List of available Entity IDs. - boost::unordered_set free_list_; + // List of available Entity::Id IDs. + boost::unordered_set free_list_; }; +template +boost::shared_ptr Entity::assign(boost::shared_ptr component) { + return entities_.assign(id_, component); +} + +template +boost::shared_ptr Entity::assign(Args && ... args) { + return entities_.assign(id_, args ...); +} + +template +boost::shared_ptr Entity::component() { + return entities_.component(id_); +} + +template +void Entity::unpack(boost::shared_ptr &a) { + entities_.unpack(id_, a); +} + +template +void Entity::unpack(boost::shared_ptr &a, boost::shared_ptr &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(e, 1, 2); - auto cp = em.component(e); + auto p = e.assign(1, 2); + //auto p = em.assign(e, 1, 2); + auto cp = e.component(); 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(1.0, 2.0)); - auto cp = em.component(e); + auto p = e.assign(make_shared(1.0, 2.0)); + auto cp = e.component(); 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(e); - em.assign(f); - em.assign(e); - em.assign(f); + auto ep = e.assign(); + f.assign(); + e.assign(e); + f.assign(); ASSERT_EQ(2, ep.use_count()); ASSERT_TRUE(em.exists(e)); ASSERT_TRUE(em.exists(f)); - ASSERT_TRUE(em.component(e)); - ASSERT_TRUE(em.component(e)); - ASSERT_TRUE(em.component(f)); - ASSERT_TRUE(em.component(f)); + ASSERT_TRUE(e.component()); + ASSERT_TRUE(e.component()); + ASSERT_TRUE(f.component()); + ASSERT_TRUE(f.component()); em.destroy(e); ASSERT_FALSE(em.exists(e)); ASSERT_TRUE(em.exists(f)); - ASSERT_FALSE(em.component(e)); - ASSERT_FALSE(em.component(e)); - ASSERT_TRUE(em.component(f)); - ASSERT_TRUE(em.component(f)); + ASSERT_FALSE(e.component()); + ASSERT_FALSE(e.component()); + ASSERT_TRUE(f.component()); + ASSERT_TRUE(f.component()); 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(e); - em.assign(e); - em.assign(f); - em.assign(g); + e.assign(); + e.assign(); + f.assign(); + g.assign(); ASSERT_EQ(3, size(em.entities_with_components())); ASSERT_EQ(1, size(em.entities_with_components())); } TEST_F(EntityManagerTest, TestGetEntitiesWithIntersectionOfComponents) { - vector entities; + vector entities; for (int i = 0; i < 150; ++i) { Entity e = em.create(); entities.push_back(e); if (i % 2 == 0) - em.assign(e); + e.assign(); if (i % 3 == 0) - em.assign(e); + e.assign(); } ASSERT_EQ(50, size(em.entities_with_components())); @@ -151,10 +152,10 @@ TEST_F(EntityManagerTest, TestGetEntitiesWithIntersectionOfComponents) { } TEST_F(EntityManagerTest, TestGetEntitiesWithComponentAndUnpacking) { - vector entities; - Entity e = em.create(); - Entity f = em.create(); - Entity g = em.create(); + vector entities; + Entity::Id e = em.create(); + Entity::Id f = em.create(); + Entity::Id g = em.create(); std::vector, shared_ptr>> position_directions; position_directions.push_back(std::make_pair( em.assign(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(e); auto d = em.assign(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(e); struct NullDeleter {template void operator()(T*) {} }; @@ -213,7 +214,7 @@ TEST_F(EntityManagerTest, TestEntityCreatedEvent) { created.push_back(event.entity); } - vector created; + vector created; }; EntityCreatedEventReceiver receiver; @@ -232,14 +233,14 @@ TEST_F(EntityManagerTest, TestEntityDestroyedEvent) { destroyed.push_back(event.entity); } - vector destroyed; + vector destroyed; }; EntityDestroyedEventReceiver receiver; ev.subscribe(receiver); ASSERT_EQ(0, receiver.destroyed.size()); - vector entities; + vector 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(e, float(i), float(i)); em.assign(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 { class TestManager : public entityx::Manager { public: - std::vector entities; + std::vector 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(e, 1, 2); -- cgit v1.2.3