diff --git a/CMakeLists.txt b/CMakeLists.txt index f996eb9..1b6efb6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -122,7 +122,7 @@ endif() # Things to install set(install_libs entityx) -set(sources entityx/tags/TagsComponent.cc entityx/deps/Dependencies.cc entityx/System.cc entityx/Event.cc entityx/Entity.cc entityx/Manager.cc entityx/help/Timer.cc) +set(sources entityx/System.cc entityx/Event.cc entityx/Entity.cc entityx/help/Timer.cc) add_library(entityx STATIC ${sources}) if (ENTITYX_BUILD_SHARED) diff --git a/README.md b/README.md index ee1fe5e..3bb4547 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ You can also contact me directly via [email](mailto:alec@swapoff.org) or [Twitte ## Recent Notable Changes +- 2014-03-02 - (1.0.0alpha1) Switch to using cache friendly component storage (big breaking change). Also largely eradicated use of `std::shared_ptr`. - 2014-02-13 - Visual C++ support thanks to [Jarrett Chisholm](https://github.com/jarrettchisholm)! - 2013-10-29 - Boost has been removed as a primary dependency for builds not using python. - 2013-08-21 - Remove dependency on `boost::signal` and switch to embedded [Simple::Signal](http://timj.testbit.eu/2013/cpp11-signal-system-performance/). @@ -58,10 +59,11 @@ An `entityx::Entity` is a convenience class wrapping an opaque `uint64_t` value Creating an entity is as simple as: ```c++ -entityx::ptr events(new entityx::EventManager()); -entityx::ptr entities(new entityx::EntityManager(event)); +#include -entityx::Entity entity = entities->create(); +EntityX entityx; + +entityx::Entity entity = entityx.entities.create(); ``` And destroying an entity is done with: @@ -75,13 +77,13 @@ entity.destroy(); - Each `entityx::Entity` is a convenience class wrapping an `entityx::Entity::Id`. - An `entityx::Entity` handle can be invalidated with `invalidate()`. This does not affect the underlying entity. - When an entity is destroyed the manager adds its ID to a free list and invalidates the `entityx::Entity` handle. -- When an entity is created IDs are recycled from the free list before allocating new ones. +- When an entity is created IDs are recycled from the free list first, before allocating new ones. - An `entityx::Entity` ID contains an index and a version. When an entity is destroyed, the version associated with the index is incremented, invalidating all previous entities referencing the previous ID. -- EntityX uses a reference counting smart pointer`entityx::ptr` to manage object lifetimes. As a general rule, passing a pointer to any EntityX method will convert to a smart pointer and take ownership. To maintain your own reference to the pointer you will need to wrap it in `entityx::ptr`. +- To improve cache coherence, components are constructed in contiguous memory ranges by using `entityx::EntityManager::assign(id, ...)`. A light weight smart pointer (`ComponentPtr`) is used to access the component. ### Components (entity data) -The general idea with the EntityX interpretation of ECS is to have as little functionality in components as possible. All logic should be contained in Systems. +The general idea with the EntityX interpretation of ECS is to have as little logic in components as possible. All logic should be contained in Systems. To that end Components are typically [POD types](http://en.wikipedia.org/wiki/Plain_Old_Data_Structures) consisting of self-contained sets of related data. Implementations are [curiously recurring template pattern](http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern) (CRTP) subclasses of `entityx::Component`. @@ -112,21 +114,14 @@ To associate a component with a previously created entity call ``entityx::Entity entity.assign(1.0f, 2.0f); ``` -You can also assign existing instances of components: - -```c++ -entityx::ptr position(new Position(1.0f, 2.0f)); -entity.assign(position); -``` - #### Querying entities and their components To query all entities with a set of components assigned, use ``entityx::EntityManager::entities_with_components()``. This method will return only those entities that have *all* of the specified components associated with them, assigning each component pointer to the corresponding component instance: ```c++ -for (auto entity : entities->entities_with_components()) { - entityx::ptr position = entity.component(); - entityx::ptr direction = entity.component(); +for (Entity entity : entities.entities_with_components()) { + ComponentPtr position = entity.component(); + ComponentPtr direction = entity.component(); // Do things with entity, position and direction. } @@ -135,7 +130,7 @@ for (auto entity : entities->entities_with_components()) { To retrieve a component associated with an entity use ``entityx::Entity::component()``: ```c++ -entityx::ptr position = entity.component(); +ComponentPtr position = entity.component(); if (position) { // Do stuff with position } @@ -166,10 +161,10 @@ A basic movement system might be implemented with something like the following: ```c++ struct MovementSystem : public System { - void update(entityx::ptr es, entityx::ptr events, double dt) override { - for (auto entity : es->entities_with_components()) { - entityx::ptr position = entity.component(); - entityx::ptr direction = entity.component(); + void update(entityx::EntityManager &es, entityx::EventManager &events, double dt) override { + for (Entity entity : es.entities_with_components()) { + ComponentPtr position = entity.component(); + ComponentPtr direction = entity.component(); position->x += direction->x * dt; position->y += direction->y * dt; @@ -204,10 +199,10 @@ Next we implement our collision system, which emits ``Collision`` objects via an ```c++ class CollisionSystem : public System { public: - void update(entityx::ptr es, entityx::ptr events, double dt) override { - entityx::ptr left_position, right_position; - for (auto left_entity : es->entities_with_components(left_position)) { - for (auto right_entity : es->entities_with_components(right_position)) { + void update(entityx::EntityManager &es, entityx::EventManager &events, double dt) override { + ComponentPtr left_position, right_position; + for (Entity left_entity : es.entities_with_components(left_position)) { + for (auto right_entity : es.entities_with_components(right_position)) { if (collide(left_position, right_position)) { events->emit(left_entity, right_entity); } @@ -223,11 +218,11 @@ Objects interested in receiving collision information can subscribe to ``Collisi ```c++ struct DebugSystem : public System, Receiver { - void configure(entityx::ptr event_manager) { - event_manager->subscribe(*this); + void configure(entityx::EventManager &event_manager) { + event_manager.subscribe(*this); } - void update(ptr entities, ptr events, double dt) {} + void update(entityx::EntityManager &entities, entityx::EventManager &events, double dt) {} void receive(const Collision &collision) { LOG(DEBUG) << "entities collided: " << collision.left << " and " << collision.right << endl; @@ -245,10 +240,10 @@ Several events are emitted by EntityX itself: - `entityx::Entity entity` - entityx::Entity about to be destroyed. - `ComponentAddedEvent` - emitted when a new component is added to an entity. - `entityx::Entity entity` - entityx::Entity that component was added to. - - `entityx::ptr component` - The component added. + - `ComponentPtr component` - The component added. - `ComponentRemovedEvent` - emitted when a component is removed from an entity. - `entityx::Entity entity` - entityx::Entity that component was removed from. - - `entityx::ptr component` - The component removed. + - `ComponentPtr component` - The component removed. #### Implementation notes diff --git a/entityx/Benchmarks_test.cc b/entityx/Benchmarks_test.cc index 2790ae0..084b7bd 100644 --- a/entityx/Benchmarks_test.cc +++ b/entityx/Benchmarks_test.cc @@ -19,10 +19,10 @@ struct AutoTimer { class BenchmarksTest : public ::testing::Test { protected: - BenchmarksTest() : ev(EventManager::make()), em(EntityManager::make(ev)) {} + BenchmarksTest() : em(ev) {} - ptr ev; - ptr em; + EventManager ev; + EntityManager em; }; @@ -33,7 +33,7 @@ TEST_F(BenchmarksTest, TestCreateEntities) { cout << "creating " << count << " entities" << endl; for (uint64_t i = 0; i < count; i++) { - em->create(); + em.create(); } } @@ -42,7 +42,7 @@ TEST_F(BenchmarksTest, TestDestroyEntities) { uint64_t count = 10000000L; vector entities; for (uint64_t i = 0; i < count; i++) { - entities.push_back(em->create()); + entities.push_back(em.create()); } AutoTimer t; @@ -60,7 +60,7 @@ struct Listener : public Receiver { TEST_F(BenchmarksTest, TestCreateEntitiesWithListener) { Listener listen; - ev->subscribe(listen); + ev.subscribe(listen); uint64_t count = 10000000L; @@ -69,18 +69,18 @@ TEST_F(BenchmarksTest, TestCreateEntitiesWithListener) { vector entities; for (uint64_t i = 0; i < count; i++) { - entities.push_back(em->create()); + entities.push_back(em.create()); } } TEST_F(BenchmarksTest, TestDestroyEntitiesWithListener) { Listener listen; - ev->subscribe(listen); + ev.subscribe(listen); uint64_t count = 10000000L; vector entities; for (uint64_t i = 0; i < count; i++) { - entities.push_back(em->create()); + entities.push_back(em.create()); } AutoTimer t; @@ -98,7 +98,7 @@ TEST_F(BenchmarksTest, TestEntityIteration) { uint64_t count = 10000000L; vector entities; for (uint64_t i = 0; i < count; i++) { - auto e = em->create(); + auto e = em.create(); e.assign(); entities.push_back(e); } @@ -108,7 +108,7 @@ TEST_F(BenchmarksTest, TestEntityIteration) { for (int i = 0; i < 10; ++i) { ComponentPtr position; - for (auto e : em->entities_with_components(position)) { + for (auto e : em.entities_with_components(position)) { } } } diff --git a/entityx/Entity.cc b/entityx/Entity.cc index f23e974..ffdfbee 100644 --- a/entityx/Entity.cc +++ b/entityx/Entity.cc @@ -18,20 +18,20 @@ BaseComponent::Family BaseComponent::family_counter_ = 0; void Entity::invalidate() { id_ = INVALID; - manager_.reset(); + manager_ = nullptr; } void Entity::destroy() { assert(valid()); - manager_.lock()->destroy(id_); + manager_->destroy(id_); invalidate(); } std::bitset Entity::component_mask() const { - return manager_.lock()->component_mask(id_); + return manager_->component_mask(id_); } -EntityManager::EntityManager(ptr event_manager) : event_manager_(event_manager) { +EntityManager::EntityManager(EventManager &event_manager) : event_manager_(event_manager) { } EntityManager::~EntityManager() { diff --git a/entityx/Entity.h b/entityx/Entity.h index 141f2e6..18f04b8 100644 --- a/entityx/Entity.h +++ b/entityx/Entity.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -76,7 +77,7 @@ class Entity { static const Id INVALID; Entity() {} - Entity(const ptr &manager, Entity::Id id) : manager_(manager), id_(id) {} + Entity(EntityManager *manager, Entity::Id id) : manager_(manager), id_(id) {} Entity(const Entity &other) : manager_(other.manager_), id_(other.id_) {} Entity &operator = (const Entity &other) { @@ -142,7 +143,7 @@ class Entity { std::bitset component_mask() const; private: - weak_ptr manager_; + EntityManager *manager_ = nullptr; Entity::Id id_ = INVALID; }; @@ -221,6 +222,8 @@ class ComponentPtr { friend class EntityManager; ComponentPtr(Entity::Id id, EntityManager *manager) : id_(id), manager_(manager) {} + ComponentPtr(Entity::Id id, const EntityManager *manager) : + id_(id), manager_(const_cast(manager)) {} T *get_component_ptr(); const T *get_component_ptr() const; @@ -351,36 +354,16 @@ class ComponentAllocator : public BaseComponentAllocator { /** * Manages Entity::Id creation and component assignment. */ -class EntityManager : entityx::help::NonCopyable, public enable_shared_from_this { +class EntityManager : entityx::help::NonCopyable { public: typedef std::bitset ComponentMask; - explicit EntityManager(ptr event_manager); + explicit EntityManager(EventManager &event_manager); virtual ~EntityManager(); - static ptr make(ptr event_manager) { - return ptr(new EntityManager(event_manager)); - } - class View { public: - typedef std::function &, const Entity::Id &)> Predicate; - - /// A predicate that matches valid entities with the given component mask. - class ComponentMaskPredicate { - public: - ComponentMaskPredicate(const std::vector &entity_component_masks, ComponentMask mask) - : entity_component_masks_(entity_component_masks), mask_(mask) {} - - bool operator()(const ptr &entities, const Entity::Id &entity) { - return entities->entity_version_[entity.index()] == entity.version() - && (entity_component_masks_[entity.index()] & mask_) == mask_; - } - - private: - const std::vector &entity_component_masks_; - ComponentMask mask_; - }; + typedef std::function Predicate; struct BaseUnpacker { virtual ~BaseUnpacker() {} @@ -403,9 +386,9 @@ class EntityManager : entityx::help::NonCopyable, public enable_shared_from_this private: friend class View; - Iterator(ptr manager, + Iterator(EntityManager *manager, const std::vector &predicates, - const std::vector> &unpackers, + const std::vector> &unpackers, uint32_t index) : manager_(manager), predicates_(predicates), unpackers_(unpackers), i_(index), capacity_(manager_->capacity()) { next(); @@ -427,16 +410,16 @@ class EntityManager : entityx::help::NonCopyable, public enable_shared_from_this bool predicate() { Entity::Id id = manager_->create_id(i_); for (auto &p : predicates_) { - if (!p(manager_, id)) { + if (!p(*manager_, id)) { return false; } } return true; } - ptr manager_; + EntityManager *manager_; std::vector predicates_; - std::vector> unpackers_; + std::vector> unpackers_; uint32_t i_; size_t capacity_; }; @@ -453,7 +436,7 @@ class EntityManager : entityx::help::NonCopyable, public enable_shared_from_this template View &unpack_to(ComponentPtr &a) { - unpackers_.push_back(ptr>(new Unpacker(manager_, a))); + unpackers_.push_back(std::shared_ptr>(new Unpacker(manager_, a))); return *this; } @@ -468,24 +451,24 @@ class EntityManager : entityx::help::NonCopyable, public enable_shared_from_this template struct Unpacker : BaseUnpacker { - Unpacker(ptr manager, ComponentPtr &c) : manager_(manager), c(c) {} + Unpacker(EntityManager *manager, ComponentPtr &c) : manager_(manager), c(c) {} void unpack(const Entity::Id &id) { c = manager_->component(id); } private: - ptr manager_; + EntityManager *manager_; ComponentPtr &c; }; - View(ptr manager, Predicate predicate) : manager_(manager) { + View(EntityManager *manager, Predicate predicate) : manager_(manager) { predicates_.push_back(predicate); } - ptr manager_; + EntityManager *manager_; std::vector predicates_; - std::vector> unpackers_; + std::vector> unpackers_; }; /** @@ -521,8 +504,8 @@ class EntityManager : entityx::help::NonCopyable, public enable_shared_from_this free_list_.pop_front(); version = entity_version_[index]; } - Entity entity(shared_from_this(), Entity::Id(index, version)); - event_manager_->emit(entity); + Entity entity(this, Entity::Id(index, version)); + event_manager_.emit(entity); return entity; } @@ -534,7 +517,7 @@ class EntityManager : entityx::help::NonCopyable, public enable_shared_from_this void destroy(Entity::Id entity) { assert_valid(entity); int index = entity.index(); - event_manager_->emit(Entity(shared_from_this(), entity)); + event_manager_.emit(Entity(this, entity)); for (BaseComponentAllocator *allocator : component_allocators_) { if (allocator) allocator->destroy(index); } @@ -545,7 +528,7 @@ class EntityManager : entityx::help::NonCopyable, public enable_shared_from_this Entity get(Entity::Id id) { assert_valid(id); - return Entity(shared_from_this(), id); + return Entity(this, id); } /** @@ -573,7 +556,7 @@ class EntityManager : entityx::help::NonCopyable, public enable_shared_from_this new(allocator->get(id.index())) C(std::forward(args) ...); entity_component_mask_[id.index()] |= uint64_t(1) << family; ComponentPtr component(id, this); - event_manager_->emit>(Entity(shared_from_this(), id), component); + event_manager_.emit>(Entity(this, id), component); return ComponentPtr(id, this); } @@ -591,14 +574,14 @@ class EntityManager : entityx::help::NonCopyable, public enable_shared_from_this assert(allocator); entity_component_mask_[id.index()] &= ~(uint64_t(1) << family); ComponentPtr component(id, this); - event_manager_->emit>(Entity(shared_from_this(), id), component); + event_manager_.emit>(Entity(this, id), component); allocator->destroy(index); } /** * Retrieve a Component assigned to an Entity::Id. * - * @returns Component instance, or empty ptr<> if the Entity::Id does not have that Component. + * @returns Component instance, or empty ComponentPtr<> if the Entity::Id does not have that Component. */ template ComponentPtr component(Entity::Id id) { @@ -613,24 +596,59 @@ class EntityManager : entityx::help::NonCopyable, public enable_shared_from_this return ComponentPtr(id, this); } + /** + * Retrieve a Component assigned to an Entity::Id. + * + * @returns Component instance, or empty ComponentPtr<> if the Entity::Id does not have that Component. + */ + template + ComponentPtr component(Entity::Id id) const { + assert_valid(id); + size_t family = C::family(); + // We don't bother checking the component mask, as we return a nullptr anyway. + if (family >= component_allocators_.size()) + return ComponentPtr(); + BaseComponentAllocator *allocator = component_allocators_[family]; + if (!allocator || !entity_component_mask_[id.index()][family]) + return ComponentPtr(); + return ComponentPtr(id, this); + } + /** * Find Entities that have all of the specified Components. + * + * @code + * for (Entity entity : entity_manager.entities_with_components()) { + * ComponentPtr position = entity.component(); + * ComponentPtr direction = entity.component(); + * + * ... + * } + * @endcode */ template View entities_with_components() { auto mask = component_mask(); - return View(shared_from_this(), View::ComponentMaskPredicate(entity_component_mask_, mask)); + return View(this, ComponentMaskPredicate(entity_component_mask_, mask)); } /** * Find Entities that have all of the specified Components and assign them * to the given parameters. + * + * @code + * ComponentPtr position; + * ComponentPtr direction; + * for (Entity entity : entity_manager.entities_with_components(position, direction)) { + * // Use position and component here. + * } + * @endcode */ template View entities_with_components(ComponentPtr &c, ComponentPtr & ... args) { auto mask = component_mask(c, args ...); return - View(shared_from_this(), View::ComponentMaskPredicate(entity_component_mask_, mask)) + View(this, ComponentMaskPredicate(entity_component_mask_, mask)) .unpack_to(c, args ...); } @@ -668,6 +686,22 @@ class EntityManager : entityx::help::NonCopyable, public enable_shared_from_this friend class ComponentPtr; friend class Entity; + /// A predicate that matches valid entities with the given component mask. + class ComponentMaskPredicate { + public: + ComponentMaskPredicate(const std::vector &entity_component_masks, ComponentMask mask) + : entity_component_masks_(entity_component_masks), mask_(mask) {} + + bool operator()(const EntityManager &entities, const Entity::Id &entity) { + return entities.entity_version_[entity.index()] == entity.version() + && (entity_component_masks_[entity.index()] & mask_) == mask_; + } + + private: + const std::vector &entity_component_masks_; + ComponentMask mask_; + }; + inline void assert_valid(Entity::Id id) const { assert(id.index() < entity_component_mask_.size() && "Entity::Id ID outside entity vector range"); assert(entity_version_[id.index()] == id.version() && "Attempt to access Entity via a stale Entity::Id"); @@ -741,7 +775,7 @@ class EntityManager : entityx::help::NonCopyable, public enable_shared_from_this uint32_t index_counter_ = 0; - ptr event_manager_; + EventManager &event_manager_; // Each element in component_allocators_ corresponds to a Component::family(). std::vector component_allocators_; // Bitmask of components associated with each entity. Index into the vector is the Entity::Id. @@ -764,29 +798,29 @@ BaseComponent::Family Component::family() { template ComponentPtr Entity::assign(Args && ... args) { assert(valid()); - return manager_.lock()->assign(id_, std::forward(args) ...); + return manager_->assign(id_, std::forward(args) ...); } template void Entity::remove() { assert(valid() && component()); - manager_.lock()->remove(id_); + manager_->remove(id_); } template ComponentPtr Entity::component() { assert(valid()); - return manager_.lock()->component(id_); + return manager_->component(id_); } template void Entity::unpack(ComponentPtr &a, ComponentPtr & ... args) { assert(valid()); - manager_.lock()->unpack(id_, a, args ...); + manager_->unpack(id_, a, args ...); } inline bool Entity::valid() const { - return !manager_.expired() && manager_.lock()->valid(id_); + return manager_ && manager_->valid(id_); } diff --git a/entityx/Entity_test.cc b/entityx/Entity_test.cc index 553a159..2caf8f4 100644 --- a/entityx/Entity_test.cc +++ b/entityx/Entity_test.cc @@ -77,10 +77,10 @@ ostream &operator << (ostream &out, const Tag &tag) { class EntityManagerTest : public ::testing::Test { protected: - EntityManagerTest() : ev(EventManager::make()), em(EntityManager::make(ev)) {} + EntityManagerTest() : em(ev) {} - ptr ev; - ptr em; + EventManager ev; + EntityManager em; virtual void SetUp() { } @@ -88,29 +88,29 @@ class EntityManagerTest : public ::testing::Test { TEST_F(EntityManagerTest, TestCreateEntity) { - ASSERT_EQ(em->size(), 0UL); + ASSERT_EQ(em.size(), 0UL); Entity e2; ASSERT_FALSE(e2.valid()); - Entity e = em->create(); + Entity e = em.create(); ASSERT_TRUE(e.valid()); - ASSERT_EQ(em->size(), 1UL); + ASSERT_EQ(em.size(), 1UL); e2 = e; ASSERT_TRUE(e2.valid()); } TEST_F(EntityManagerTest, TestEntityAsBoolean) { - ASSERT_EQ(em->size(), 0UL); - Entity e = em->create(); + ASSERT_EQ(em.size(), 0UL); + Entity e = em.create(); ASSERT_TRUE(e.valid()); - ASSERT_EQ(em->size(), 1UL); + ASSERT_EQ(em.size(), 1UL); ASSERT_FALSE(!e); e.destroy(); - ASSERT_EQ(em->size(), 0UL); + ASSERT_EQ(em.size(), 0UL); ASSERT_TRUE(!e); @@ -119,7 +119,7 @@ TEST_F(EntityManagerTest, TestEntityAsBoolean) { } TEST_F(EntityManagerTest, TestEntityReuse) { - Entity e1 = em->create(); + Entity e1 = em.create(); Entity e2 = e1; auto id = e1.id(); ASSERT_TRUE(e1.valid()); @@ -127,7 +127,7 @@ TEST_F(EntityManagerTest, TestEntityReuse) { e1.destroy(); ASSERT_TRUE(!e1.valid()); ASSERT_TRUE(!e2.valid()); - Entity e3 = em->create(); + Entity e3 = em.create(); // It is assumed that the allocation will reuse the same entity id, though // the version will change. auto new_id = e3.id(); @@ -136,7 +136,7 @@ TEST_F(EntityManagerTest, TestEntityReuse) { } TEST_F(EntityManagerTest, TestComponentConstruction) { - auto e = em->create(); + auto e = em.create(); auto p = e.assign(1, 2); auto cp = e.component(); ASSERT_EQ(p, cp); @@ -145,8 +145,8 @@ TEST_F(EntityManagerTest, TestComponentConstruction) { } TEST_F(EntityManagerTest, TestDestroyEntity) { - Entity e = em->create(); - Entity f = em->create(); + Entity e = em.create(); + Entity f = em.create(); e.assign(); f.assign(); e.assign(); @@ -168,37 +168,37 @@ TEST_F(EntityManagerTest, TestDestroyEntity) { } TEST_F(EntityManagerTest, TestGetEntitiesWithComponent) { - Entity e = em->create(); - Entity f = em->create(); - Entity g = em->create(); + Entity e = em.create(); + Entity f = em.create(); + Entity g = em.create(); e.assign(); e.assign(); f.assign(); g.assign(); - ASSERT_EQ(3, size(em->entities_with_components())); - ASSERT_EQ(1, size(em->entities_with_components())); + ASSERT_EQ(3, size(em.entities_with_components())); + ASSERT_EQ(1, size(em.entities_with_components())); } TEST_F(EntityManagerTest, TestGetEntitiesWithIntersectionOfComponents) { vector entities; for (int i = 0; i < 150; ++i) { - Entity e = em->create(); + Entity e = em.create(); entities.push_back(e); if (i % 2 == 0) e.assign(); if (i % 3 == 0) e.assign(); } - ASSERT_EQ(50, size(em->entities_with_components())); - ASSERT_EQ(75, size(em->entities_with_components())); - ASSERT_EQ(25, size(em->entities_with_components())); + ASSERT_EQ(50, size(em.entities_with_components())); + ASSERT_EQ(75, size(em.entities_with_components())); + ASSERT_EQ(25, size(em.entities_with_components())); } TEST_F(EntityManagerTest, TestGetEntitiesWithComponentAndUnpacking) { vector entities; - Entity e = em->create(); - Entity f = em->create(); - Entity g = em->create(); + Entity e = em.create(); + Entity f = em.create(); + Entity g = em.create(); std::vector, ComponentPtr>> position_directions; position_directions.push_back(std::make_pair( e.assign(1.0f, 2.0f), @@ -212,10 +212,10 @@ TEST_F(EntityManagerTest, TestGetEntitiesWithComponentAndUnpacking) { ComponentPtr position; - ASSERT_EQ(3, size(em->entities_with_components(position))); + ASSERT_EQ(3, size(em.entities_with_components(position))); ComponentPtr direction; - for (auto unused_entity : em->entities_with_components(position, direction)) { + for (auto unused_entity : em.entities_with_components(position, direction)) { (void)unused_entity; ASSERT_TRUE(static_cast(position)); ASSERT_TRUE(static_cast(direction)); @@ -227,7 +227,7 @@ TEST_F(EntityManagerTest, TestGetEntitiesWithComponentAndUnpacking) { ASSERT_EQ(2, i); ComponentPtr tag; i = 0; - for (auto unused_entity : em->entities_with_components(position, direction, tag)) { + for (auto unused_entity : em.entities_with_components(position, direction, tag)) { (void)unused_entity; ASSERT_TRUE(static_cast(position)); ASSERT_TRUE(static_cast(direction)); @@ -242,7 +242,7 @@ TEST_F(EntityManagerTest, TestGetEntitiesWithComponentAndUnpacking) { } TEST_F(EntityManagerTest, TestUnpack) { - Entity e = em->create(); + Entity e = em.create(); auto p = e.assign(1.0, 2.0); auto d = e.assign(3.0, 4.0); auto t = e.assign("tag"); @@ -264,14 +264,14 @@ TEST_F(EntityManagerTest, TestUnpack) { // gcc 4.7.2 does not allow this struct to be declared locally inside the TEST_F. // TEST_F(EntityManagerTest, TestUnpackNullMissing) { -// Entity e = em->create(); +// Entity e = em.create(); // auto p = e.assign(); -// ptr up(reinterpret_cast(0Xdeadbeef), NullDeleter()); -// ptr ud(reinterpret_cast(0Xdeadbeef), NullDeleter()); +// std::shared_ptr up(reinterpret_cast(0Xdeadbeef), NullDeleter()); +// std::shared_ptr ud(reinterpret_cast(0Xdeadbeef), NullDeleter()); // e.unpack(up, ud); // ASSERT_EQ(p, up); -// ASSERT_EQ(ptr(), ud); +// ASSERT_EQ(std::shared_ptr(), ud); // } TEST_F(EntityManagerTest, TestComponentIdsDiffer) { @@ -288,11 +288,11 @@ TEST_F(EntityManagerTest, TestEntityCreatedEvent) { }; EntityCreatedEventReceiver receiver; - ev->subscribe(receiver); + ev.subscribe(receiver); ASSERT_EQ(0UL, receiver.created.size()); for (int i = 0; i < 10; ++i) { - em->create(); + em.create(); } ASSERT_EQ(10UL, receiver.created.size()); } @@ -307,12 +307,12 @@ TEST_F(EntityManagerTest, TestEntityDestroyedEvent) { }; EntityDestroyedEventReceiver receiver; - ev->subscribe(receiver); + ev.subscribe(receiver); ASSERT_EQ(0UL, receiver.destroyed.size()); vector entities; for (int i = 0; i < 10; ++i) { - entities.push_back(em->create()); + entities.push_back(em.create()); } ASSERT_EQ(0UL, receiver.destroyed.size()); for (auto e : entities) { @@ -344,8 +344,8 @@ TEST_F(EntityManagerTest, TestComponentAddedEvent) { }; ComponentAddedEventReceiver receiver; - ev->subscribe>(receiver); - ev->subscribe>(receiver); + ev.subscribe>(receiver); + ev.subscribe>(receiver); ASSERT_NE(ComponentAddedEvent::family(), ComponentAddedEvent::family()); @@ -353,7 +353,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 e = em.create(); e.assign(static_cast(i), static_cast(i)); e.assign(static_cast(-i), static_cast(-i)); } @@ -371,10 +371,10 @@ TEST_F(EntityManagerTest, TestComponentRemovedEvent) { }; ComponentRemovedReceiver receiver; - ev->subscribe>(receiver); + ev.subscribe>(receiver); ASSERT_FALSE(receiver.removed); - Entity e = em->create(); + Entity e = em.create(); auto p = e.assign(1.0, 2.0); e.remove(); ASSERT_EQ(receiver.removed, p); @@ -383,7 +383,7 @@ TEST_F(EntityManagerTest, TestComponentRemovedEvent) { TEST_F(EntityManagerTest, TestEntityAssignment) { Entity a, b; - a = em->create(); + a = em.create(); ASSERT_NE(a, b); b = a; ASSERT_EQ(a, b); @@ -392,8 +392,8 @@ TEST_F(EntityManagerTest, TestEntityAssignment) { } TEST_F(EntityManagerTest, TestEntityDestroyAll) { - Entity a = em->create(), b = em->create(); - em->reset(); + Entity a = em.create(), b = em.create(); + em.reset(); ASSERT_FALSE(a.valid()); ASSERT_FALSE(b.valid()); } @@ -403,12 +403,12 @@ TEST_F(EntityManagerTest, TestEntityDestroyHole) { std::vector entities; auto count = [this]() -> int { - auto e = em->entities_with_components(); + auto e = em.entities_with_components(); return std::count_if(e.begin(), e.end(), [] (const Entity &) { return true; }); }; for (int i = 0; i < 5000; i++) { - auto e = em->create(); + auto e = em.create(); e.assign(); entities.push_back(e); } diff --git a/entityx/Event.h b/entityx/Event.h index 8c8a8dc..a4b3e5d 100644 --- a/entityx/Event.h +++ b/entityx/Event.h @@ -10,10 +10,11 @@ #pragma once +#include #include #include +#include #include -#include #include "entityx/config.h" #include "entityx/3rdparty/simplesignal.h" #include "entityx/help/NonCopyable.h" @@ -37,8 +38,8 @@ class BaseEvent { typedef Simple::Signal EventSignal; -typedef ptr EventSignalPtr; -typedef weak_ptr EventSignalWeakPtr; +typedef std::shared_ptr EventSignalPtr; +typedef std::weak_ptr EventSignalWeakPtr; /** @@ -107,10 +108,6 @@ class EventManager : entityx::help::NonCopyable { EventManager(); virtual ~EventManager(); - static ptr make() { - return ptr(new EventManager()); - } - /** * Subscribe an object to receive events of type E. * @@ -142,7 +139,7 @@ class EventManager : entityx::help::NonCopyable { * Emit an already constructed event. */ template - void emit(ptr event) { + void emit(std::unique_ptr event) { auto sig = signal_for(E::family()); sig->emit(static_cast(event.get())); } @@ -154,7 +151,7 @@ class EventManager : entityx::help::NonCopyable { * * eg. * - * ptr em = new EventManager(); + * std::shared_ptr em = new EventManager(); * em->emit(10); * */ diff --git a/entityx/Event_test.cc b/entityx/Event_test.cc index 7986526..362d361 100644 --- a/entityx/Event_test.cc +++ b/entityx/Event_test.cc @@ -33,49 +33,49 @@ struct ExplosionSystem : public Receiver { }; TEST(EventManagerTest, TestEmitReceive) { - auto em = EventManager::make(); + EventManager em; ExplosionSystem explosion_system; - em->subscribe(explosion_system); + em.subscribe(explosion_system); ASSERT_EQ(0, explosion_system.damage_received); - em->emit(10); + em.emit(10); ASSERT_EQ(10, explosion_system.damage_received); } TEST(EventManagerTest, TestUntypedEmitReceive) { - auto em = EventManager::make(); + EventManager em; ExplosionSystem explosion_system; - em->subscribe(explosion_system); + em.subscribe(explosion_system); ASSERT_EQ(0, explosion_system.damage_received); Explosion explosion(10); - em->emit(explosion); + em.emit(explosion); ASSERT_EQ(10, explosion_system.damage_received); } TEST(EventManagerTest, TestReceiverExpired) { - auto em = EventManager::make(); + EventManager em; { ExplosionSystem explosion_system; - em->subscribe(explosion_system); - em->emit(10); + em.subscribe(explosion_system); + em.emit(10); ASSERT_EQ(10, explosion_system.damage_received); ASSERT_EQ(1, explosion_system.connected_signals()); - ASSERT_EQ(1, em->connected_receivers()); + ASSERT_EQ(1, em.connected_receivers()); } - ASSERT_EQ(0, em->connected_receivers()); + ASSERT_EQ(0, em.connected_receivers()); } TEST(EventManagerTest, TestSenderExpired) { ExplosionSystem explosion_system; { - auto em = EventManager::make(); - em->subscribe(explosion_system); - em->emit(10); + EventManager em; + em.subscribe(explosion_system); + em.emit(10); ASSERT_EQ(10, explosion_system.damage_received); ASSERT_EQ(1, explosion_system.connected_signals()); - ASSERT_EQ(1, em->connected_receivers()); + ASSERT_EQ(1, em.connected_receivers()); } ASSERT_EQ(0, explosion_system.connected_signals()); } diff --git a/entityx/Manager.cc b/entityx/Manager.cc deleted file mode 100644 index a86ee56..0000000 --- a/entityx/Manager.cc +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2012 Alec Thomas - * All rights reserved. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. - * - * Author: Alec Thomas - */ - -#include "Manager.h" - -namespace entityx { - -void Manager::start() { - configure(); - system_manager->configure(); - initialize(); -} - -void Manager::run() { - running_ = true; - double dt; - timer_.restart(); - while (running_) { - dt = timer_.elapsed(); - timer_.restart(); - update(dt); - } -} - -void Manager::step(double dt) { - update(dt); -} - - -void Manager::stop() { - running_ = false; -} - -} // namespace entityx diff --git a/entityx/Manager.h b/entityx/Manager.h deleted file mode 100644 index 2da482e..0000000 --- a/entityx/Manager.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2012 Alec Thomas - * All rights reserved. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. - * - * Author: Alec Thomas - */ - -#pragma once - -#include "entityx/Entity.h" -#include "entityx/Event.h" -#include "entityx/System.h" -#include "entityx/help/Timer.h" - -namespace entityx { - -class Manager { - public: - virtual ~Manager() {} - - /** - * Call start() to initialize the Manager. - */ - void start(); - - - /** - * Run the main loop. To explicitly manage your own main loop use step(dt); - */ - void run(); - - /** - * Step the system by dt. - * @param dt Delta time since last frame. - */ - void step(double dt); - - /** - * Stop the manager. - */ - void stop(); - - protected: - Manager() : - event_manager(EventManager::make()), - entity_manager(EntityManager::make(event_manager)), - system_manager(SystemManager::make(entity_manager, event_manager)) {} - - /** - * Configure the world. - * - * This is called once on Manager initialization. It is typically used to add Systems to the world, load permanent - * resources, global configuration, etc. - */ - virtual void configure() = 0; - - /** - * Initialize the entities and events in the world. - * - * Typically used to create initial entities, setup event handlers, and so on. - */ - virtual void initialize() = 0; - - /** - * Update the world. - * - * Typically this is where you would call update() on all Systems in the world. - */ - virtual void update(double dt) = 0; - - ptr event_manager; - ptr entity_manager; - ptr system_manager; - - private: - help::Timer timer_; - bool running_ = false; -}; - -} // namespace entityx diff --git a/entityx/System.cc b/entityx/System.cc index 44995bc..dc83cac 100644 --- a/entityx/System.cc +++ b/entityx/System.cc @@ -15,10 +15,10 @@ namespace entityx { BaseSystem::Family BaseSystem::family_counter_; void SystemManager::configure() { - for (auto pair : systems_) { + for (auto &pair : systems_) { pair.second->configure(event_manager_); } initialized_ = true; } -} +} // namespace entityx diff --git a/entityx/System.h b/entityx/System.h index 9c0b2ec..d94c91a 100644 --- a/entityx/System.h +++ b/entityx/System.h @@ -11,9 +11,9 @@ #pragma once +#include #include #include -#include #include #include "entityx/config.h" #include "entityx/Entity.h" @@ -23,6 +23,10 @@ namespace entityx { + +class SystemManager; + + /** * Base System class. Generally should not be directly used, instead see System. */ @@ -37,14 +41,14 @@ class BaseSystem : entityx::help::NonCopyable { * * Typically used to set up event handlers. */ - virtual void configure(ptr events) {} + virtual void configure(EventManager &events) {} /** * Apply System behavior. * * Called every game step. */ - virtual void update(ptr entities, ptr events, double dt) = 0; + virtual void update(EntityManager &entities, EventManager &events, double dt) = 0; static Family family_counter_; @@ -56,7 +60,7 @@ class BaseSystem : entityx::help::NonCopyable { * Use this class when implementing Systems. * * struct MovementSystem : public System { - * void update(ptr entities, EventManager &events, double dt) { + * void update(EntityManager &entities, EventManager &events, double dt) { * // Do stuff to/with entities... * } * } @@ -66,6 +70,9 @@ class System : public BaseSystem { public: virtual ~System() {} +private: + friend class SystemManager; + static Family family() { static Family family = family_counter_++; return family; @@ -73,29 +80,24 @@ class System : public BaseSystem { }; -class SystemManager : entityx::help::NonCopyable, public enable_shared_from_this { +class SystemManager : entityx::help::NonCopyable { public: - SystemManager(ptr entity_manager, - ptr event_manager) : + SystemManager(EntityManager &entity_manager, + EventManager &event_manager) : entity_manager_(entity_manager), event_manager_(event_manager) {} - static ptr make(ptr entity_manager, - ptr event_manager) { - return ptr(new SystemManager(entity_manager, event_manager)); - } - /** * Add a System to the SystemManager. * * Must be called before Systems can be used. * * eg. - * ptr movement = entityx::make_shared(); + * std::shared_ptr movement = entityx::make_shared(); * system.add(movement); */ template - void add(ptr system) { + void add(std::shared_ptr system) { systems_.insert(std::make_pair(S::family(), system)); } @@ -108,8 +110,8 @@ class SystemManager : entityx::help::NonCopyable, public enable_shared_from_this * auto movement = system.add(); */ template - ptr add(Args && ... args) { - ptr s(new S(std::forward(args) ...)); + std::shared_ptr add(Args && ... args) { + std::shared_ptr s(new S(std::forward(args) ...)); add(s); return s; } @@ -117,17 +119,17 @@ class SystemManager : entityx::help::NonCopyable, public enable_shared_from_this /** * Retrieve the registered System instance, if any. * - * ptr collisions = systems.system(); + * std::shared_ptr collisions = systems.system(); * - * @return System instance or empty shared_ptr. + * @return System instance or empty shared_std::shared_ptr. */ template - ptr system() { + std::shared_ptr system() { auto it = systems_.find(S::family()); assert(it != systems_.end()); return it == systems_.end() - ? ptr() - : ptr(static_pointer_cast(it->second)); + ? std::shared_ptr() + : std::shared_ptr(std::static_pointer_cast(it->second)); } /** @@ -136,7 +138,7 @@ class SystemManager : entityx::help::NonCopyable, public enable_shared_from_this template void update(double dt) { assert(initialized_ && "SystemManager::configure() not called"); - ptr s = system(); + std::shared_ptr s = system(); s->update(entity_manager_, event_manager_, dt); } @@ -149,9 +151,9 @@ class SystemManager : entityx::help::NonCopyable, public enable_shared_from_this private: bool initialized_ = false; - ptr entity_manager_; - ptr event_manager_; - std::unordered_map> systems_; + EntityManager &entity_manager_; + EventManager &event_manager_; + std::unordered_map> systems_; }; } // namespace entityx diff --git a/entityx/System_test.cc b/entityx/System_test.cc index e0d5b6a..16f7372 100644 --- a/entityx/System_test.cc +++ b/entityx/System_test.cc @@ -8,11 +8,11 @@ * Author: Alec Thomas */ +#include #include #include -#include -#include "entityx/Manager.h" #include "entityx/System.h" +#include "entityx/quick.h" // using namespace std; @@ -36,8 +36,8 @@ class MovementSystem : public System { public: explicit MovementSystem(string label = "") : label(label) {} - void update(ptr es, ptr events, double) override { - EntityManager::View entities = es->entities_with_components(); + void update(EntityManager &es, EventManager &events, double) override { + EntityManager::View entities = es.entities_with_components(); ComponentPtr position; ComponentPtr direction; for (auto entity : entities) { @@ -51,59 +51,49 @@ class MovementSystem : public System { }; -class TestManager : public entityx::Manager { +class TestContainer : public EntityX { public: - std::vector entities; - - ptr sm() { return system_manager; } - ptr em() { return entity_manager; } - - protected: - void configure() override { - } + std::vector created_entities; - void initialize() override { + void initialize() { for (int i = 0; i < 150; ++i) { - Entity e = entity_manager->create(); - entities.push_back(e); + Entity e = entities.create(); + created_entities.push_back(e); if (i % 2 == 0) e.assign(1, 2); if (i % 3 == 0) e.assign(1, 1); } } - - void update(double dt) override { - } }; class SystemManagerTest : public ::testing::Test { protected: - TestManager manager; + TestContainer manager; virtual void SetUp() override { - manager.start(); + manager.initialize(); } }; TEST_F(SystemManagerTest, TestConstructSystemWithArgs) { - manager.sm()->add("movement"); - manager.sm()->configure(); + manager.systems.add("movement"); + manager.systems.configure(); - ASSERT_EQ("movement", manager.sm()->system()->label); + ASSERT_EQ("movement", manager.systems.system()->label); } TEST_F(SystemManagerTest, TestApplySystem) { - manager.sm()->add(); - manager.sm()->configure(); + manager.systems.add(); + manager.systems.configure(); - manager.sm()->update(0.0); + manager.systems.update(0.0); ComponentPtr position; ComponentPtr direction; - for (auto entity : manager.entities) { + for (auto entity : manager.created_entities) { entity.unpack(position, direction); if (position && direction) { ASSERT_FLOAT_EQ(2.0, position->x); diff --git a/entityx/config.h.in b/entityx/config.h.in index 940d4d1..575b34f 100644 --- a/entityx/config.h.in +++ b/entityx/config.h.in @@ -1,55 +1,11 @@ #pragma once #cmakedefine ENTITYX_MAX_COMPONENTS @ENTITYX_MAX_COMPONENTS@ -#cmakedefine ENTITYX_HAVE_BOOST_PYTHON 1 -#cmakedefine ENTITYX_INSTALLED_PYTHON_PACKAGE_DIR "@ENTITYX_INSTALLED_PYTHON_PACKAGE_DIR@" -#cmakedefine ENTITYX_NEED_GET_POINTER_SHARED_PTR_SPECIALIZATION "@ENTITYX_NEED_GET_POINTER_SHARED_PTR_SPECIALIZATION@" #include -#include "entityx/config.h" namespace entityx { static const uint64_t MAX_COMPONENTS = ENTITYX_MAX_COMPONENTS; } // namespace entityx - - -// Which shared_ptr implementation should we use? -#include -// for std::forward() -#include - -namespace entityx { - -template -using ptr = std::shared_ptr; -template -using weak_ptr = std::weak_ptr; -template -ptr static_pointer_cast(const ptr &ptr) { - return std::static_pointer_cast(ptr); -} -template -using enable_shared_from_this = std::enable_shared_from_this; - -} // namespace entityx - -namespace entityx { - -template -ptr make_ptr(Args&&... args) { - return ptr(::new T(std::forward(args)...)); -} - -template -bool operator == (const weak_ptr &a, const weak_ptr &b) { - return a.lock() == b.lock(); -} - -template -int use_count(const ptr &ptr) { - return ptr.use_count(); -} - -} // namespace entityx diff --git a/entityx/deps/Dependencies.cc b/entityx/deps/Dependencies.cc deleted file mode 100644 index 9271a2f..0000000 --- a/entityx/deps/Dependencies.cc +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright (C) 2013 Alec Thomas - * All rights reserved. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. - * - * Author: Alec Thomas - */ - -#include "entityx/deps/Dependencies.h" diff --git a/entityx/deps/Dependencies.h b/entityx/deps/Dependencies.h index 69e68bb..83ab4a5 100644 --- a/entityx/deps/Dependencies.h +++ b/entityx/deps/Dependencies.h @@ -31,11 +31,11 @@ class Dependency : public System>, public Receiver(event.entity); } - virtual void configure(ptr events) override { - events->subscribe>(*this); + virtual void configure(EventManager &events) override { + events.subscribe>(*this); } - virtual void update(ptr entities, ptr events, double dt) override {} + virtual void update(EntityManager &entities, EventManager &events, double dt) override {} private: template diff --git a/entityx/deps/Dependencies_test.cc b/entityx/deps/Dependencies_test.cc index 8a2f17c..72d4ae8 100644 --- a/entityx/deps/Dependencies_test.cc +++ b/entityx/deps/Dependencies_test.cc @@ -10,6 +10,7 @@ #include #include "entityx/deps/Dependencies.h" +#include "entityx/quick.h" namespace ex = entityx; @@ -25,25 +26,15 @@ struct B : public ex::Component { struct C : public ex::Component {}; -class DepsTest : public ::testing::Test { - protected: - DepsTest() : events(ex::EventManager::make()), - entities(ex::EntityManager::make(events)), - systems(ex::SystemManager::make(entities, events)) {} - - ex::ptr events; - ex::ptr entities; - ex::ptr systems; - - virtual void SetUp() {} +class DepsTest : public ::testing::Test, public ex::EntityX { }; TEST_F(DepsTest, TestSingleDependency) { - systems->add>(); - systems->configure(); + systems.add>(); + systems.configure(); - ex::Entity e = entities->create(); + ex::Entity e = entities.create(); ASSERT_FALSE(static_cast(e.component())); ASSERT_FALSE(static_cast(e.component())); e.assign(); @@ -52,10 +43,10 @@ TEST_F(DepsTest, TestSingleDependency) { } TEST_F(DepsTest, TestMultipleDependencies) { - systems->add>(); - systems->configure(); + systems.add>(); + systems.configure(); - ex::Entity e = entities->create(); + ex::Entity e = entities.create(); ASSERT_FALSE(static_cast(e.component())); ASSERT_FALSE(static_cast(e.component())); ASSERT_FALSE(static_cast(e.component())); @@ -66,10 +57,10 @@ TEST_F(DepsTest, TestMultipleDependencies) { } TEST_F(DepsTest, TestDependencyDoesNotRecreateComponent) { - systems->add>(); - systems->configure(); + systems.add>(); + systems.configure(); - ex::Entity e = entities->create(); + ex::Entity e = entities.create(); e.assign(true); ASSERT_TRUE(e.component()->b); e.assign(); diff --git a/entityx/entityx.h b/entityx/entityx.h index eb83073..72cfa24 100644 --- a/entityx/entityx.h +++ b/entityx/entityx.h @@ -5,3 +5,4 @@ #include "entityx/Entity.h" #include "entityx/System.h" #include "entityx/Manager.h" +#include "entityx/quick.h" diff --git a/entityx/tags/TagsComponent.cc b/entityx/tags/TagsComponent.cc deleted file mode 100644 index 509debf..0000000 --- a/entityx/tags/TagsComponent.cc +++ /dev/null @@ -1,11 +0,0 @@ -/* - * Copyright (C) 2012 Alec Thomas - * All rights reserved. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. - * - * Author: Alec Thomas - */ - -#include "entityx/tags/TagsComponent.h" diff --git a/entityx/tags/TagsComponent.h b/entityx/tags/TagsComponent.h index 6fcb937..f47249f 100644 --- a/entityx/tags/TagsComponent.h +++ b/entityx/tags/TagsComponent.h @@ -10,8 +10,8 @@ #pragma once -#include #include +#include #include "entityx/Entity.h" namespace entityx { @@ -19,13 +19,20 @@ namespace tags { /** * Allow entities to be tagged with strings. + * + * entity.assign("tag1", "tag2"); + * + * ComponentPtr tags; + * for (Entity entity : entity_manager.entities_with_components(tags)) + * for (Entity entity : TagsComponent::view(entity_manager, "tag1")) { + * } */ class TagsComponent : public Component { struct TagsPredicate { explicit TagsPredicate(const std::string &tag) : tag(tag) {} - bool operator() (ptr manager, Entity::Id id) { - auto tags = manager->component(id); + bool operator() (const EntityManager &manager, Entity::Id id) { + auto tags = manager.component(id); return tags && tags->tags.find(tag) != tags->tags.end(); } diff --git a/entityx/tags/TagsComponent_test.cc b/entityx/tags/TagsComponent_test.cc index 34651b3..4674c5a 100644 --- a/entityx/tags/TagsComponent_test.cc +++ b/entityx/tags/TagsComponent_test.cc @@ -8,6 +8,7 @@ * Author: Alec Thomas */ +#include #include #include "entityx/tags/TagsComponent.h" @@ -39,16 +40,17 @@ TEST(TagsComponentTest, TestVariadicConstruction) { } TEST(TagsComponentTest, TestEntitiesWithTag) { - auto en = EntityManager::make(EventManager::make()); - Entity a = en->create(); + EventManager ev; + EntityManager en(ev); + Entity a = en.create(); a.assign(); for (int i = 0; i < 99; ++i) { - auto e = en->create(); + auto e = en.create(); e.assign(); e.assign("positionable"); } a.assign("player", "indestructible"); - auto entities = en->entities_with_components(); + auto entities = en.entities_with_components(); ASSERT_EQ(100, size(entities)); ASSERT_EQ(1, size(TagsComponent::view(entities, "player"))); }