Continued working on docs.
#require(HAS_CXX11_CSTDINT_H "C++11 stdint support")
require(HAS_CXX11_VARIADIC_TEMPLATES "C++11 variadic templates")
require(HAS_CXX11_RVALUE_REFERENCES "C++11 rvalue references")
+require(HAS_CXX11_LONG_LONG "C++11 long long")
+require(HAS_CXX11_LONG_LONG "C++11 lambdas")
message("-- Checking misc features")
require(HAVE_STDINT_H "stdint.h")
## Overview
-In EntityX, data is associated with entities through components. This data is then used by systems to implement behavior. Behavior systems can utilize as many types of data 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 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.
-Finally, an event system ties behavior systems together, allowing them to interact without tight coupling.
+Finally, an event system ties systems together, allowing them to interact without being tightly coupled.
### Entities
-Entities are simply 64-bit numeric identifiers with which behaviors are associated. Entity IDs are allocated by the `EntityManager`, and all entities assigned particular types of data can be retrieved.
+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.
+
+Creating an entity is as simple as:
+
+```
+EntityManager entities;
+
+Entity entity = entities.create();
+```
### Components (entity data)
-Components are typically POD types containing self-contained sets of related data. Implementations are [CRTP](http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern) subclasses of `Component<T>`.
+Components are typically POD types containing self-contained sets of related data. Implementations are [curiously recurring template pattern](http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern) (CRTP) subclasses of `Component<T>`.
+
+#### Creating components
As an example, position and direction information might be represented as:
float x, y;
};
+```
+
+#### Assigning components to entities
+
+To associate a component with a previously created entity call ``EntityManager::assign<C>()`` with the component type, the entity, and any component constructor arguments:
+
+```
+// Assign a Position with x=1.0f and y=2.0f to "entity"
+entities.assign<Position>(entity, 1.0f, 2.0f);
+```
+
+You can also assign existing instances of components:
+
+```
+boost::shared_ptr<Position> position = boost::make_shared<Position>(1.0f, 2.0f);
+entities.assign(entity, position);
+```
+
+#### Querying entities and components
+
+To retrieve a component associated with an entity use ``EntityManager::component()``:
+
+```
+boost::shared_ptr<Position> position = entities.component<Position>();
+if (position) {
+ // Do stuff with 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:
+
+```
+Position *position;
+Direction *direction;
+for (auto entity : entities.entities_with_components(position, direction)) {
+ // Do things with entity ID, position and 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>`.
+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.
A basic movement system might be implemented with something like the following:
```
-class MovementSystem : public System<MovementSystem> {
- public:
- MovementSystem() {}
-
- void update(EntityManager &es, EventManager &events, double) override {
- auto entities = es.entities_with_components<Position, Direction>();
+struct MovementSystem : public System<MovementSystem> {
+ void update(EntityManager &es, EventManager &events, double dt) override {
Position *position;
Direction *direction;
- for (auto entity : entities) {
- es.unpack<Position, Direction>(entity, position, direction);
+ for (auto entity : es.entities_with_components(position, direction)) {
position->x += direction->x;
position->y += direction->y;
}
```
class CollisionSystem : public System<CollisionSystem> {
public:
- CollisionSystem(EventManager &events) : events_(events) {}
-
void update(EntityManager &es, EventManager &events, double dt) override {
Position *left_position, *right_position;
- auto left_entities = es.entities_with_components<Position>(),
- right_entities = es.entities_with_components<Position>();
-
- for (auto left_entity : left_entities) {
- es.unpack<Position>(left_entity, left_position);
- for (auto right_entity : right_entities) {
- es.unpack<Position>(right_entity, 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)) {
- events_.emit<Collision>(left_entity, right_entity);
+ events.emit<Collision>(left_entity, right_entity);
}
}
}
}
-
- private:
- EventManager &events_;
};
```
events.subscribe<Collision>(debug_collisions);
```
+### World (tying it all together)
+
## Installation
EntityX has the following build and runtime requirements:
template <typename Derived>
struct Component : public BaseComponent {
public:
- /**
- * Emitted when a component of this type is added to an entity.
- */
- class AddEvent : public Event<AddEvent> {
- public:
- AddEvent(Entity entity, boost::shared_ptr<Derived> component) :
- entity(entity), component(component) {}
-
- Entity entity;
- boost::shared_ptr<Derived> component;
- };
-
-
/// Used internally for registration.
static Family family() {
static Family family = family_counter_++;
/**
* Emitted when any component is added to an entity.
*/
-struct ComponentAddedEvent : public Event<ComponentAddedEvent> {
- ComponentAddedEvent(EntityManager &manager, Entity entity, boost::shared_ptr<BaseComponent> component) :
+template <typename T>
+struct ComponentAddedEvent : public Event<ComponentAddedEvent<T>> {
+ ComponentAddedEvent(EntityManager &manager, Entity entity, boost::shared_ptr<T> component) :
manager(manager), entity(entity), component(component) {}
EntityManager &manager;
Entity entity;
- boost::shared_ptr<BaseComponent> component;
+ boost::shared_ptr<T> component;
};
public:
EntityManager(EventManager &event_manager) : event_manager_(event_manager) {}
- class View {
- private:
+ class View {
public:
typedef boost::function<bool (EntityManager &, Entity)> Predicate;
private:
friend class View;
- Iterator(EntityManager &manager, const std::vector<Predicate> &predicates, Entity entity) : manager_(manager), predicates_(predicates), i_(entity) {
+ Iterator(EntityManager &manager, const std::vector<Predicate> &predicates,
+ const std::vector<boost::function<void (Entity)>> &unpackers, Entity entity)
+ : manager_(manager), predicates_(predicates), unpackers_(unpackers), i_(entity) {
next();
}
while (i_ < manager_.size() && !predicate()) {
++i_;
}
+ if (i_ < manager_.size()) {
+ unpack();
+ }
+ }
+
+ void unpack() {
+ for (auto unpacker : unpackers_) {
+ unpacker(i_);
+ }
}
bool predicate() {
EntityManager &manager_;
const std::vector<Predicate> predicates_;
+ std::vector<boost::function<void (Entity)>> unpackers_;
Entity i_;
};
predicates_.push_back(predicate);
}
- Iterator begin() { return Iterator(manager_, predicates_, 0); }
- Iterator end() { return Iterator(manager_, predicates_, manager_.size()); }
- const Iterator begin() const { return Iterator(manager_, predicates_, 0); }
- const Iterator end() const { return Iterator(manager_, predicates_, manager_.size()); }
+ Iterator begin() { return Iterator(manager_, predicates_, unpackers_, 0); }
+ Iterator end() { return Iterator(manager_, predicates_, unpackers_, manager_.size()); }
+ 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) {
+ unpackers_.push_back([&] (Entity id) {
+ a = manager_.component<A>(id).get();
+ });
+ }
+ template <typename A, typename B, typename ... Args>
+ void unpack_to(A *&a, B *&b, Args *& ... args) {
+ unpack_to<A>(a);
+ unpack_to<B, Args ...>(b, args ...);
+ }
private:
friend class EntityManager;
EntityManager &manager_;
std::vector<Predicate> predicates_;
+ std::vector<boost::function<void (Entity)>> unpackers_;
};
/**
entity_components_.at(C::family()).at(entity) = base;
entity_component_mask_.at(entity) |= uint64_t(1) << C::family();
- // TODO(alec): Figure out why this doesn't compile...gets an odd error about AddEvent not being a value.
- //event_manager_.emit<C::AddEvent>(entity, component);
- event_manager_.emit<ComponentAddedEvent>(*this, entity, base);
+ event_manager_.emit<ComponentAddedEvent<C>>(*this, entity, component);
return component;
}
return View(*this, View::ComponentMaskPredicate(entity_component_mask_, mask));
}
+ /**
+ * Get all entities with the given component.
+ */
+ template <typename C>
+ View entities_with_components(C *&c) {
+ auto mask = component_mask<C>();
+ auto view = View(*this, View::ComponentMaskPredicate(entity_component_mask_, mask));
+ view.unpack_to<C>(c);
+ return view;
+ }
+
+ /**
+ * 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) {
+ 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;
+ }
+
/**
* Unpack components directly into pointers.
*
* Position *p;
* Direction *d;
* unpack<Position, Direction>(e, p, d);
- *
- * Ideally this process would be more like:
- *
- * for (auto components : em.unpack_entities<Position, Direction>()) {
- * }
*/
template <typename A>
void unpack(Entity id, A *&a) {
*/
template <typename A, typename B, typename ... Args>
void unpack(Entity id, A *&a, B *&b, Args *& ... args) {
- a = component<A>(id).get();
+ unpack<A>(id, a);
unpack<B, Args ...>(id, b, args ...);
}
struct Position : Component<Position> {
Position(float x = 0.0f, float y = 0.0f) : x(x), y(y) {}
+ bool operator == (const Position &other) const { return x == other.x && y == other.y; }
+
float x, y;
};
+
+ostream &operator << (ostream &out, const Position &position) {
+ out << "Position(" << position.x << ", " << position.y << ")";
+ return out;
+}
+
+
struct Direction : Component<Direction> {
Direction(float x = 0.0f, float y = 0.0f) : x(x), y(y) {}
+ bool operator == (const Direction &other) const { return x == other.x && y == other.y; }
+
float x, y;
};
+ostream &operator << (ostream &out, const Direction &direction) {
+ out << "Direction(" << direction.x << ", " << direction.y << ")";
+ return out;
+}
+
+
class EntityManagerTest : public ::testing::Test {
protected:
EntityManagerTest() : em(ev) {}
ASSERT_EQ(25, size(em.entities_with_components<Direction, Position>()));
}
+TEST_F(EntityManagerTest, TestGetEntitiesWithComponentAndUnpacking) {
+ vector<Entity> entities;
+ Entity e = em.create();
+ Entity f = em.create();
+ Entity g = em.create();
+ std::vector<std::pair<boost::shared_ptr<Position>, boost::shared_ptr<Direction>>> position_directions;
+ 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;
+ int i = 0;
+ for (auto unused_entity : em.entities_with_components(position, direction)) {
+ (void)unused_entity;
+ ASSERT_TRUE(position != nullptr);
+ ASSERT_TRUE(direction != nullptr);
+ auto pd = position_directions.at(i);
+ ASSERT_EQ(*position, *pd.first);
+ ASSERT_EQ(*direction, *pd.second);
+ ++i;
+ }
+ ASSERT_EQ(2, i);
+}
+
TEST_F(EntityManagerTest, TestUnpack) {
Entity e = em.create();
auto p = em.assign<Position>(e);
ASSERT_NE(Position::family(), Direction::family());
}
-
TEST_F(EntityManagerTest, TestEntityCreatedEvent) {
struct EntityCreatedEventReceiver : public Receiver<EntityCreatedEventReceiver> {
void receive(const EntityCreatedEvent &event) {
ASSERT_EQ(10, receiver.created.size());
};
-
TEST_F(EntityManagerTest, TestEntityDestroyedEvent) {
struct EntityDestroyedEventReceiver : public Receiver<EntityDestroyedEventReceiver> {
void receive(const EntityDestroyedEvent &event) {
}
ASSERT_TRUE(entities == receiver.destroyed);
};
+
+TEST_F(EntityManagerTest, TestComponentAddedEvent) {
+ struct ComponentAddedEventReceiver : public Receiver<ComponentAddedEventReceiver> {
+ void receive(const ComponentAddedEvent<Position> &event) {
+ auto p = event.component;
+ float n = float(created.size());
+ ASSERT_EQ(p->x, n);
+ ASSERT_EQ(p->y, n);
+ created.push_back(event.entity);
+ }
+
+ vector<Entity> created;
+ };
+
+ ComponentAddedEventReceiver receiver;
+ ev.subscribe<ComponentAddedEvent<Position>>(receiver);
+
+ ASSERT_EQ(0, receiver.created.size());
+ for (int i = 0; i < 10; ++i) {
+ Entity e = em.create();
+ em.assign<Position>(e, float(i), float(i));
+ }
+ ASSERT_EQ(10, receiver.created.size());
+};
return s;
}
+ /**
+ * Retrieve the registered System instance, if any.
+ *
+ * boost::shared_ptr<CollisionSystem> collisions = systems.system<CollisionSystem>();
+ *
+ * @return System instance or empty shared_ptr<S>.
+ */
template <typename S>
boost::shared_ptr<S> system() {
auto it = systems_.find(S::family());
void World::run() {
running_ = true;
double dt;
+ timer_.restart();
while (running_) {
dt = timer_.elapsed();
timer_.restart();