]> code.bitgloo.com Git - clyne/entityx.git/commitdiff
Add `.each<C...>([](C &c...) {})` iteration.
authorAlec Thomas <alec@swapoff.org>
Sun, 19 Jul 2015 01:15:23 +0000 (21:15 -0400)
committerAlec Thomas <alec@swapoff.org>
Sun, 19 Jul 2015 01:16:53 +0000 (21:16 -0400)
May break code that uses `EntityManager::View`, as this is now a
template type `EntityManager::View<C...>`.

Fixes #62.

README.md
entityx/Entity.h
entityx/Entity_test.cc
entityx/System_test.cc
examples/example.cc

index 7807f23f536bf345d9427f481af45feae502200a..b450c184de53d26e7e2a55f008850e69a3bf49a4 100644 (file)
--- a/README.md
+++ b/README.md
@@ -143,7 +143,23 @@ entity.assign<Position>(1.0f, 2.0f);
 
 #### 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:
+To query all entities with a set of components assigned you can use two
+methods. Both methods will return only those entities that have *all* of the
+specified components associated with them.
+
+`entityx::EntityManager::each(f) provides functional-style iteration over
+`entity components. The callback for `each()` can optionally accept an Entity as
+`its first argument.
+
+```c++
+entities.each<Position, Direction>([](Entity entity, Position &position, Direction &direction) {
+  // Do things with entity, position and direction.
+};)
+```
+
+
+For iterator-style traversal of components, use
+``entityx::EntityManager::entities_with_components()``:
 
 ```c++
 ComponentHandle<Position> position;
@@ -189,12 +205,10 @@ A basic movement system might be implemented with something like the following:
 ```c++
 struct MovementSystem : public System<MovementSystem> {
   void update(entityx::EntityManager &es, entityx::EventManager &events, TimeDelta dt) override {
-    ComponentHandle<Position> position;
-    ComponentHandle<Direction> direction;
-    for (Entity entity : es.entities_with_components(position, direction)) {
-      position->x += direction->x * dt;
-      position->y += direction->y * dt;
-    }
+    es.each<Position, Direction>([dt](Position &position, Direction &direction) {
+      position.x += direction.x * dt;
+      position.y += direction.y * dt;
+    });
   };
 };
 ```
index 493f948cee0293961842ab687dab665a7e00b057..9f44106831c64517354d3129866130075bbff3f2 100644 (file)
@@ -412,7 +412,6 @@ class EntityManager : entityx::help::NonCopyable {
       void next_entity(Entity &entity) {}
     };
 
-
     Iterator begin() { return Iterator(manager_, mask_, 0); }
     Iterator end() { return Iterator(manager_, mask_, uint32_t(manager_->capacity())); }
     const Iterator begin() const { return Iterator(manager_, mask_, 0); }
@@ -429,7 +428,29 @@ class EntityManager : entityx::help::NonCopyable {
     ComponentMask mask_;
   };
 
-  typedef BaseView<false> View;
+  template <bool All, typename ... Components>
+  class TypedView: public BaseView<All> {
+  public:
+    template <typename T> struct identity { typedef T type; };
+
+    void each(typename identity<std::function<void(Components&...)>>::type f) {
+      for (auto it : *this)
+        f(*(it.template component<Components>().get())...);
+    }
+
+    void each(typename identity<std::function<void(Entity entity, Components&...)>>::type f) {
+      for (auto it : *this)
+        f(it, *(it.template component<Components>().get())...);
+    }
+
+  private:
+    friend class EntityManager;
+
+    explicit TypedView(EntityManager *manager) : BaseView<All>(manager) {}
+    TypedView(EntityManager *manager, ComponentMask mask) : BaseView<All>(manager, mask) {}
+  };
+
+  template <typename ... Components> using View = TypedView<false, Components...>;
   typedef BaseView<true> DebugView;
 
   template <typename ... Components>
@@ -443,6 +464,7 @@ class EntityManager : entityx::help::NonCopyable {
         unpack_<0, Components...>(entity);
       }
 
