entityx/config.h
Makefile
html/
+Vagrantfile
+.vagrant
list(APPEND install_libs entityx_python_shared)
endif (ENTITYX_BUILD_SHARED)
set(CMAKE_REQUIRED_FLAGS ${CMAKE_CXX_FLAGS})
- include(CheckNeedGetPointer.cmake)
endif (ENTITYX_BUILD_PYTHON AND Boost_PYTHON_LIBRARY)
if (ENTITYX_BUILD_TESTING)
invalidate();
}
-bool Entity::valid() const {
- return !manager_.expired() && manager_.lock()->valid(id_);
+std::bitset<entityx::MAX_COMPONENTS> Entity::component_mask() const {
+ return manager_.lock()->component_mask(id_);
}
EntityManager::EntityManager(ptr<EventManager> event_manager) : event_manager_(event_manager) {
static const Id INVALID;
Entity() {}
- Entity(const Entity &) = default;
- Entity(Entity &&) = default;
- Entity &operator = (const Entity &) = default;
+ Entity(const ptr<EntityManager> &manager, Entity::Id id) : manager_(manager), id_(id) {
+ }
+ Entity(const Entity &other) : manager_(other.manager_), id_(other.id_) {
+ }
+ Entity &operator = (const Entity &other) {
+ manager_ = other.manager_;
+ id_ = other.id_;
+ return *this;
+ }
/**
* Check if Entity handle is invalid.
*/
void destroy();
- private:
- friend class EntityManager;
-
- Entity(ptr<EntityManager> manager, Entity::Id id) : manager_(manager), id_(id) {}
+ std::bitset<entityx::MAX_COMPONENTS> component_mask() const;
+ private:
weak_ptr<EntityManager> manager_;
Entity::Id id_ = INVALID;
};
inline std::ostream &operator << (std::ostream &out, const Entity::Id &id) {
- out << "Entity::Id(" << std::hex << id.id() << ")";
+ out << "Entity::Id(" << id.index() << "." << id.version() << ")";
return out;
}
*/
void destroy_all();
+ ComponentMask component_mask(Entity::Id id) {
+ return entity_component_mask_.at(id.index());
+ }
+
private:
template <typename C>
ComponentMask component_mask() {
manager_.lock()->unpack(id_, a, args ...);
}
+inline bool Entity::valid() const {
+ return !manager_.expired() && manager_.lock()->valid(id_);
+}
+
+
+
} // namespace entityx
*/
// http://docs.python.org/2/extending/extending.html
+#include <boost/noncopyable.hpp>
#include <Python.h>
#include <cassert>
#include <string>
#include <iostream>
#include <sstream>
#include "entityx/python/PythonSystem.h"
-#include "entityx/help/NonCopyable.h"
namespace py = boost::python;
static const py::object None;
-/**
- * Convert Entity::Id to a Python long.
- */
-struct EntityIdToPythonInteger {
- static PyObject* convert(Entity::Id const& id) {
- return py::incref(py::long_(id.id()).ptr());
- }
-};
-
-
class PythonEntityXLogger {
public:
PythonEntityXLogger() {}
};
+/**
+ * Base class for Python entities.
+ */
struct PythonEntity {
- PythonEntity(Entity entity) : _entity(entity) {} // NOLINT
+ explicit PythonEntity(ptr<EntityManager> entity_manager, Entity::Id id) : _entity(Entity(entity_manager, id)) {} // NOLINT
+ virtual ~PythonEntity() {}
void destroy() {
_entity.destroy();
operator Entity () const { return _entity; }
- void update(float dt, int frame) {}
+ virtual void update(float dt, int frame) {}
+
+ Entity::Id _entity_id() const {
+ return _entity.id();
+ }
Entity _entity;
};
-static std::string python_entity_repr(const PythonEntity &entity) {
+static std::string PythonEntity_repr(const PythonEntity &entity) {
std::stringstream repr;
repr << "<Entity " << entity._entity.id().index() << "." << entity._entity.id().version() << ">";
return repr.str();
}
-static std::string entity_repr(Entity entity) {
+static std::string Entity_Id_repr(Entity::Id id) {
std::stringstream repr;
- repr << "<Entity::Id " << entity.id().index() << "." << entity.id().version() << ">";
+ repr << "<Entity::Id " << id.index() << "." << id.version() << ">";
return repr.str();
}
-static bool entity_eq(Entity left, Entity right) {
- return left.id() == right.id();
-}
+// static std::string entity_repr(Entity entity) {
+// std::stringstream repr;
+// repr << "<Entity::Id " << entity.id().index() << "." << entity.id().version() << ">";
+// return repr.str();
+// }
+
+// static bool entity_eq(Entity left, Entity right) {
+// return left.id() == right.id();
+// }
+
+
+// A to-Python converter from Entity to PythonEntity.
+struct EntityToPythonEntity {
+ static PyObject *convert(Entity entity) {
+ auto python = entity.component<PythonComponent>();
+ assert(python && "Entity does not have a PythonComponent");
+ return py::incref(python->object.ptr());
+ }
+};
+
+
+Entity::Id EntityManager_configure(ptr<EntityManager> entity_manager, py::object self) {
+ Entity entity = entity_manager->create();
+ entity.assign<PythonComponent>(self);
+ return entity.id();
+}
BOOST_PYTHON_MODULE(_entityx) {
- py::to_python_converter<Entity::Id, EntityIdToPythonInteger>();
+ py::to_python_converter<Entity, EntityToPythonEntity>();
py::class_<PythonEntityXLogger>("Logger", py::no_init)
.def("write", &PythonEntityXLogger::write);
- py::class_<BaseEvent, ptr<BaseEvent>, entityx::help::NonCopyable>("BaseEvent", py::no_init);
+ py::class_<BaseEvent, ptr<BaseEvent>, boost::noncopyable>("BaseEvent", py::no_init);
- py::class_<PythonEntity>("Entity", py::init<Entity>())
- .def_readonly("_entity", &PythonEntity::_entity)
+ py::class_<PythonEntity>("Entity", py::init<ptr<EntityManager>, Entity::Id>())
+ .def_readonly("_entity_id", &PythonEntity::_entity_id)
.def("update", &PythonEntity::update)
.def("destroy", &PythonEntity::destroy)
- .def("__repr__", &python_entity_repr);
+ .def("__repr__", &PythonEntity_repr);
- py::class_<Entity>("RawEntity", py::no_init)
- .add_property("id", &Entity::id)
- .def("__eq__", &entity_eq)
- .def("__repr__", &entity_repr);
+ py::class_<Entity::Id>("EntityId", py::no_init)
+ .def_readonly("id", &Entity::Id::id)
+ .def_readonly("index", &Entity::Id::index)
+ .def_readonly("version", &Entity::Id::version)
+ .def("__repr__", &Entity_Id_repr);
py::class_<PythonComponent, ptr<PythonComponent>>("PythonComponent", py::init<py::object>())
.def("assign_to", &assign_to<PythonComponent>)
.def("get_component", &get_component<PythonComponent>)
.staticmethod("get_component");
- py::class_<EntityManager, ptr<EntityManager>, entityx::help::NonCopyable>("EntityManager", py::no_init)
- .def("create", &EntityManager::create);
+ py::class_<EntityManager, ptr<EntityManager>, boost::noncopyable>("EntityManager", py::no_init)
+ .def("configure", &EntityManager_configure);
void (EventManager::*emit)(const BaseEvent &) = &EventManager::emit;
- py::class_<EventManager, ptr<EventManager>, entityx::help::NonCopyable>("EventManager", py::no_init)
+ py::class_<EventManager, ptr<EventManager>, boost::noncopyable>("EventManager", py::no_init)
.def("emit", emit);
py::implicitly_convertible<PythonEntity, Entity>();
py::object cls = module.attr(event.component->cls.c_str());
py::object from_raw_entity = cls.attr("_from_raw_entity");
if (py::len(event.component->args) == 0) {
- event.component->object = from_raw_entity(event.entity);
+ event.component->object = from_raw_entity(event.entity.id());
} else {
py::list args;
- args.append(event.entity);
+ args.append(event.entity.id());
args.extend(event.component->args);
event.component->object = from_raw_entity(*py::tuple(args));
}
}
/**
- * Delete an Entity receiver. This is called automatically by PythonSystem
- * after testing with can_send().
+ * Delete an Entity receiver. This is called automatically by PythonSystem.
*
* @param entity The entity that was receiving events.
*/
* A helper function for class_ to assign a component to an entity.
*/
template <typename Component>
-void assign_to(ptr<Component> component, Entity &entity) { // NOLINT
- entity.assign<Component>(component);
+void assign_to(ptr<Component> component, ptr<EntityManager> entity_manager, Entity::Id id) {
+ entity_manager->assign<Component>(id, component);
}
* entity.
*/
template <typename Component>
-ptr<Component> get_component(Entity &entity) { // NOLINT
- return entity.component<Component>();
+ptr<Component> get_component(ptr<EntityManager> entity_manager, Entity::Id id) {
+ return entity_manager->component<Component>(id);
}
* Author: Alec Thomas <alec@swapoff.org>
*/
-// http://docs.python.org/2/extending/extending.html
+// NOTE: MUST be first include. See http://docs.python.org/2/extending/extending.html
#include <Python.h>
+#include <gtest/gtest.h>
+#include <boost/python.hpp>
#include <cassert>
#include <vector>
#include <string>
-#include <gtest/gtest.h>
-#include <boost/python.hpp>
+#include <iostream>
#include "entityx/Entity.h"
#include "entityx/Event.h"
#include "entityx/python/PythonSystem.h"
namespace py = boost::python;
+using std::cerr;
+using std::endl;
using namespace entityx;
using namespace entityx::python;
void receive(const CollisionEvent &event) {
for (auto entity : entities) {
- auto py_entity = entity.component<PythonComponent>();
if (entity == event.a || entity == event.b) {
+ auto py_entity = entity.component<PythonComponent>();
py_entity->object.attr("on_collision")(event);
}
}
.def_readwrite("y", &Direction::y);
py::class_<CollisionEvent, ptr<CollisionEvent>, py::bases<BaseEvent>>("Collision", py::init<Entity, Entity>())
- .def_readonly("a", &CollisionEvent::a)
- .def_readonly("b", &CollisionEvent::b);
+ .add_property("a", py::make_getter(&CollisionEvent::a, py::return_value_policy<py::return_by_value>()))
+ .add_property("b", py::make_getter(&CollisionEvent::b, py::return_value_policy<py::return_by_value>()));
}
initentityx_python_test();
initialized = true;
}
- system->add_event_proxy<CollisionEvent, CollisionEventProxy>(ev, ptr<CollisionEventProxy>(new CollisionEventProxy()));
+ system->add_event_proxy<CollisionEvent>(ev, ptr<CollisionEventProxy>(new CollisionEventProxy()));
system->configure(ev);
}
Entity g = em->create();
auto scripte = e.assign<PythonComponent>("entityx.tests.event_test", "EventTest");
auto scriptf = f.assign<PythonComponent>("entityx.tests.event_test", "EventTest");
+ auto scriptg = g.assign<PythonComponent>("entityx.tests.event_test", "EventTest");
ASSERT_FALSE(scripte->object.attr("collided"));
ASSERT_FALSE(scriptf->object.attr("collided"));
ev->emit<CollisionEvent>(f, g);
### Exposing C++ Components to Python
-In most cases, this should be pretty simple. Given a component, provide a `boost::python` class definition, with two extra methods defined with EntityX::Python helper functions `assign_to<Component>` and `get_component<Component>`. These are used from Python to assign Python-created components to an entity and to retrieve existing components from an entity, respectively.
+In most cases, this should be pretty simple. Given a component, provide a `boost::python` class definition wrapped in `entityx::ptr<T>`, with two extra methods defined with EntityX::Python helper functions `assign_to<Component>` and `get_component<Component>`. These are used from Python to assign Python-created components to an entity and to retrieve existing components from an entity, respectively.
Here's an example:
```c++
namespace py = boost::python;
+using namespace entityx;
+using namespace entityx::python;
+
struct Position : public Component<Position> {
Position(float x = 0.0, float y = 0.0) : x(x), y(y) {}
};
void export_position_to_python() {
- py::class_<PythonPosition, entityx::ptr<PythonPosition>>("Position", py::init<py::optional<float, float>>())
- .def("assign_to", &entityx::python::assign_to<Position>)
- .def("get_component", &entityx::python::get_component<Position>)
- .staticmethod("get_component")
+ py::class_<PythonPosition, ptr<PythonPosition>>("Position", py::init<py::optional<float, float>>())
+ .def("assign_to", &assign_to<Position>) // Allows this component to be assigned to an entity
+ .def("get_component", &get_component<Position>) // Allows this component to be retrieved from an entity.
+ .staticmethod("get_component") // (as above)
.def_readwrite("x", &PythonPosition::x)
.def_readwrite("y", &PythonPosition::y);
}
};
void export_collision_event_to_python() {
- py::class_<CollisionEvent>("Collision", py::init<Entity, Entity>())
+ py::class_<CollisionEvent, ptr<CollisionEvent>, py::bases<BaseEvent>>("Collision", py::init<Entity, Entity>())
.def_readonly("a", &CollisionEvent::a)
.def_readonly("b", &CollisionEvent::b);
}
}
```
+
+### Sending events from Python
+
+This is relatively straight forward. Once you have exported a C++ event to Python:
+
+```python
+from entityx import Entity, emit
+from mygame import Collision
+
+
+class AnEntity(Entity): pass
+
+
+emit(Collision(AnEntity(), AnEntity()))
+```
+
+
### Initialization
Finally, initialize the `mygame` module once, before using `PythonSystem`, with something like this:
vector<string> paths;
paths.push_back(MYGAME_PYTHON_PATH);
// +any other Python paths...
-entityx::ptr<PythonSystem> script_system = new PythonSystem(paths);
+ptr<PythonSystem> python(new PythonSystem(paths));
// Add any Event proxies.
-script_system->add_event_proxy<CollisionEvent>(ev, new CollisionEventProxy());
+python->add_event_proxy<CollisionEvent>(ev, ptr<CollisionEventProxy>(new CollisionEventProxy()));
```
self._args = args
self._kwargs = kwargs
- def _build(self, entity):
- component = self._cls.get_component(entity)
+ def _build(self, entity_id):
+ component = self._cls.get_component(_entityx._entity_manager, entity_id)
if not component:
component = self._cls(*self._args, **self._kwargs)
- component.assign_to(entity)
+ component.assign_to(_entityx._entity_manager, entity_id)
return component
__metaclass__ = EntityMetaClass
def __new__(cls, *args, **kwargs):
- entity = kwargs.pop('raw_entity', None)
+ entity_id = kwargs.pop('entity_id', None)
self = _entityx.Entity.__new__(cls)
- if entity is None:
- entity = _entityx._entity_manager.create()
- component = _entityx.PythonComponent(self)
- component.assign_to(entity)
- _entityx.Entity.__init__(self, entity)
+ if entity_id is None:
+ entity_id = _entityx._entity_manager.configure(self)
+ _entityx.Entity.__init__(self, _entityx._entity_manager, entity_id)
for k, v in self._components.items():
- setattr(self, k, v._build(self._entity))
+ setattr(self, k, v._build(self._entity_id))
return self
def __init__(self):
"""Default constructor."""
+ def __repr__(self):
+ return '<%s.%s %d.%d>' % (self.__class__.__module__, self.__class__.__name__, self._entity_id.index, self._entity_id.version)
+
@classmethod
- def _from_raw_entity(cls, raw_entity, *args, **kwargs):
+ def _from_raw_entity(cls, entity_id, *args, **kwargs):
"""Create a new Entity from a raw entity.
This is called from C++.
"""
- self = Entity.__new__(cls, raw_entity=raw_entity)
+ self = Entity.__new__(cls, entity_id=entity_id)
cls.__init__(self, *args, **kwargs)
return self
def create_entities_from_python_test():
a = EntityA()
- assert a._entity.id & 0xffffffff == 0
+ assert a._entity_id.index == 0
assert a.position.x == 1.0
assert a.position.y == 2.0
b = EntityA()
- assert b._entity.id & 0xffffffff == 1
+ assert b._entity_id.index == 1
a.destroy()
c = EntityA()
# Reuse destroyed index of "a".
- assert c._entity.id & 0xffffffff == 0
+ assert c._entity_id.index == 0
# However, version is different
- assert a._entity.id != c._entity.id and c._entity.id > a._entity.id
+ assert a._entity_id.id != c._entity_id.id and c._entity_id.id > a._entity_id.id
d = EntityA(2.0, 3.0)
assert d.position.x == 2.0
a = AnEntity()
b = AnEntity()
collision = Collision(a, b)
- print a, b, collision
- emit(Collision(a, b))
+ emit(collision)
def on_collision(self, event):
assert event.a
assert event.b
+ assert event.a == self or event.b == self
self.collided = True