project(EntityX)
include_directories(${CMAKE_CURRENT_LIST_DIR})
-set(RUN_BENCHMARKS false CACHE BOOL "Run benchmarks")
+set(ENTITYX_BUILD_TESTING false CACHE BOOL "Enable building of tests.")
+set(ENTITYX_RUN_BENCHMARKS false CACHE BOOL "Run benchmarks (in conjunction with -DENTITYX_BUILD_TESTING=1).")
+set(ENTITYX_MAX_COMPONENTS 64 CACHE STRING "Set the maximum number of components.")
+set(ENTITYX_USE_CPP11_STDLIB false CACHE BOOL "Use the C++11 stdlib (-stdlib=libc++).")
+# Check for which shared_ptr implementation to use.
+set(ENTITYX_USE_STD_SHARED_PTR false CACHE BOOL "Use std::shared_ptr<T> rather than boost::shared_ptr<T>?")
include(${CMAKE_ROOT}/Modules/CheckIncludeFile.cmake)
include(CheckCXXSourceCompiles)
# Misc features
check_include_file("stdint.h" HAVE_STDINT_H)
-set(USE_CPP11_STDLIB false CACHE BOOL "Use the C++11 stdlib (-stdlib=libc++).")
-
-if (USE_CPP11_STDLIB)
- set(OLD_CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
- check_cxx_source_compiles(
- "
- #include <memory>
-
- int main() {
- std::shared_ptr<int>();
- }
- "
- HAVE_CXX11_STDLIB
- )
-
- if (NOT HAVE_CXX11_STDLIB)
- message("-- Not using -stdlib=libc++ (test failed to build)")
- set(CMAKE_CXX_FLAGS "${OLD_CMAKE_CXX_FLAGS}")
- else ()
- message("-- Using -stdlib=libc++")
- endif ()
-else ()
- message("-- Using default stdlib (try -DUSE_CPP11_STDLIB=1 to use -stdlib=libc++)")
-endif ()
-
-# Check for which shared_ptr implementation to use.
-set(USE_STD_SHARED_PTR false CACHE BOOL "Use std::shared_ptr<T> rather than boost::shared_ptr<T>?")
-
-check_cxx_source_compiles(
-"
-#include <memory>
-
-int main() { std::shared_ptr<int>(); }
-"
-HAVE_STD_SHARED_PTR
-)
-
-check_cxx_source_compiles(
-"
-#include <boost/shared_ptr.hpp>
-
-int main() { boost::shared_ptr<int>(); }
-"
-HAVE_BOOST_SHARED_PTR
-)
-
-if (HAVE_STD_SHARED_PTR AND USE_STD_SHARED_PTR)
- message("-- Using std::shared_ptr<T>")
-else()
- if (USE_STD_SHARED_PTR)
- message("-- Using boost::shared_ptr<T> (std::shared_ptr<T> could not be used)")
- else()
- message("-- Using boost::shared_ptr<T> (try -DUSE_STD_SHARED_PTR=1 to use std::shared_ptr<T>)")
- endif()
-endif()
-
-configure_file(
- ${CMAKE_CURRENT_SOURCE_DIR}/entityx/config.h.in
- ${CMAKE_CURRENT_SOURCE_DIR}/entityx/config.h
-)
-
macro(require FEATURE_NAME MESSAGE_STRING)
if (NOT ${${FEATURE_NAME}})
message(FATAL_ERROR "${MESSAGE_STRING} required -- ${${FEATURE_NAME}}")
set(Boost_USE_MULTITHREADED ON)
set(Boost_USE_STATIC_RUNTIME OFF)
find_package(Boost 1.48.0 REQUIRED COMPONENTS signals)
+include_directories(${Boost_INCLUDE_DIR})
+
+include(CheckCXX11SharedPtr.cmake)
set(sources entityx/System.cc entityx/Event.cc entityx/Entity.cc entityx/Manager.cc)
add_library(entityx STATIC ${sources})
entityx_shared
${Boost_SIGNALS_LIBRARY}
)
-set_target_properties(entityx_shared PROPERTIES OUTPUT_NAME entityx)
-include_directories(
- ${Boost_INCLUDE_DIR}
- ${GTest_INCLUDE_DIR}
- )
+set_target_properties(entityx_shared PROPERTIES OUTPUT_NAME entityx)
-set(BUILD_TESTING false CACHE BOOL "Enable building of tests")
-if (BUILD_TESTING)
+if (ENTITYX_BUILD_TESTING)
find_package(Boost 1.48.0 REQUIRED COMPONENTS signals timer system)
add_subdirectory(gtest-1.6.0)
include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR})
create_test(event_test entityx/Event_test.cc)
create_test(system_test entityx/System_test.cc)
create_test(tags_component_test entityx/tags/TagsComponent_test.cc)
- if (RUN_BENCHMARKS)
+ if (ENTITYX_RUN_BENCHMARKS)
message("-- Running benchmarks")
+ add_definitions(-DGTEST_USE_OWN_TR1_TUPLE=1 -DBOOST_NO_CXX11_NUMERIC_LIMITS=1)
create_test(benchmarks_test entityx/Benchmarks_test.cc)
else ()
- message("-- Not running benchmarks (use -DRUN_BENCHMARKS=1 to enable)")
+ message("-- Not running benchmarks (use -DENTITYX_RUN_BENCHMARKS=1 to enable)")
endif ()
-endif (BUILD_TESTING)
+endif (ENTITYX_BUILD_TESTING)
file(GLOB headers "${CMAKE_CURRENT_SOURCE_DIR}/entityx/*.h")
--- /dev/null
+cmake_minimum_required(VERSION 2.8.3)
+
+if (ENTITYX_USE_CPP11_STDLIB)
+ add_definitions(-DGTEST_USE_OWN_TR1_TUPLE=1)
+ set(OLD_CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
+ check_cxx_source_compiles(
+ "
+ #include <memory>
+
+ int main() {
+ std::shared_ptr<int>();
+ }
+ "
+ ENTITYX_HAVE_CXX11_STDLIB
+ )
+
+ if (NOT ENTITYX_HAVE_CXX11_STDLIB)
+ message("-- Not using -stdlib=libc++ (test failed to build)")
+ set(CMAKE_CXX_FLAGS "${OLD_CMAKE_CXX_FLAGS}")
+ else ()
+ message("-- Using -stdlib=libc++")
+ endif ()
+else ()
+ message("-- Using default stdlib (try -DENTITYX_USE_CPP11_STDLIB=1 to use -stdlib=libc++)")
+endif ()
+
+check_cxx_source_compiles(
+"
+#include <memory>
+
+int main() { std::shared_ptr<int>(); }
+"
+ENTITYX_HAVE_STD_SHARED_PTR
+)
+
+check_cxx_source_compiles(
+"
+#include <boost/shared_ptr.hpp>
+
+int main() { boost::shared_ptr<int>(); }
+"
+ENTITYX_HAVE_BOOST_SHARED_PTR
+)
+
+if (ENTITYX_HAVE_STD_SHARED_PTR AND ENTITYX_USE_STD_SHARED_PTR)
+ message("-- Using std::shared_ptr<T>")
+else()
+ if (ENTITYX_USE_STD_SHARED_PTR)
+ message("-- Using boost::shared_ptr<T> (std::shared_ptr<T> could not be used)")
+ else()
+ message("-- Using boost::shared_ptr<T> (try -DENTITYX_USE_STD_SHARED_PTR=1 to use std::shared_ptr<T>)")
+ endif()
+endif()
+
+configure_file(
+ ${CMAKE_CURRENT_SOURCE_DIR}/entityx/config.h.in
+ ${CMAKE_CURRENT_SOURCE_DIR}/entityx/config.h
+)
+
EntityX has the following build and runtime requirements:
-- A C++ compiler that supports a basic set of C++11 features (ie. recent clang, recent gcc, but **NOT** Visual C++).
+- A C++ compiler that supports a basic set of C++11 features (ie. recent clang, recent gcc, and maybe (untested) VC++ with the [Nov 2012 CTP](http://www.microsoft.com/en-us/download/details.aspx?id=35515)).
- [CMake](http://cmake.org/)
- [Boost](http://boost.org) `1.48.0` or higher (links against `boost::signals`).
-- [Glog](http://code.google.com/p/google-glog/) (tested with `0.3.2`).
-- [GTest](http://code.google.com/p/googletest/) (needed for testing only)
-**Note:** GTest is no longer installable directly through Homebrew. You can use [this formula](https://raw.github.com/mxcl/homebrew/2bf506e3d90254f81a669a0216f33b2f09589028/Library/Formula/gtest.rb) to install it manually.
-For Debian Linux, install libgtest-dev and then see /usr/share/doc/libgtest-dev/README.Debian.
+Once these dependencies are installed you should be able to build and install EntityX as below. The following options can be passed to cmake to modify how EntityX is built:
-Once these dependencies are installed you should be able to build and install EntityX as follows. BUILD_TESTING is false by default.
+- `-DENTITYX_BUILD_TESTING=1` - Build tests (run with `make test`).
+- `-DENTITYX_RUN_BENCHMARKS=1` - In conjunction with `-DENTITYX_BUILD_TESTING=1`, also build benchmarks.
+- `-DENTITYX_USE_CPP11_STDLIB=1` - For Clang, specify whether to use `-stdlib=libc++`.
+- `-DENTITYX_USE_STD_SHARED_PTR=1` - Use `std::shared_ptr<T>` (and friends) rather than the Boost equivalents. This does not eliminate the need for Boost, but is useful if the rest of your application uses `std::shared_ptr<T>`.
-```c++
+For a production build, you'll typically only need the `-DENTITYX_USE_STD_SHARED_PTR=1` flag, if any.
+
+Once you have selected your flags, build and install with:
+
+```sh
mkdir build
cd build
-cmake [-DBUILD_TESTING=true] ..
+cmake [-DENTITYX_BUILD_TESTING=1] [-DENTITYX_RUN_BENCHMARKS=1] [-DENTITYX_USE_CPP11_STDLIB=1] [-DENTITYX_USE_STD_SHARED_PTR=1] ..
make
make install
```
class BenchmarksTest : public ::testing::Test {
protected:
- BenchmarksTest() : em(ev) {}
+ BenchmarksTest() : ev(EventManager::make()), em(EntityManager::make(ev)) {}
- EventManager ev;
- EntityManager em;
+ entityx::shared_ptr<EventManager> ev;
+ entityx::shared_ptr<EntityManager> em;
};
cout << "creating " << count << " entities" << endl;
for (uint64_t i = 0; i < count; i++) {
- em.create();
+ em->create();
}
}
uint64_t count = 10000000L;
vector<Entity> entities;
for (uint64_t i = 0; i < count; i++) {
- entities.push_back(em.create());
+ entities.push_back(em->create());
}
boost::timer::auto_cpu_timer t;
TEST_F(BenchmarksTest, TestCreateEntitiesWithListener) {
Listener listen;
- ev.subscribe<EntityCreatedEvent>(listen);
+ ev->subscribe<EntityCreatedEvent>(listen);
uint64_t count = 10000000L;
vector<Entity> 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<EntityDestroyedEvent>(listen);
+ ev->subscribe<EntityDestroyedEvent>(listen);
uint64_t count = 10000000L;
vector<Entity> entities;
for (uint64_t i = 0; i < count; i++) {
- entities.push_back(em.create());
+ entities.push_back(em->create());
}
boost::timer::auto_cpu_timer t;
uint64_t count = 10000000L;
vector<Entity> entities;
for (uint64_t i = 0; i < count; i++) {
- auto e = em.create();
+ auto e = em->create();
e.assign<Position>();
entities.push_back(e);
}
cout << "iterating over " << count << " entities with a component 10 times" << endl;
for (int i = 0; i < 10; ++i) {
- for (auto e : em.entities_with_components<Position>()) {
+ for (auto e : em->entities_with_components<Position>()) {
entityx::shared_ptr<Position> position = e.component<Position>();
}
}
void Entity::invalidate() {
id_ = INVALID;
- manager_ = nullptr;
+ manager_.reset();
}
void Entity::destroy() {
- manager_->destroy(id_);
+ manager_.lock()->destroy(id_);
invalidate();
}
}
bool operator == (const Entity &other) const {
- return other.manager_ == manager_ && other.id_ == id_;
+ return other.manager_.lock() == manager_.lock() && other.id_ == id_;
}
bool operator != (const Entity &other) const {
- return other.manager_ != manager_ || other.id_ != id_;
+ return !(other == *this);
}
/**
* the same, or is valid in any way.
*/
bool valid() const {
- return manager_ != nullptr && id_ != INVALID;
+ return !manager_.expired() && id_ != INVALID;
}
/**
void invalidate();
Id id() const { return id_; }
- EntityManager &manager() { return *manager_; }
template <typename C>
entityx::shared_ptr<C> assign(entityx::shared_ptr<C> component);
private:
friend class EntityManager;
- Entity(EntityManager *entities, Entity::Id id) : manager_(entities), id_(id) {}
+ Entity(entityx::shared_ptr<EntityManager> manager, Entity::Id id) : manager_(manager), id_(id) {}
- EntityManager *manager_ = nullptr;
+ entityx::weak_ptr<EntityManager> manager_;
Entity::Id id_ = INVALID;
};
/**
* Manages Entity::Id creation and component assignment.
- *
- * eg.
- * EntityManager e;
- *
- * Entity player = e.create();
- *
- * player.assign<Movable>();
- * player.assign<Physical>();
- * player.assign<Scriptable>();
- * shared_ptr<Controllable> controllable = player.assign<Controllable>();
*/
-class EntityManager : boost::noncopyable {
+class EntityManager : public entityx::enable_shared_from_this<EntityManager>, boost::noncopyable {
public:
- static const int MAX_COMPONENTS = 64;
-
- typedef std::bitset<MAX_COMPONENTS> ComponentMask;
+ typedef std::bitset<entityx::MAX_COMPONENTS> ComponentMask;
- EntityManager(EventManager &event_manager) : event_manager_(event_manager) {}
+ static entityx::shared_ptr<EntityManager> make(entityx::shared_ptr<EventManager> event_manager) {
+ return entityx::shared_ptr<EntityManager>(new EntityManager(event_manager));
+ }
class View {
public:
- typedef boost::function<bool (EntityManager &, Entity::Id)> Predicate;
+ typedef boost::function<bool (entityx::shared_ptr<EntityManager>, Entity::Id)> Predicate;
/// A predicate that excludes entities that don't match the given component mask.
class ComponentMaskPredicate {
public:
ComponentMaskPredicate(const std::vector<ComponentMask> &entity_bits, ComponentMask mask) : entity_bits_(entity_bits), mask_(mask) {}
- bool operator () (EntityManager &, Entity::Id entity) {
+ bool operator () (entityx::shared_ptr<EntityManager>, Entity::Id entity) {
return (entity_bits_.at(entity) & mask_) == mask_;
}
private:
friend class View;
- Iterator() : manager_(nullptr) {}
+ Iterator() {}
- Iterator(EntityManager *manager, const std::vector<Predicate> &predicates,
+ Iterator(entityx::shared_ptr<EntityManager> manager, const std::vector<Predicate> &predicates,
const std::vector<boost::function<void (Entity::Id)>> &unpackers, Entity::Id entity)
: manager_(manager), predicates_(predicates), unpackers_(unpackers), i_(entity) {
next();
bool predicate() {
for (auto &p : predicates_) {
- if (!p(*manager_, i_)) {
+ if (!p(manager_, i_)) {
return false;
}
}
return true;
}
- EntityManager *manager_;
+ entityx::shared_ptr<EntityManager> manager_;
const std::vector<Predicate> predicates_;
std::vector<boost::function<void (Entity::Id)>> unpackers_;
Entity::Id i_;
template <typename T>
struct Unpacker {
- Unpacker(EntityManager *manager, entityx::shared_ptr<T> &c) : manager_(manager), c(c) {}
+ Unpacker(entityx::shared_ptr<EntityManager> manager, entityx::shared_ptr<T> &c) : manager_(manager), c(c) {}
void operator () (Entity::Id id) {
c = manager_->component<T>(id);
}
private:
- EntityManager *manager_;
+ entityx::shared_ptr<EntityManager> manager_;
entityx::shared_ptr<T> &c;
};
- View(EntityManager *manager, Predicate predicate) : manager_(manager) {
+ View(entityx::shared_ptr<EntityManager> manager, Predicate predicate) : manager_(manager) {
predicates_.push_back(predicate);
}
- EntityManager *manager_;
+ entityx::shared_ptr<EntityManager> manager_;
std::vector<Predicate> predicates_;
std::vector<boost::function<void (Entity::Id)>> unpackers_;
};
id = free_list_.front();
free_list_.pop_front();
}
- event_manager_.emit<EntityCreatedEvent>(Entity(this, id));
- return Entity(this, id);
+ event_manager_->emit<EntityCreatedEvent>(Entity(shared_from_this(), id));
+ return Entity(shared_from_this(), id);
}
/**
*/
void destroy(Entity::Id entity) {
assert(entity < entity_component_mask_.size() && "Entity::Id ID outside entity vector range");
- event_manager_.emit<EntityDestroyedEvent>(Entity(this, entity));
+ event_manager_->emit<EntityDestroyedEvent>(Entity(shared_from_this(), entity));
for (auto &components : entity_components_) {
components.at(entity).reset();
}
}
Entity get(Entity::Id id) {
- return Entity(this, id);
+ return Entity(shared_from_this(), id);
}
/**
entity_components_.at(C::family()).at(entity) = base;
entity_component_mask_.at(entity) |= uint64_t(1) << C::family();
- event_manager_.emit<ComponentAddedEvent<C>>(Entity(this, entity), component);
+ event_manager_->emit<ComponentAddedEvent<C>>(Entity(shared_from_this(), entity), component);
return component;
}
template <typename C, typename ... Components>
View entities_with_components() {
auto mask = component_mask<C, Components ...>();
- return View(this, View::ComponentMaskPredicate(entity_component_mask_, mask));
+ return View(shared_from_this(), View::ComponentMaskPredicate(entity_component_mask_, mask));
}
/**
View entities_with_components(entityx::shared_ptr<C> &c, Components && ... args) {
auto mask = component_mask(c, args ...);
return
- View(this, View::ComponentMaskPredicate(entity_component_mask_, mask))
+ View(shared_from_this(), View::ComponentMaskPredicate(entity_component_mask_, mask))
.unpack_to(c, args ...);
}
}
private:
+ EntityManager(entityx::shared_ptr<EventManager> event_manager) : event_manager_(event_manager) {}
+
+
template <typename C>
ComponentMask component_mask() {
ComponentMask mask;
Entity::Id id_counter_ = 0;
- EventManager &event_manager_;
+ entityx::shared_ptr<EventManager> event_manager_;
// A nested array of: components = entity_components_[family][entity]
std::vector<std::vector<entityx::shared_ptr<BaseComponent>>> entity_components_;
// Bitmask of components associated with each entity. Index into the vector is the Entity::Id.
template <typename C>
BaseComponent::Family Component<C>::family() {
static Family family = family_counter_++;
- assert(family < EntityManager::MAX_COMPONENTS);
+ assert(family < entityx::MAX_COMPONENTS);
return family;
}
template <typename C>
entityx::shared_ptr<C> Entity::assign(entityx::shared_ptr<C> component) {
- return manager_->assign<C>(id_, component);
+ return manager_.lock()->assign<C>(id_, component);
}
template <typename C, typename ... Args>
entityx::shared_ptr<C> Entity::assign(Args && ... args) {
- return manager_->assign<C>(id_, args ...);
+ return manager_.lock()->assign<C>(id_, args ...);
}
template <typename C>
entityx::shared_ptr<C> Entity::component() {
- return manager_->component<C>(id_);
+ return manager_.lock()->component<C>(id_);
}
template <typename A>
void Entity::unpack(entityx::shared_ptr<A> &a) {
- manager_->unpack(id_, a);
+ manager_.lock()->unpack(id_, a);
}
template <typename A, typename B, typename ... Args>
void Entity::unpack(entityx::shared_ptr<A> &a, entityx::shared_ptr<B> &b, Args && ... args) {
- manager_->unpack(id_, a, b, args ...);
+ manager_.lock()->unpack(id_, a, b, args ...);
}
}
#include <iterator>
#include <string>
#include <vector>
-#include <boost/ref.hpp>
#include <gtest/gtest.h>
#include "entityx/Entity.h"
class EntityManagerTest : public ::testing::Test {
protected:
- EntityManagerTest() : em(ev) {}
+ EntityManagerTest() : ev(EventManager::make()), em(EntityManager::make(ev)) {}
- EventManager ev;
- EntityManager em;
+ entityx::shared_ptr<EventManager> ev;
+ entityx::shared_ptr<EntityManager> em;
virtual void SetUp() {
}
TEST_F(EntityManagerTest, TestCreateEntity) {
- ASSERT_TRUE(em.size() == 0);
+ ASSERT_TRUE(em->size() == 0);
Entity e2;
ASSERT_FALSE(e2.valid());
- Entity e = em.create();
+ Entity e = em->create();
ASSERT_TRUE(e.valid());
- ASSERT_TRUE(em.size() == 1);
+ ASSERT_TRUE(em->size() == 1);
e2 = e;
ASSERT_TRUE(e2.valid());
}
TEST_F(EntityManagerTest, TestEntityAsBoolean) {
- ASSERT_TRUE(em.size() == 0);
- Entity e = em.create();
+ ASSERT_TRUE(em->size() == 0);
+ Entity e = em->create();
ASSERT_TRUE(e.valid());
- ASSERT_TRUE(em.size() == 1);
+ ASSERT_TRUE(em->size() == 1);
ASSERT_FALSE(!e);
e.destroy();
}
TEST_F(EntityManagerTest, TestEntityReuse) {
- Entity e1 = em.create();
+ Entity e1 = em->create();
auto id = e1.id();
e1.destroy();
- Entity e2 = em.create();
+ Entity e2 = em->create();
// It is assumed that the allocation will reuse the same entity id.
ASSERT_EQ(e2.id(), id);
}
TEST_F(EntityManagerTest, TestComponentConstruction) {
- auto e = em.create();
+ auto e = em->create();
auto p = e.assign<Position>(1, 2);
- //auto p = em.assign<Position>(e, 1, 2);
+ //auto p = em->assign<Position>(e, 1, 2);
auto cp = e.component<Position>();
ASSERT_EQ(p, cp);
ASSERT_FLOAT_EQ(1.0, cp->x);
}
TEST_F(EntityManagerTest, TestComponentCreationWithObject) {
- auto e = em.create();
- auto p = e.assign(make_shared<Position>(1.0, 2.0));
+ auto e = em->create();
+ auto p = e.assign(entityx::make_shared<Position>(1.0, 2.0));
auto cp = e.component<Position>();
ASSERT_EQ(p, cp);
ASSERT_FLOAT_EQ(1.0, cp->x);
}
TEST_F(EntityManagerTest, TestDestroyEntity) {
- Entity e = em.create();
- Entity f = em.create();
+ Entity e = em->create();
+ Entity f = em->create();
auto ep = e.assign<Position>();
f.assign<Position>();
e.assign<Direction>();
ASSERT_EQ(2, ep.use_count());
ASSERT_TRUE(e.valid());
ASSERT_TRUE(f.valid());
- ASSERT_TRUE(e.component<Position>());
- ASSERT_TRUE(e.component<Direction>());
- ASSERT_TRUE(f.component<Position>());
- ASSERT_TRUE(f.component<Direction>());
+ ASSERT_TRUE(bool(e.component<Position>()));
+ ASSERT_TRUE(bool(e.component<Direction>()));
+ ASSERT_TRUE(bool(f.component<Position>()));
+ ASSERT_TRUE(bool(f.component<Direction>()));
e.destroy();
ASSERT_FALSE(e.valid());
ASSERT_TRUE(f.valid());
- ASSERT_TRUE(f.component<Position>());
- ASSERT_TRUE(f.component<Direction>());
+ ASSERT_TRUE(bool(f.component<Position>()));
+ ASSERT_TRUE(bool(f.component<Direction>()));
ASSERT_EQ(1, ep.use_count());
}
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<Position>();
e.assign<Direction>();
f.assign<Position>();
g.assign<Position>();
- ASSERT_EQ(3, size(em.entities_with_components<Position>()));
- ASSERT_EQ(1, size(em.entities_with_components<Direction>()));
+ ASSERT_EQ(3, size(em->entities_with_components<Position>()));
+ ASSERT_EQ(1, size(em->entities_with_components<Direction>()));
}
TEST_F(EntityManagerTest, TestGetEntitiesWithIntersectionOfComponents) {
vector<Entity> 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<Position>();
e.assign<Direction>();
}
- ASSERT_EQ(50, size(em.entities_with_components<Direction>()));
- ASSERT_EQ(75, size(em.entities_with_components<Position>()));
- ASSERT_EQ(25, size(em.entities_with_components<Direction, Position>()));
+ ASSERT_EQ(50, size(em->entities_with_components<Direction>()));
+ ASSERT_EQ(75, size(em->entities_with_components<Position>()));
+ ASSERT_EQ(25, size(em->entities_with_components<Direction, Position>()));
}
TEST_F(EntityManagerTest, TestGetEntitiesWithComponentAndUnpacking) {
vector<Entity::Id> entities;
- Entity e = em.create();
- Entity f = em.create();
- Entity g = em.create();
- std::vector<std::pair<shared_ptr<Position>, shared_ptr<Direction>>> position_directions;
+ Entity e = em->create();
+ Entity f = em->create();
+ Entity g = em->create();
+ std::vector<std::pair<entityx::shared_ptr<Position>, entityx::shared_ptr<Direction>>> position_directions;
position_directions.push_back(std::make_pair(
e.assign<Position>(1.0f, 2.0f),
e.assign<Direction>(3.0f, 4.0f)));
g.assign<Position>(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)) {
+ entityx::shared_ptr<Position> position;
+ entityx::shared_ptr<Direction> direction;
+ for (auto unused_entity : em->entities_with_components(position, direction)) {
(void)unused_entity;
- ASSERT_TRUE(position);
- ASSERT_TRUE(direction);
+ ASSERT_TRUE(bool(position));
+ ASSERT_TRUE(bool(direction));
auto pd = position_directions.at(i);
ASSERT_EQ(position, pd.first);
ASSERT_EQ(direction, pd.second);
}
TEST_F(EntityManagerTest, TestUnpack) {
- Entity e = em.create();
+ Entity e = em->create();
auto p = e.assign<Position>();
auto d = e.assign<Direction>();
- shared_ptr<Position> up;
- shared_ptr<Direction> ud;
+ entityx::shared_ptr<Position> up;
+ entityx::shared_ptr<Direction> ud;
e.unpack<Position, Direction>(up, ud);
ASSERT_EQ(p, up);
ASSERT_EQ(d, ud);
struct NullDeleter {template<typename T> void operator()(T*) {} };
TEST_F(EntityManagerTest, TestUnpackNullMissing) {
- Entity e = em.create();
+ Entity e = em->create();
auto p = e.assign<Position>();
- shared_ptr<Position> up(reinterpret_cast<Position*>(0Xdeadbeef), NullDeleter());
- shared_ptr<Direction> ud(reinterpret_cast<Direction*>(0Xdeadbeef), NullDeleter());
+ entityx::shared_ptr<Position> up(reinterpret_cast<Position*>(0Xdeadbeef), NullDeleter());
+ entityx::shared_ptr<Direction> ud(reinterpret_cast<Direction*>(0Xdeadbeef), NullDeleter());
e.unpack<Position, Direction>(up, ud);
ASSERT_EQ(p, up);
- ASSERT_EQ(shared_ptr<Direction>(), ud);
+ ASSERT_EQ(entityx::shared_ptr<Direction>(), ud);
}
TEST_F(EntityManagerTest, TestComponentIdsDiffer) {
};
EntityCreatedEventReceiver receiver;
- ev.subscribe<EntityCreatedEvent>(receiver);
+ ev->subscribe<EntityCreatedEvent>(receiver);
ASSERT_EQ(0UL, receiver.created.size());
for (int i = 0; i < 10; ++i) {
- em.create();
+ em->create();
}
ASSERT_EQ(10UL, receiver.created.size());
}
};
EntityDestroyedEventReceiver receiver;
- ev.subscribe<EntityDestroyedEvent>(receiver);
+ ev->subscribe<EntityDestroyedEvent>(receiver);
ASSERT_EQ(0UL, receiver.destroyed.size());
vector<Entity> 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) {
};
ComponentAddedEventReceiver receiver;
- ev.subscribe<ComponentAddedEvent<Position>>(receiver);
- ev.subscribe<ComponentAddedEvent<Direction>>(receiver);
+ ev->subscribe<ComponentAddedEvent<Position>>(receiver);
+ ev->subscribe<ComponentAddedEvent<Direction>>(receiver);
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();
+ Entity e = em->create();
e.assign<Position>(float(i), float(i));
e.assign<Direction>(float(-i), float(-i));
}
TEST_F(EntityManagerTest, TestEntityAssignment) {
Entity a, b;
- a = em.create();
+ a = em->create();
ASSERT_NE(a, b);
b = a;
ASSERT_EQ(a, b);
std::list<boost::signals::connection> connections_;
};
+
template <typename Derived>
class Receiver : public BaseReceiver {
public:
*
* Subscriptions are automatically removed when receivers are destroyed..
*/
-class EventManager : boost::noncopyable {
+class EventManager : public entityx::enable_shared_from_this<EventManager>, boost::noncopyable {
public:
+ static entityx::shared_ptr<EventManager> make() {
+ return entityx::shared_ptr<EventManager>(new EventManager());
+ }
+
/**
* Subscribe an object to receive events of type E.
*
*
* eg.
*
- * EntityManager em;
- * em.emit<Explosion>(10);
+ * entityx::shared_ptr<EventManager> em(entityx::make_shared<EventManager>());
+ * em->emit<Explosion>(10);
*
*/
template <typename E, typename ... Args>
typedef boost::signal<void (const BaseEvent*)> EventSignal;
typedef entityx::shared_ptr<EventSignal> EventSignalPtr;
+ EventManager() {}
+
EventSignalPtr signal_for(int id) {
auto it = handlers_.find(id);
if (it == handlers_.end()) {
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution.
- *
+ *
* Author: Alec Thomas <alec@swapoff.org>
*/
};
TEST(EventManagerTest, TestEmitReceive) {
- EventManager em;
+ auto em = EventManager::make();
ExplosionSystem explosion_system;
- em.subscribe<Explosion>(explosion_system);
+ em->subscribe<Explosion>(explosion_system);
ASSERT_EQ(0, explosion_system.damage_received);
- em.emit<Explosion>(10);
+ em->emit<Explosion>(10);
ASSERT_EQ(10, explosion_system.damage_received);
}
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution.
- *
+ *
* Author: Alec Thomas <alec@swapoff.org>
*/
void Manager::start() {
configure();
- system_manager.configure();
+ system_manager->configure();
initialize();
}
class Manager {
public:
- Manager() : entity_manager(event_manager), system_manager(entity_manager, event_manager) {}
+ Manager() :
+ event_manager(EventManager::make()),
+ entity_manager(EntityManager::make(event_manager)),
+ system_manager(SystemManager::make(entity_manager, event_manager)) {}
virtual ~Manager() {}
void start();
*/
virtual void update(double dt) = 0;
- EventManager event_manager;
- EntityManager entity_manager;
- SystemManager system_manager;
+ entityx::shared_ptr<EventManager> event_manager;
+ entityx::shared_ptr<EntityManager> entity_manager;
+ entityx::shared_ptr<SystemManager> system_manager;
private:
boost::timer timer_;
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution.
- *
+ *
* Author: Alec Thomas <alec@swapoff.org>
*/
void SystemManager::configure() {
for (auto pair : systems_) {
- pair.second->configure(events_);
+ pair.second->configure(event_manager_);
}
initialized_ = true;
}
*
* Typically used to set up event handlers.
*/
- virtual void configure(EventManager &events) {}
+ virtual void configure(entityx::shared_ptr<EventManager> events) {}
/**
* Apply System behavior.
*
* Called every game step.
*/
- virtual void update(EntityManager &entities, EventManager &events, double dt) = 0;
+ virtual void update(entityx::shared_ptr<EntityManager> entities, entityx::shared_ptr<EventManager> events, double dt) = 0;
static Family family_counter_;
* Use this class when implementing Systems.
*
* struct MovementSystem : public System<MovementSystem> {
- * void update(EntityManager &entities, EventManager &events, double dt) {
+ * void update(entityx::shared_ptr<EntityManager> entities, EventManager &events, double dt) {
* // Do stuff to/with entities...
* }
* }
};
-class SystemManager : boost::noncopyable {
+class SystemManager : public entityx::enable_shared_from_this<SystemManager>, boost::noncopyable {
public:
- SystemManager(EntityManager &entities, EventManager &events) : entities_(entities), events_(events) {}
+ static entityx::shared_ptr<SystemManager> make(entityx::shared_ptr<EntityManager> entity_manager,
+ entityx::shared_ptr<EventManager> event_manager) {
+ return entityx::shared_ptr<SystemManager>(new SystemManager(entity_manager, event_manager));
+ }
/**
* Add a System to the SystemManager.
void update(double dt) {
assert(initialized_ && "SystemManager::configure() not called");
entityx::shared_ptr<S> s = system<S>();
- s->update(entities_, events_, dt);
+ s->update(entity_manager_, event_manager_, dt);
}
/**
void configure();
private:
+ SystemManager(entityx::shared_ptr<EntityManager> entity_manager,
+ entityx::shared_ptr<EventManager> event_manager) :
+ entity_manager_(entity_manager),
+ event_manager_(event_manager) {}
+
bool initialized_ = false;
- EntityManager &entities_;
- EventManager &events_;
+ entityx::shared_ptr<EntityManager> entity_manager_;
+ entityx::shared_ptr<EventManager> event_manager_;
boost::unordered_map<BaseSystem::Family, entityx::shared_ptr<BaseSystem>> systems_;
};
public:
MovementSystem(string label = "") : label(label) {}
- void update(EntityManager &es, EventManager &events, double) override {
- EntityManager::View entities = es.entities_with_components<Position, Direction>();
- shared_ptr<Position> position;
- shared_ptr<Direction> direction;
+ void update(entityx::shared_ptr<EntityManager> es, entityx::shared_ptr<EventManager> events, double) override {
+ EntityManager::View entities = es->entities_with_components<Position, Direction>();
+ entityx::shared_ptr<Position> position;
+ entityx::shared_ptr<Direction> direction;
for (auto entity : entities) {
entity.unpack<Position, Direction>(position, direction);
position->x += direction->x;
public:
std::vector<Entity> entities;
- SystemManager &sm() { return system_manager; }
- EntityManager &em() { return entity_manager; }
+ entityx::shared_ptr<SystemManager> sm() { return system_manager; }
+ entityx::shared_ptr<EntityManager> em() { return entity_manager; }
protected:
void configure() override {
void initialize() override {
for (int i = 0; i < 150; ++i) {
- Entity e = entity_manager.create();
+ Entity e = entity_manager->create();
entities.push_back(e);
if (i % 2 == 0)
e.assign<Position>(1, 2);
TEST_F(SystemManagerTest, TestConstructSystemWithArgs) {
- manager.sm().add<MovementSystem>("movement");
- manager.sm().configure();
+ manager.sm()->add<MovementSystem>("movement");
+ manager.sm()->configure();
- ASSERT_EQ("movement", manager.sm().system<MovementSystem>()->label);
+ ASSERT_EQ("movement", manager.sm()->system<MovementSystem>()->label);
}
TEST_F(SystemManagerTest, TestApplySystem) {
- manager.sm().add<MovementSystem>();
- manager.sm().configure();
+ manager.sm()->add<MovementSystem>();
+ manager.sm()->configure();
- manager.sm().update<MovementSystem>(0.0);
- shared_ptr<Position> position;
- shared_ptr<Direction> direction;
+ manager.sm()->update<MovementSystem>(0.0);
+ entityx::shared_ptr<Position> position;
+ entityx::shared_ptr<Direction> direction;
for (auto entity : manager.entities) {
entity.unpack<Position, Direction>(position, direction);
if (position && direction) {
#pragma once
-#cmakedefine HAVE_BOOST_SHARED_PTR 1
-#cmakedefine HAVE_STD_SHARED_PTR 1
-#cmakedefine USE_STD_SHARED_PTR 0
+#cmakedefine ENTITYX_HAVE_BOOST_SHARED_PTR 1
+#cmakedefine ENTITYX_HAVE_STD_SHARED_PTR 1
+#cmakedefine ENTITYX_USE_STD_SHARED_PTR 1
+#cmakedefine ENTITYX_MAX_COMPONENTS @ENTITYX_MAX_COMPONENTS@
// Which shared_ptr implementation should we use?
-#if (HAVE_STD_SHARED_PTR && USE_STD_SHARED_PTR)
+#if (ENTITYX_HAVE_STD_SHARED_PTR && ENTITYX_USE_STD_SHARED_PTR)
#include <memory>
namespace entityx {
using std::make_shared;
using std::shared_ptr;
using std::static_pointer_cast;
+using std::weak_ptr;
+using std::enable_shared_from_this;
}
-#elif HAVE_BOOST_SHARED_PTR
+#elif ENTITYX_HAVE_BOOST_SHARED_PTR
#include <boost/shared_ptr.hpp>
+#include <boost/weak_ptr.hpp>
#include <boost/make_shared.hpp>
namespace entityx {
using boost::shared_ptr;
using boost::make_shared;
using boost::static_pointer_cast;
+using boost::weak_ptr;
+using boost::enable_shared_from_this;
}
#endif
+
+namespace entityx {
+static const int MAX_COMPONENTS = ENTITYX_MAX_COMPONENTS;
+}
struct TagsPredicate {
TagsPredicate(const std::string &tag) : tag(tag) {}
- bool operator () (EntityManager &manager, Entity::Id id) {
- auto tags = manager.component<TagsComponent>(id);
+ bool operator () (entityx::shared_ptr<EntityManager> manager, Entity::Id id) {
+ auto tags = manager->component<TagsComponent>(id);
return tags != nullptr && tags->tags.find(tag) != tags->tags.end();
}
}
TEST(TagsComponentTest, TestEntitiesWithTag) {
- EventManager ev;
- EntityManager en(ev);
- Entity a = en.create();
+ auto en = EntityManager::make(EventManager::make());
+ Entity a = en->create();
a.assign<Position>();
for (int i = 0; i < 99; ++i) {
- auto e = en.create();
+ auto e = en->create();
e.assign<Position>();
e.assign<TagsComponent>("positionable");
}
a.assign<TagsComponent>("player", "indestructible");
- auto entities = en.entities_with_components<Position>();
+ auto entities = en->entities_with_components<Position>();
ASSERT_EQ(100, size(entities));
ASSERT_EQ(1, size(TagsComponent::view(entities, "player")));
}
#!/bin/bash -e
-CMAKE_ARGS="-DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTING=1"
+CMAKE_ARGS="-DCMAKE_BUILD_TYPE=Debug -DENTITYX_BUILD_TESTING=1"
if [ "$USE_STD_SHARED_PTR" = "1" ]; then
- CMAKE_ARGS="${CMAKE_ARGS} -DUSE_STD_SHARED_PTR=1"
+ CMAKE_ARGS="${CMAKE_ARGS} -DENTITYX_USE_STD_SHARED_PTR=1"
# This fails on OSX
if [ "$CXX" = "clang++" ]; then
- CMAKE_ARGS="${CMAKE_ARGS} -DUSE_CPP11_STDLIB=1"
+ CMAKE_ARGS="${CMAKE_ARGS} -DENTITYX_USE_CPP11_STDLIB=1"
fi
fi