+
     private:
       template <int N, typename C>
       void unpack_(entityx::Entity &entity) const {
@@ -691,9 +713,21 @@ class EntityManager : entityx::help::NonCopyable {
    * @endcode
    */
   template <typename ... Components>
-  View entities_with_components() {
+  View<Components...> entities_with_components() {
     auto mask = component_mask<Components ...>();
-    return View(this, mask);
+    return View<Components...>(this, mask);
+  }
+
+  template <typename T> struct identity { typedef T type; };
+
+  template <typename ... Components>
+  void each(typename identity<std::function<void(Components&...)>>::type f) {
+    return entities_with_components<Components...>().each(f);
+  }
+
+  template <typename ... Components>
+  void each(typename identity<std::function<void(Entity entity, Components&...)>>::type f) {
+    return entities_with_components<Components...>().each(f);
   }
 
   /**
index f5667e296f38e514f2f76a2b5e793f74302e7df4..a79becb50c369b074b536a400df5b186ce0f525a 100644 (file)
@@ -604,3 +604,40 @@ TEST_CASE_METHOD(EntityManagerFixture, "TestConstComponentsNotInstantiatedTwice"
   REQUIRE(b.component<const Position>()->x == 1);
   REQUIRE(b.component<const Position>()->y == 2);
 }
+
+TEST_CASE_METHOD(EntityManagerFixture, "TestEntityManagerEach") {
+  Entity a = em.create();
+  a.assign<Position>(1, 2);
+  int count = 0;
+  em.each<Position>([&count](Position &position) {
+    count++;
+    REQUIRE(position.x == 1);
+    REQUIRE(position.y == 2);
+  });
+  REQUIRE(count == 1);
+}
+
+TEST_CASE_METHOD(EntityManagerFixture, "TestViewEach") {
+  Entity a = em.create();
+  a.assign<Position>(1, 2);
+  int count = 0;
+  em.entities_with_components<Position>().each([&count](Position &position) {
+    count++;
+    REQUIRE(position.x == 1);
+    REQUIRE(position.y == 2);
+  });
+  REQUIRE(count == 1);
+}
+
+TEST_CASE_METHOD(EntityManagerFixture, "TestViewEachWithEntity") {
+  Entity a = em.create();
+  a.assign<Position>(1, 2);
+  int count = 0;
+  em.entities_with_components<Position>().each([&count](Entity entity, Position &position) {
+    count++;
+    REQUIRE(position.x == 1);
+    REQUIRE(position.y == 2);
+    REQUIRE(*entity.component<Position>().get() == position);
+  });
+  REQUIRE(count == 1);
+}
index 581035733b2406ae345a7ca126e7e92cd4c3c9ff..71ef183a5c8dd5deda2d7d71b7c0082dff2cb113 100644 (file)
@@ -43,8 +43,7 @@ class MovementSystem : public System<MovementSystem> {
   explicit MovementSystem(string label = "") : label(label) {}
 
   void update(EntityManager &es, EventManager &events, TimeDelta) override {
-    EntityManager::View entities =
-        es.entities_with_components<Position, Direction>();
+    auto entities = es.entities_with_components<Position, Direction>();
     ComponentHandle<Position> position;
     ComponentHandle<Direction> direction;
     for (auto entity : entities) {
@@ -60,8 +59,7 @@ class MovementSystem : public System<MovementSystem> {
 class CounterSystem : public System<CounterSystem> {
 public:
   void update(EntityManager &es, EventManager &events, TimeDelta) override {
-    EntityManager::View entities =
-        es.entities_with_components<Counter>();
+    auto entities = es.entities_with_components<Counter>();
     Counter::Handle counter;
     for (auto entity : entities) {
       entity.unpack<Counter>(counter);
index 7488a748fca3174ddffaa6effb58a0c86e539987..7474f7f139a1272b162aba629bc36961804f1a95 100644 (file)
@@ -87,7 +87,7 @@ public:
   void update(ex::EntityManager &es, ex::EventManager &events, ex::TimeDelta dt) override {
     int c = 0;
     ex::ComponentHandle<Collideable> collideable;
-    for (ex::Entity entity : es.entities_with_components<Collideable>()) c++;
+    es.each<Collideable>([&](Collideable&) { ++c; });
 
     for (int i = 0; i < count - c; i++) {
       ex::Entity entity = es.create();
@@ -117,11 +117,10 @@ private:
 // Updates a body's position and rotation.
 struct BodySystem : public ex::System<BodySystem> {
   void update(ex::EntityManager &es, ex::EventManager &events, ex::TimeDelta dt) override {
-    ex::ComponentHandle<Body> body;
-    for (ex::Entity entity : es.entities_with_components(body)) {
-      body->position += body->direction * static_cast<float>(dt);
-      body->rotation += body->rotationd * dt;
-    }
+    es.each<Body>([dt](Body &body) {
+      body.position += body.direction * static_cast<float>(dt);
+      body.rotation += body.rotationd * dt;
+    });
   };
 };
 
@@ -132,15 +131,14 @@ public:
   explicit BounceSystem(sf::RenderTarget &target) : size(target.getSize()) {}
 
   void update(ex::EntityManager &es, ex::EventManager &events, ex::TimeDelta dt) override {
-    ex::ComponentHandle<Body> body;
-    for (ex::Entity entity : es.entities_with_components(body)) {
-      if (body->position.x + body->direction.x < 0 ||
-          body->position.x + body->direction.x >= size.x)
-        body->direction.x = -body->direction.x;
-      if (body->position.y + body->direction.y < 0 ||
-          body->position.y + body->direction.y >= size.y)
-        body->direction.y = -body->direction.y;
-    }
+    es.each<Body>([this](Body &body) {
+      if (body.position.x + body.direction.x < 0 ||
+          body.position.x + body.direction.x >= size.x)
+        body.direction.x = -body.direction.x;
+      if (body.position.y + body.direction.y < 0 ||
+          body.position.y + body.direction.y >= size.y)
+        body.direction.y = -body.direction.y;
+    });
   }
 
 private:
@@ -185,15 +183,13 @@ private:
   }
 
   void collect(ex::EntityManager &entities) {
-    ex::ComponentHandle<Body> body;
-    ex::ComponentHandle<Collideable> collideable;
-    for (ex::Entity entity : entities.entities_with_components(body, collideable)) {
+    entities.each<Body, Collideable>([this](ex::Entity entity, Body &body, Collideable &collideable) {
       unsigned int
-          left = static_cast<int>(body->position.x - collideable->radius) / PARTITIONS,
-          top = static_cast<int>(body->position.y - collideable->radius) / PARTITIONS,
-          right = static_cast<int>(body->position.x + collideable->radius) / PARTITIONS,
-          bottom = static_cast<int>(body->position.y + collideable->radius) / PARTITIONS;
-        Candidate candidate {body->position, collideable->radius, entity};
+          left = static_cast<int>(body.position.x - collideable.radius) / PARTITIONS,
+          top = static_cast<int>(body.position.y - collideable.radius) / PARTITIONS,
+          right = static_cast<int>(body.position.x + collideable.radius) / PARTITIONS,
+          bottom = static_cast<int>(body.position.y + collideable.radius) / PARTITIONS;
+        Candidate candidate {body.position, collideable.radius, entity};
         unsigned int slots[4] = {
           left + top * size.x,
           right + top * size.x,
@@ -204,7 +200,7 @@ private:
         if (slots[0] != slots[1]) grid[slots[1]].push_back(candidate);
         if (slots[1] != slots[2]) grid[slots[2]].push_back(candidate);
         if (slots[2] != slots[3]) grid[slots[3]].push_back(candidate);
-    }
+    });
   }
 
   void collide(ex::EventManager &events) {
@@ -232,15 +228,14 @@ private:
 class ParticleSystem : public ex::System<ParticleSystem> {
 public:
   void update(ex::EntityManager &es, ex::EventManager &events, ex::TimeDelta dt) override {
-    ex::ComponentHandle<Particle> particle;
-    for (ex::Entity entity : es.entities_with_components(particle)) {
-      particle->alpha -= particle->d * dt;
-      if (particle->alpha <= 0) {
+    es.each<Particle>([dt](ex::Entity entity, Particle &particle) {
+      particle.alpha -= particle.d * dt;
+      if (particle.alpha <= 0) {
         entity.destroy();
       } else {
-        particle->colour.a = particle->alpha;
+        particle.colour.a = particle.alpha;
       }
-    }
+    });
   }
 };
 
@@ -251,15 +246,13 @@ public:
 
   void update(ex::EntityManager &es, ex::EventManager &events, ex::TimeDelta dt) override {
     sf::VertexArray vertices(sf::Quads);
-    ex::ComponentHandle<Particle> particle;
-    ex::ComponentHandle<Body> body;
-    for (ex::Entity entity : es.entities_with_components(body, particle)) {
-      float r = particle->radius;
-      vertices.append(sf::Vertex(body->position + sf::Vector2f(-r, -r), particle->colour));
-      vertices.append(sf::Vertex(body->position + sf::Vector2f(r, -r), particle->colour));
-      vertices.append(sf::Vertex(body->position + sf::Vector2f(r, r), particle->colour));
-      vertices.append(sf::Vertex(body->position + sf::Vector2f(-r, r), particle->colour));
-    }
+    es.each<Particle, Body>([&vertices](Particle &particle, Body &body) {
+      const float r = particle.radius;
+      vertices.append(sf::Vertex(body.position + sf::Vector2f(-r, -r), particle.colour));
+      vertices.append(sf::Vertex(body.position + sf::Vector2f(r, -r), particle.colour));
+      vertices.append(sf::Vertex(body.position + sf::Vector2f(r, r), particle.colour));
+      vertices.append(sf::Vertex(body.position + sf::Vector2f(-r, r), particle.colour));
+    });
     target.draw(vertices);
   }
 private:
@@ -331,13 +324,11 @@ public:
   }
 
   void update(ex::EntityManager &es, ex::EventManager &events, ex::TimeDelta dt) override {
-    ex::ComponentHandle<Body> body;
-    ex::ComponentHandle<Renderable> renderable;
-    for (ex::Entity entity : es.entities_with_components(body, renderable)) {
-      renderable->shape->setPosition(body->position);
-      renderable->shape->setRotation(body->rotation);
-      target.draw(*renderable->shape.get());
-    }
+    es.each<Body, Renderable>([this](Body &body, Renderable &renderable) {
+      renderable.shape->setPosition(body.position);
+      renderable.shape->setRotation(body.rotation);
+      target.draw(*renderable.shape.get());
+    });
     last_update += dt;
     frame_count++;
     if (last_update >= 0.5) {