diff --git a/ecs.hpp b/ecs.hpp index 57c49bc..ec624e4 100644 --- a/ecs.hpp +++ b/ecs.hpp @@ -11,11 +11,13 @@ #include #include +#include #include #include #include #include #include +#include // ----------------------------------------------------------------------------- // @@ -42,7 +44,7 @@ namespace ecs_hpp // ----------------------------------------------------------------------------- // -// detail +// detail::type_family // // ----------------------------------------------------------------------------- @@ -74,6 +76,52 @@ namespace ecs_hpp } } +// ----------------------------------------------------------------------------- +// +// detail::component_storage +// +// ----------------------------------------------------------------------------- + +namespace ecs_hpp +{ + namespace detail + { + class component_storage_base { + public: + virtual ~component_storage_base() = default; + virtual bool remove(entity_id id) noexcept = 0; + virtual bool exists(entity_id id) const noexcept = 0; + }; + + template < typename T > + class component_storage : public component_storage_base { + public: + template < typename... Args > + void assign(entity_id id, Args&&... args); + bool remove(entity_id id) noexcept override; + bool exists(entity_id id) const noexcept override; + private: + std::unordered_map components_; + }; + + template < typename T > + template < typename... Args > + void component_storage::assign(entity_id id, Args&&... args) { + components_[id] = T(std::forward(args)...); + } + + template < typename T > + bool component_storage::remove(entity_id id) noexcept { + return components_.erase(id) > 0u; + } + + template < typename T > + bool component_storage::exists(entity_id id) const noexcept { + return components_.find(id) != components_.end(); + } + } +} + // ----------------------------------------------------------------------------- // // entity @@ -91,6 +139,18 @@ namespace ecs_hpp entity_id id() const noexcept; bool destroy(); + bool is_alive() const noexcept; + + template < typename T, typename... Args > + void assign_component(Args&&... args); + + template < typename T > + bool remove_component(); + + template < typename T > + bool exists_component() const noexcept; + + std::size_t remove_all_components() noexcept; private: world& owner_; entity_id id_{0u}; @@ -128,10 +188,30 @@ namespace ecs_hpp entity create_entity(); bool destroy_entity(const entity& ent); bool is_entity_alive(const entity& ent) const noexcept; + + template < typename T, typename... Args > + void assign_component(const entity& ent, Args&&... args); + + template < typename T > + bool remove_component(const entity& ent); + + template < typename T > + bool exists_component(const entity& ent) const noexcept; + + std::size_t remove_all_components(const entity& ent) const noexcept; + private: + template < typename T > + detail::component_storage* find_storage_() const; + template < typename T > + detail::component_storage& get_or_create_storage_(); private: mutable std::mutex mutex_; + entity_id last_entity_id_{0u}; std::unordered_set entities_; + + using storage_uptr = std::unique_ptr; + std::unordered_map storages_; }; } @@ -162,6 +242,31 @@ namespace ecs_hpp return owner_.destroy_entity(*this); } + inline bool entity::is_alive() const noexcept { + return owner_.is_entity_alive(*this); + } + + template < typename T, typename... Args > + void entity::assign_component(Args&&... args) { + owner_.assign_component( + *this, + std::forward(args)...); + } + + template < typename T > + bool entity::remove_component() { + return owner_.remove_component(*this); + } + + template < typename T > + bool entity::exists_component() const noexcept { + return owner_.exists_component(*this); + } + + inline std::size_t entity::remove_all_components() noexcept { + return owner_.remove_all_components(*this); + } + inline bool operator==(const entity& l, const entity& r) noexcept { return l.id() == r.id(); } @@ -199,4 +304,66 @@ namespace ecs_hpp std::lock_guard guard(mutex_); return entities_.count(ent) > 0u; } + + template < typename T, typename... Args > + void world::assign_component(const entity& ent, Args&&... args) { + std::lock_guard guard(mutex_); + get_or_create_storage_().assign( + ent.id(), + std::forward(args)...); + } + + template < typename T > + bool world::remove_component(const entity& ent) { + std::lock_guard guard(mutex_); + const detail::component_storage* storage = find_storage_(); + return storage + ? storage->remove(ent.id()) + : false; + } + + template < typename T > + bool world::exists_component(const entity& ent) const noexcept { + std::lock_guard guard(mutex_); + const detail::component_storage* storage = find_storage_(); + return storage + ? storage->exists(ent.id()) + : false; + } + + inline std::size_t world::remove_all_components(const entity& ent) const noexcept { + std::lock_guard guard(mutex_); + std::size_t removed_components = 0u; + for ( auto& storage_p : storages_ ) { + if ( storage_p.second->remove(ent.id()) ) { + ++removed_components; + } + } + return removed_components; + } + + template < typename T > + detail::component_storage* world::find_storage_() const { + const auto family = detail::type_family::id(); + const auto iter = storages_.find(family); + if ( iter != storages_.end() ) { + return static_cast*>(iter->second.get()); + } + return nullptr; + } + + template < typename T > + detail::component_storage& world::get_or_create_storage_() { + detail::component_storage* storage = find_storage_(); + if ( storage ) { + return *storage; + } + const auto family = detail::type_family::id(); + const auto emplace_r = storages_.emplace(std::make_pair( + family, + std::make_unique>())); + assert(emplace_r.second && "unexpected internal error"); + return *static_cast*>( + emplace_r.first->second.get()); + } } diff --git a/ecs_tests.cpp b/ecs_tests.cpp index c1b2af1..e5912ef 100644 --- a/ecs_tests.cpp +++ b/ecs_tests.cpp @@ -80,5 +80,44 @@ TEST_CASE("world") { } } SECTION("components") { + { + ecs::world w; + ecs::entity e1{w}; + + { + REQUIRE_FALSE(w.exists_component(e1)); + REQUIRE_FALSE(w.exists_component(e1)); + + w.assign_component(e1); + + REQUIRE(w.exists_component(e1)); + REQUIRE_FALSE(w.exists_component(e1)); + + w.assign_component(e1); + + REQUIRE(w.exists_component(e1)); + REQUIRE(w.exists_component(e1)); + + REQUIRE(w.remove_all_components(e1) == 2u); + + REQUIRE_FALSE(w.exists_component(e1)); + REQUIRE_FALSE(w.exists_component(e1)); + } + + { + REQUIRE_FALSE(e1.exists_component()); + REQUIRE_FALSE(e1.exists_component()); + + e1.assign_component(); + + REQUIRE(e1.exists_component()); + REQUIRE_FALSE(e1.exists_component()); + + e1.assign_component(); + + REQUIRE(e1.exists_component()); + REQUIRE(e1.exists_component()); + } + } } }