From 11bacfc5c65c543a47a8634262a0a2664742031f Mon Sep 17 00:00:00 2001 From: Zack Mulgrew Date: Wed, 3 Feb 2016 22:28:00 -0800 Subject: Add test 'TestComponentRemovedEventOnEntityDestroyed' --- entityx/Entity_test.cc | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/entityx/Entity_test.cc b/entityx/Entity_test.cc index 2076ed7..e07a567 100644 --- a/entityx/Entity_test.cc +++ b/entityx/Entity_test.cc @@ -405,6 +405,27 @@ TEST_CASE_METHOD(EntityManagerFixture, "TestComponentRemovedEvent") { REQUIRE(!(e.component())); } +TEST_CASE_METHOD(EntityManagerFixture, "TestComponentRemovedEventOnEntityDestroyed") { + struct ComponentRemovedReceiver : public Receiver { + void receive(const ComponentRemovedEvent &event) { + removed = true; + } + + bool removed = false; + }; + + ComponentRemovedReceiver receiver; + ev.subscribe>(receiver); + + REQUIRE(!(receiver.removed)); + + Entity e = em.create(); + e.assign(1.0, 2.0); + e.destroy(); + + REQUIRE(receiver.removed); +} + TEST_CASE_METHOD(EntityManagerFixture, "TestEntityAssignment") { Entity a, b; a = em.create(); -- cgit v1.2.3 From d8c5d0e30980b747749571b5a1e5c3b5b20423b2 Mon Sep 17 00:00:00 2001 From: Zack Mulgrew Date: Wed, 3 Feb 2016 23:27:35 -0800 Subject: Split Entity and ComponentHandle into their own headers --- entityx/ComponentHandle.h | 65 ++++++++++++++++ entityx/Entity.h | 183 +--------------------------------------------- entityx/EntityClass.h | 145 ++++++++++++++++++++++++++++++++++++ 3 files changed, 213 insertions(+), 180 deletions(-) create mode 100644 entityx/ComponentHandle.h create mode 100644 entityx/EntityClass.h diff --git a/entityx/ComponentHandle.h b/entityx/ComponentHandle.h new file mode 100644 index 0000000..9406705 --- /dev/null +++ b/entityx/ComponentHandle.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2012 Alec Thomas + * All rights reserved. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. + * + * Author: Alec Thomas + */ + +#pragma once + +namespace entityx { + +class EntityManager; + +/** + * A ComponentHandle is a wrapper around an instance of a component. + * + * It provides safe access to components. The handle will be invalidated under + * the following conditions: + * + * - If a component is removed from its host entity. + * - If its host entity is destroyed. + */ +template +class ComponentHandle { +public: + typedef C ComponentType; + + ComponentHandle() : manager_(nullptr) {} + + bool valid() const; + operator bool() const; + + C *operator -> (); + const C *operator -> () const; + + C *get(); + const C *get() const; + + /** + * Remove the component from its entity and destroy it. + */ + void remove(); + + bool operator == (const ComponentHandle &other) const { + return manager_ == other.manager_ && id_ == other.id_; + } + + bool operator != (const ComponentHandle &other) const { + return !(*this == other); + } + +private: + friend class EntityManager; + + ComponentHandle(EM *manager, Entity::Id id) : + manager_(manager), id_(id) {} + + EM *manager_; + Entity::Id id_; +}; + +} diff --git a/entityx/Entity.h b/entityx/Entity.h index 3d0b96f..8e1e93e 100644 --- a/entityx/Entity.h +++ b/entityx/Entity.h @@ -27,12 +27,14 @@ #include #include #include - #include +#include #include "entityx/help/Pool.h" +#include "entityx/EntityClass.h" #include "entityx/config.h" #include "entityx/Event.h" #include "entityx/help/NonCopyable.h" +#include "entityx/ComponentHandle.h" namespace entityx { @@ -41,185 +43,6 @@ typedef std::uint64_t uint64_t; class EntityManager; - -template -class ComponentHandle; - - - -/** A convenience handle around an Entity::Id. - * - * If an entity is destroyed, any copies will be invalidated. Use valid() to - * check for validity before using. - * - * Create entities with `EntityManager`: - * - * Entity entity = entity_manager->create(); - */ -class Entity { -public: - struct Id { - Id() : id_(0) {} - explicit Id(uint64_t id) : id_(id) {} - Id(uint32_t index, uint32_t version) : id_(uint64_t(index) | uint64_t(version) << 32UL) {} - - uint64_t id() const { return id_; } - - bool operator == (const Id &other) const { return id_ == other.id_; } - bool operator != (const Id &other) const { return id_ != other.id_; } - bool operator < (const Id &other) const { return id_ < other.id_; } - - uint32_t index() const { return id_ & 0xffffffffUL; } - uint32_t version() const { return id_ >> 32; } - - private: - friend class EntityManager; - - uint64_t id_; - }; - - - /** - * Id of an invalid Entity. - */ - static const Id INVALID; - - Entity() = default; - Entity(EntityManager *manager, Entity::Id id) : manager_(manager), id_(id) {} - Entity(const Entity &other) = default; - Entity &operator = (const Entity &other) = default; - - /** - * Check if Entity handle is invalid. - */ - operator bool() const { - return valid(); - } - - bool operator == (const Entity &other) const { - return other.manager_ == manager_ && other.id_ == id_; - } - - bool operator != (const Entity &other) const { - return !(other == *this); - } - - bool operator < (const Entity &other) const { - return other.id_ < id_; - } - - /** - * Is this Entity handle valid? - * - * In older versions of EntityX, there were no guarantees around entity - * validity if a previously allocated entity slot was reassigned. That is no - * longer the case: if a slot is reassigned, old Entity::Id's will be - * invalid. - */ - bool valid() const; - - /** - * Invalidate Entity handle, disassociating it from an EntityManager and invalidating its ID. - * - * Note that this does *not* affect the underlying entity and its - * components. Use destroy() to destroy the associated Entity and components. - */ - void invalidate(); - - Id id() const { return id_; } - - template - ComponentHandle assign(Args && ... args); - - template - ComponentHandle assign_from_copy(const C &component); - - template - ComponentHandle replace(Args && ... args); - - template - void remove(); - - template ::value>::type> - ComponentHandle component(); - - template ::value>::type> - const ComponentHandle component() const; - - template - std::tuple...> components(); - - template - std::tuple...> components() const; - - template - bool has_component() const; - - template - void unpack(ComponentHandle &a, ComponentHandle & ... args); - - /** - * Destroy and invalidate this Entity. - */ - void destroy(); - - std::bitset component_mask() const; - - private: - EntityManager *manager_ = nullptr; - Entity::Id id_ = INVALID; -}; - - -/** - * A ComponentHandle is a wrapper around an instance of a component. - * - * It provides safe access to components. The handle will be invalidated under - * the following conditions: - * - * - If a component is removed from its host entity. - * - If its host entity is destroyed. - */ -template -class ComponentHandle { -public: - typedef C ComponentType; - - ComponentHandle() : manager_(nullptr) {} - - bool valid() const; - operator bool() const; - - C *operator -> (); - const C *operator -> () const; - - C *get(); - const C *get() const; - - /** - * Remove the component from its entity and destroy it. - */ - void remove(); - - bool operator == (const ComponentHandle &other) const { - return manager_ == other.manager_ && id_ == other.id_; - } - - bool operator != (const ComponentHandle &other) const { - return !(*this == other); - } - -private: - friend class EntityManager; - - ComponentHandle(EM *manager, Entity::Id id) : - manager_(manager), id_(id) {} - - EM *manager_; - Entity::Id id_; -}; - - /** * Base component class, only used for insertion into collections. * diff --git a/entityx/EntityClass.h b/entityx/EntityClass.h new file mode 100644 index 0000000..444e494 --- /dev/null +++ b/entityx/EntityClass.h @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2012 Alec Thomas + * All rights reserved. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. + * + * Author: Alec Thomas + */ + +#pragma once + +#include "entityx/config.h" + +namespace entityx { + +class EntityManager; + +template +class ComponentHandle; + +/** A convenience handle around an Entity::Id. + * + * If an entity is destroyed, any copies will be invalidated. Use valid() to + * check for validity before using. + * + * Create entities with `EntityManager`: + * + * Entity entity = entity_manager->create(); + */ +class Entity { +public: + struct Id { + Id() : id_(0) {} + explicit Id(uint64_t id) : id_(id) {} + Id(uint32_t index, uint32_t version) : id_(uint64_t(index) | uint64_t(version) << 32UL) {} + + uint64_t id() const { return id_; } + + bool operator == (const Id &other) const { return id_ == other.id_; } + bool operator != (const Id &other) const { return id_ != other.id_; } + bool operator < (const Id &other) const { return id_ < other.id_; } + + uint32_t index() const { return id_ & 0xffffffffUL; } + uint32_t version() const { return id_ >> 32; } + + private: + friend class EntityManager; + + uint64_t id_; + }; + + + /** + * Id of an invalid Entity. + */ + static const Id INVALID; + + Entity() = default; + Entity(EntityManager *manager, Entity::Id id) : manager_(manager), id_(id) {} + Entity(const Entity &other) = default; + Entity &operator = (const Entity &other) = default; + + /** + * Check if Entity handle is invalid. + */ + operator bool() const { + return valid(); + } + + bool operator == (const Entity &other) const { + return other.manager_ == manager_ && other.id_ == id_; + } + + bool operator != (const Entity &other) const { + return !(other == *this); + } + + bool operator < (const Entity &other) const { + return other.id_ < id_; + } + + /** + * Is this Entity handle valid? + * + * In older versions of EntityX, there were no guarantees around entity + * validity if a previously allocated entity slot was reassigned. That is no + * longer the case: if a slot is reassigned, old Entity::Id's will be + * invalid. + */ + bool valid() const; + + /** + * Invalidate Entity handle, disassociating it from an EntityManager and invalidating its ID. + * + * Note that this does *not* affect the underlying entity and its + * components. Use destroy() to destroy the associated Entity and components. + */ + void invalidate(); + + Id id() const { return id_; } + + template + ComponentHandle assign(Args && ... args); + + template + ComponentHandle assign_from_copy(const C &component); + + template + ComponentHandle replace(Args && ... args); + + template + void remove(); + + template ::value>::type> + ComponentHandle component(); + + template ::value>::type> + const ComponentHandle component() const; + + template + std::tuple...> components(); + + template + std::tuple...> components() const; + + template + bool has_component() const; + + template + void unpack(ComponentHandle &a, ComponentHandle & ... args); + + /** + * Destroy and invalidate this Entity. + */ + void destroy(); + + std::bitset component_mask() const; + + private: + EntityManager *manager_ = nullptr; + Entity::Id id_ = INVALID; +}; + +} -- cgit v1.2.3 From 070cd89d2ed140594608d252318f444202579a52 Mon Sep 17 00:00:00 2001 From: Zack Mulgrew Date: Wed, 3 Feb 2016 23:49:41 -0800 Subject: Trigger ComponentRemovedEvent for each component before destroying --- entityx/Entity.h | 6 ++++-- entityx/EntityClass.h | 1 + entityx/help/Pool.h | 7 +++++++ entityx/help/Pool_test.cc | 3 ++- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/entityx/Entity.h b/entityx/Entity.h index 8e1e93e..2ef01f7 100644 --- a/entityx/Entity.h +++ b/entityx/Entity.h @@ -381,12 +381,14 @@ class EntityManager : entityx::help::NonCopyable { assert_valid(entity); uint32_t index = entity.index(); auto mask = entity_component_mask_[entity.index()]; - event_manager_.emit(Entity(this, entity)); for (size_t i = 0; i < component_pools_.size(); i++) { BasePool *pool = component_pools_[i]; - if (pool && mask.test(i)) + if (pool && mask.test(i)) { + pool->removeComponent(Entity(this, entity)); pool->destroy(index); + } } + event_manager_.emit(Entity(this, entity)); entity_component_mask_[index].reset(); entity_version_[index]++; free_list_.push_back(index); diff --git a/entityx/EntityClass.h b/entityx/EntityClass.h index 444e494..94a2a02 100644 --- a/entityx/EntityClass.h +++ b/entityx/EntityClass.h @@ -10,6 +10,7 @@ #pragma once +#include #include "entityx/config.h" namespace entityx { diff --git a/entityx/help/Pool.h b/entityx/help/Pool.h index f217ec2..c4b7d83 100644 --- a/entityx/help/Pool.h +++ b/entityx/help/Pool.h @@ -14,6 +14,8 @@ #include #include +#include "entityx/EntityClass.h" + namespace entityx { /** @@ -63,6 +65,7 @@ class BasePool { } virtual void destroy(std::size_t n) = 0; + virtual void removeComponent(Entity entity) = 0; protected: std::vector blocks_; @@ -90,6 +93,10 @@ class Pool : public BasePool { T *ptr = static_cast(get(n)); ptr->~T(); } + + virtual void removeComponent(Entity entity) override { + entity.remove(); + } }; } // namespace entityx diff --git a/entityx/help/Pool_test.cc b/entityx/help/Pool_test.cc index 56ca85a..08ef1d2 100644 --- a/entityx/help/Pool_test.cc +++ b/entityx/help/Pool_test.cc @@ -13,8 +13,9 @@ #include #include "entityx/3rdparty/catch.hpp" #include "entityx/help/Pool.h" +#include "entityx/Entity.h" -struct Position { +struct Position : public entityx::Component { explicit Position(int *ptr = nullptr) : ptr(ptr) { if (ptr) (*ptr)++; } -- cgit v1.2.3 From 55fc21ca701f333552c9ef9882661ec3f86bd237 Mon Sep 17 00:00:00 2001 From: Zack Mulgrew Date: Wed, 3 Feb 2016 23:50:09 -0800 Subject: Update README to reflect how components are removed before entity destruction --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 90057da..43d195c 100644 --- a/README.md +++ b/README.md @@ -119,13 +119,13 @@ To that end Components are typically [POD types](http://en.wikipedia.org/wiki/Pl As an example, position and direction information might be represented as: ```c++ -struct Position { +struct Position : public entityx::Component { Position(float x = 0.0f, float y = 0.0f) : x(x), y(y) {} float x, y; }; -struct Direction { +struct Direction : public entityx::Component { Direction(float x = 0.0f, float y = 0.0f) : x(x), y(y) {} float x, y; @@ -224,7 +224,7 @@ As an example, we might want to implement a very basic collision system using ou First, we define the event type, which for our example is simply the two entities that collided: ```c++ -struct Collision { +struct Collision : public entityx::Component { Collision(entityx::Entity left, entityx::Entity right) : left(left), right(right) {} entityx::Entity left, right; @@ -290,6 +290,7 @@ Several events are emitted by EntityX itself: - Event objects are destroyed after delivery, so references should not be retained. - A single class can receive any number of types of events by implementing a ``receive(const EventType &)`` method for each event type. - Any class implementing `Receiver` can receive events, but typical usage is to make `System`s also be `Receiver`s. +- When an `Entity` is destroyed it will cause all of its components to be removed. This triggers `ComponentRemovedEvent`s to be triggered for each of its components. These events are triggered before the `EntityDestroyedEvent`. ### Manager (tying it all together) -- cgit v1.2.3 From f176609c242afd53f6083b3e115227ace10e2eb2 Mon Sep 17 00:00:00 2001 From: Zack Mulgrew Date: Thu, 4 Feb 2016 00:04:46 -0800 Subject: Fix method name to be consistent with others --- entityx/Entity.h | 2 +- entityx/help/Pool.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/entityx/Entity.h b/entityx/Entity.h index 2ef01f7..5e9b17d 100644 --- a/entityx/Entity.h +++ b/entityx/Entity.h @@ -384,7 +384,7 @@ class EntityManager : entityx::help::NonCopyable { for (size_t i = 0; i < component_pools_.size(); i++) { BasePool *pool = component_pools_[i]; if (pool && mask.test(i)) { - pool->removeComponent(Entity(this, entity)); + pool->remove_component(Entity(this, entity)); pool->destroy(index); } } diff --git a/entityx/help/Pool.h b/entityx/help/Pool.h index c4b7d83..b28e4e7 100644 --- a/entityx/help/Pool.h +++ b/entityx/help/Pool.h @@ -65,7 +65,7 @@ class BasePool { } virtual void destroy(std::size_t n) = 0; - virtual void removeComponent(Entity entity) = 0; + virtual void remove_component(Entity entity) = 0; protected: std::vector blocks_; @@ -94,7 +94,7 @@ class Pool : public BasePool { ptr->~T(); } - virtual void removeComponent(Entity entity) override { + virtual void remove_component(Entity entity) override { entity.remove(); } }; -- cgit v1.2.3 From 4f67e896183c5d4f6ac32661d20ab715e5f6dbc8 Mon Sep 17 00:00:00 2001 From: Zack Mulgrew Date: Fri, 5 Feb 2016 20:14:39 -0800 Subject: Revert change made regarding component inheritance --- README.md | 6 +++--- entityx/help/Pool_test.cc | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 43d195c..db124ce 100644 --- a/README.md +++ b/README.md @@ -119,13 +119,13 @@ To that end Components are typically [POD types](http://en.wikipedia.org/wiki/Pl As an example, position and direction information might be represented as: ```c++ -struct Position : public entityx::Component { +struct Position { Position(float x = 0.0f, float y = 0.0f) : x(x), y(y) {} float x, y; }; -struct Direction : public entityx::Component { +struct Direction { Direction(float x = 0.0f, float y = 0.0f) : x(x), y(y) {} float x, y; @@ -224,7 +224,7 @@ As an example, we might want to implement a very basic collision system using ou First, we define the event type, which for our example is simply the two entities that collided: ```c++ -struct Collision : public entityx::Component { +struct Collision { Collision(entityx::Entity left, entityx::Entity right) : left(left), right(right) {} entityx::Entity left, right; diff --git a/entityx/help/Pool_test.cc b/entityx/help/Pool_test.cc index 08ef1d2..1092068 100644 --- a/entityx/help/Pool_test.cc +++ b/entityx/help/Pool_test.cc @@ -15,7 +15,7 @@ #include "entityx/help/Pool.h" #include "entityx/Entity.h" -struct Position : public entityx::Component { +struct Position { explicit Position(int *ptr = nullptr) : ptr(ptr) { if (ptr) (*ptr)++; } -- cgit v1.2.3