aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlec Thomas <alec@swapoff.org>2012-10-22 14:16:43 -0400
committerAlec Thomas <alec@swapoff.org>2012-10-22 14:16:43 -0400
commitb547516621b71bf635f04c8e7ab52b86b1480771 (patch)
treec1b8a9e0679e7c87fd3b14c4e7312dc6a6b79931
parentec441e05616b3e9a5a384236b8c0ff536ed859e5 (diff)
Convert remaining bare pointers to shared_ptr.
-rw-r--r--README.md32
-rw-r--r--entityx/Entity.h33
-rw-r--r--entityx/Entity_test.cc44
3 files changed, 67 insertions, 42 deletions
diff --git a/README.md b/README.md
index f0fbe4e..db4f140 100644
--- 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:
```
diff --git a/entityx/Entity.h b/entityx/Entity.h
index 6c0ab55..356c773 100644
--- a/entityx/Entity.h
+++ b/entityx/Entity.h
@@ -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...);
}
/**
diff --git a/entityx/Entity_test.cc b/entityx/Entity_test.cc
index 33f3773..aa1b32b 100644
--- a/entityx/Entity_test.cc
+++ b/entityx/Entity_test.cc
@@ -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);
};