]> code.bitgloo.com Git - clyne/entityx.git/commitdiff
Convert remaining bare pointers to shared_ptr.
authorAlec Thomas <alec@swapoff.org>
Mon, 22 Oct 2012 18:16:43 +0000 (14:16 -0400)
committerAlec Thomas <alec@swapoff.org>
Mon, 22 Oct 2012 18:16:43 +0000 (14:16 -0400)
README.md
entityx/Entity.h
entityx/Entity_test.cc

index f0fbe4e5b101b92e79449eb2686b2c19a1936592..db4f140c03d98027f7737fb92c5df625e18e1a17 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,18 +1,20 @@
 # EntityX - A fast, type-safe C++ Entity-Component system
 
-Entity-Component (EC) systems decouple entity behavior from the entity objects themselves. The [Evolve your Hierarchy](http://cowboyprogramming.com/2007/01/05/evolve-your-heirachy/) article provides a solid overview of EC systems.
+Entity-Component (EC) systems are a form of decomposition that completely decouple entity logic and data from the entity "objects" themselves. The [Evolve your Hierarchy](http://cowboyprogramming.com/2007/01/05/evolve-your-heirachy/) article provides a solid overview of EC systems.
 
 EntityX is an EC system that uses C++11 features to provide type-safe component management, event delivery, etc.
 
 ## Overview
 
-In EntityX, data is associated with entities via components. Systems then use component data to implement behavior. Systems can utilize as many components as necessary. As an example, a physics system might need *position* and *mass* data, while a collision system might only need *position* - the data would be logically separated, but usable by any system.
+In EntityX data associated with an entity is called a `Component`. `Systems` use components to implement behavior and can utilize as many components as necessary. An `EventManager` allows systems to interact without being tightly coupled. Finally, a `World` object ties all of the systems together for convenience.
 
-Finally, an event system ties systems together, allowing them to interact without being tightly coupled.
+As an example, a physics system might need *position* and *mass* data, while a collision system might only need *position* - the data would be logically separated into two components, but usable by any system. The physics system might emit *collision* events whenever two entities collide.
+
+## Tutorial
 
 ### Entities
 
-Entities are simply 64-bit numeric identifiers with which component data is associated. Entity IDs are allocated by the `EntityManager`. Data can then be associated with an entity, and queried or retrieved directly.
+Entities are simply 64-bit numeric identifiers with which components are associated. Entity IDs are allocated by the `EntityManager`. Components are then associated with the entity, and can be queried or retrieved directly.
 
 Creating an entity is as simple as:
 
@@ -60,7 +62,7 @@ boost::shared_ptr<Position> position = boost::make_shared<Position>(1.0f, 2.0f);
 entities.assign(entity, position);
 ```
 
-#### Querying entities and components
+#### Querying entities and their components
 
 To retrieve a component associated with an entity use ``EntityManager::component()``:
 
@@ -71,11 +73,11 @@ if (position) {
 }
 ```
 
-To query all components with a set of components assigned use ``EntityManager::entities_with_components()``. This method will return only those entities that have *all* of the specified components associated with them, assigning the component pointer to the corresponding component pointer:
+To query all components with a set of components assigned use ``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:
 
 ```
-Position *position;
-Direction *direction;
+boost::shared_ptr<Position> position;
+boost::shared_ptr<Direction> direction;
 for (auto entity : entities.entities_with_components(position, direction)) {
   // Do things with entity ID, position and direction.
 }
@@ -83,7 +85,7 @@ for (auto entity : entities.entities_with_components(position, direction)) {
 
 ### Systems (implementing behavior)
 
-Systems implement behavior using one or more components. Implementations are [CRTP](http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern) subclasses of `System<T>` and *must* implement the `update()` method, as shown below.
+Systems implement behavior using one or more components. Implementations are subclasses of `System<T>` and *must* implement the `update()` method, as shown below.
 
 A basic movement system might be implemented with something like the following:
 
@@ -106,6 +108,8 @@ Events are objects emitted by systems, typically when some condition is met. Lis
 
 As an example, we might want to implement a very basic collision system using our ``Position` data from above.
 
+#### Creating event types
+
 First, we define the event type, which for our example is simply the two entities that collided:
 
 ```
@@ -116,13 +120,15 @@ struct Collision : public Event<Collision> {
 };
 ```
 
+#### Emitting events
+
 Next we implement our collision system, which emits ``Collision`` objects via an ``EventManager`` instance whenever two entities collide.
 
 ```
 class CollisionSystem : public System<CollisionSystem> {
  public:
   void update(EntityManager &es, EventManager &events, double dt) override {
-    Position *left_position, *right_position;
+    boost::shared_ptr<Position> left_position, right_position;
     for (auto left_entity : es.entities_with_components(left_position)) {
       for (auto right_entity : es.entities_with_components(right_position)) {
         if (collide(left_position, right_position)) {
@@ -134,7 +140,9 @@ class CollisionSystem : public System<CollisionSystem> {
 };
 ```
 
-Objects interested in receiving collision can subscribe to ``Collision`` events by first subclassing the CRTP class ``Receiver<T>``:
+#### Subscribing to events
+
+Objects interested in receiving collision information can subscribe to ``Collision`` events by first subclassing the CRTP class ``Receiver<T>``:
 
 ```
 struct DebugCollisions : public Receiver<DebugCollisions> {
@@ -144,6 +152,8 @@ struct DebugCollisions : public Receiver<DebugCollisions> {
 };
 ```
 
+Note that a single class can receive any number of types of events by implementing a ``receive(const EventType &)`` method for each event type.
+
 Finally, we subscribe our receiver to collision events:
 
 ```
index 6c0ab551a41013b1195134af62b4aeea26b075cd..356c773d5c4fae2838603822ec8bd5f05b3a03ce 100644 (file)
@@ -127,7 +127,7 @@ struct ComponentAddedEvent : public Event<ComponentAddedEvent<T>> {
  * e.assign<Movable>(player);
  * e.assign<Physical>(player);
  * e.assign<Scriptable>(player);
- * Controllable *controllable = e.assign<Controllable>(player);
+ * shared_ptr<Controllable> controllable = e.assign<Controllable>(player);
  */
 class EntityManager : boost::noncopyable {
  private:
@@ -216,19 +216,18 @@ class EntityManager : boost::noncopyable {
     const Iterator begin() const { return Iterator(manager_, predicates_, unpackers_, 0); }
     const Iterator end() const { return Iterator(manager_, predicates_, unpackers_, manager_.size()); }
 
-    // It's a bit less than ideal to mix this int othe View, but I couldn't find a way to separate concerns without
-    // vastly increasing the amount of code that had to be written.
     template <typename A>
-    void unpack_to(A *&a) {
+    View &unpack_to(boost::shared_ptr<A> &a) {
       unpackers_.push_back([&] (Entity id) {
-        a = manager_.component<A>(id).get();
+        a = manager_.component<A>(id);
       });
+      return *this;
     }
 
     template <typename A, typename B, typename ... Args>
-    void unpack_to(A *&a, B *&b, Args *& ... args) {
+    View &unpack_to(boost::shared_ptr<A> &a, boost::shared_ptr<B> &b, Args *& ... args) {
       unpack_to<A>(a);
-      unpack_to<B, Args ...>(b, args ...);
+      return unpack_to<B, Args ...>(b, args ...);
     }
    private:
     friend class EntityManager;
@@ -308,7 +307,7 @@ class EntityManager : boost::noncopyable {
   /**
    * Assign a Component to an Entity, optionally passing through Component constructor arguments.
    *
-   *   Position *position = em.assign<Position>(e, x, y);
+   *   shared_ptr<Position> position = em.assign<Position>(e, x, y);
    *
    * @returns Newly created component.
    */
@@ -328,7 +327,7 @@ class EntityManager : boost::noncopyable {
     if (C::family() >= entity_components_.size()) {
       return boost::shared_ptr<C>();
     }
-    auto &c = entity_components_.at(C::family()).at(id);
+    boost::shared_ptr<BaseComponent> c = entity_components_.at(C::family()).at(id);
     return boost::static_pointer_cast<C>(c);
   }
 
@@ -354,22 +353,22 @@ class EntityManager : boost::noncopyable {
    * Get all entities with the given component.
    */
   template <typename C>
-  View entities_with_components(C *&c) {
+  View entities_with_components(boost::shared_ptr<C> &c) {
     auto mask = component_mask<C>();
-    auto view = View(*this, View::ComponentMaskPredicate(entity_component_mask_, mask));
-    view.unpack_to<C>(c);
-    return view;
+    return
+        View(*this, View::ComponentMaskPredicate(entity_component_mask_, mask))
+        .unpack_to<C>(c);
   }
 
   /**
    * Find Entities that have all of the specified Components.
    */
   template <typename C1, typename C2, typename ... Components>
-  View entities_with_components(C1 *&c1, C2 *&c2, Components *& ... args) {
+  View entities_with_components(boost::shared_ptr<C1> &c1, boost::shared_ptr<C2> &c2, Components *& ... args) {
     auto mask = component_mask<C1, C2, Components ...>();
-    auto view = View(*this, View::ComponentMaskPredicate(entity_component_mask_, mask));
-    view.unpack_to<C1, C2, Components...>(c1, c2, args...);
-    return view;
+    return
+        View(*this, View::ComponentMaskPredicate(entity_component_mask_, mask))
+        .unpack_to<C1, C2, Components...>(c1, c2, args...);
   }
 
   /**
index 33f3773cec2dede8042397dd66f98eb7be7de3ff..aa1b32bf21ab5ef1ce269f9873a9f2b61a621b08 100644 (file)
@@ -159,20 +159,20 @@ TEST_F(EntityManagerTest, TestGetEntitiesWithComponentAndUnpacking) {
   position_directions.push_back(std::make_pair(
           em.assign<Position>(e, 1.0f, 2.0f),
           em.assign<Direction>(e, 3.0f, 4.0f)));
-  em.assign<Position>(f, 5.0f, 6.0f);
   position_directions.push_back(std::make_pair(
-          em.assign<Position>(g, 7.0f, 8.0f),
-          em.assign<Direction>(g, 9.0f, 10.0f)));
-  Position *position;
-  Direction *direction;
+          em.assign<Position>(f, 7.0f, 8.0f),
+          em.assign<Direction>(f, 9.0f, 10.0f)));
+  em.assign<Position>(g, 5.0f, 6.0f);
   int i = 0;
+  shared_ptr<Position> position;
+  shared_ptr<Direction> direction;
   for (auto unused_entity : em.entities_with_components(position, direction)) {
     (void)unused_entity;
-    ASSERT_TRUE(position != nullptr);
-    ASSERT_TRUE(direction != nullptr);
+    ASSERT_TRUE(position);
+    ASSERT_TRUE(direction);
     auto pd = position_directions.at(i);
-    ASSERT_EQ(*position, *pd.first);
-    ASSERT_EQ(*direction, *pd.second);
+    ASSERT_EQ(position, pd.first);
+    ASSERT_EQ(direction, pd.second);
     ++i;
   }
   ASSERT_EQ(2, i);
@@ -253,22 +253,38 @@ TEST_F(EntityManagerTest, TestComponentAddedEvent) {
   struct ComponentAddedEventReceiver : public Receiver<ComponentAddedEventReceiver> {
     void receive(const ComponentAddedEvent<Position> &event) {
       auto p = event.component;
-      float n = float(created.size());
+      float n = float(position_events);
       ASSERT_EQ(p->x, n);
       ASSERT_EQ(p->y, n);
-      created.push_back(event.entity);
+      position_events++;
     }
 
-    vector<Entity> created;
+    void receive(const ComponentAddedEvent<Direction> &event) {
+      auto p = event.component;
+      float n = float(direction_events);
+      ASSERT_EQ(p->x, -n);
+      ASSERT_EQ(p->y, -n);
+      direction_events++;
+    }
+
+    int position_events = 0;
+    int direction_events = 0;
   };
 
   ComponentAddedEventReceiver receiver;
   ev.subscribe<ComponentAddedEvent<Position>>(receiver);
+  ev.subscribe<ComponentAddedEvent<Direction>>(receiver);
 
-  ASSERT_EQ(0, receiver.created.size());
+  ASSERT_NE(ComponentAddedEvent<Position>::family(),
+            ComponentAddedEvent<Direction>::family());
+
+  ASSERT_EQ(0, receiver.position_events);
+  ASSERT_EQ(0, receiver.direction_events);
   for (int i = 0; i < 10; ++i) {
     Entity e = em.create();
     em.assign<Position>(e, float(i), float(i));
+    em.assign<Direction>(e, float(-i), float(-i));
   }
-  ASSERT_EQ(10, receiver.created.size());
+  ASSERT_EQ(10, receiver.position_events);
+  ASSERT_EQ(10, receiver.direction_events);
 };