diff --git a/README.md b/README.md index 5ac6014..5000e63 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,7 @@ private: ecs_hpp::registry world; world.add_system(); +world.add_system(9.8f); auto entity_one = world.create_entity(); world.assign_component(entity_one, 4.f, 2.f); diff --git a/ecs.hpp b/ecs.hpp index 9fa9a01..d72c9f4 100644 --- a/ecs.hpp +++ b/ecs.hpp @@ -29,6 +29,8 @@ namespace ecs_hpp { class entity; + class const_entity; + class system; class registry; @@ -115,19 +117,29 @@ namespace ecs_hpp // tuple_contains // - template < typename V > - bool tuple_contains(const std::tuple<>& t, const V& v) { - (void)t; - (void)v; - return false; + namespace impl + { + template < size_t I, typename V, typename... Ts > + std::enable_if_t + tuple_contains_impl(const std::tuple& t, const V& v) { + (void)t; + (void)v; + return false; + } + + template < size_t I, typename V, typename... Ts > + std::enable_if_t + tuple_contains_impl(const std::tuple& t, const V& v) { + if ( std::get(t) == v ) { + return true; + } + return tuple_contains_impl(t, v); + } } - template < typename V, typename T, typename... Ts > - bool tuple_contains(const std::tuple& t, const V& v) { - if ( std::get<0>(t) == v ) { - return true; - } - return tuple_contains(tuple_tail(t), v); + template < typename V, typename... Ts > + bool tuple_contains(const std::tuple& t, const V& v) { + return impl::tuple_contains_impl<0>(t, v); } // @@ -202,7 +214,8 @@ namespace ecs_hpp namespace detail { template < typename T - , bool = std::is_unsigned::value && sizeof(T) <= sizeof(std::size_t) > + , bool = std::is_unsigned::value + && sizeof(T) <= sizeof(std::size_t) > struct sparse_unsigned_indexer { std::size_t operator()(const T v) const noexcept { return static_cast(v); @@ -240,8 +253,7 @@ namespace ecs_hpp } iterator end() noexcept { - using dt = typename std::iterator_traits::difference_type; - return begin() + static_cast
(size_); + return dense_.end(); } const_iterator begin() const noexcept { @@ -249,8 +261,7 @@ namespace ecs_hpp } const_iterator end() const noexcept { - using dt = typename std::iterator_traits::difference_type; - return begin() + static_cast
(size_); + return dense_.end(); } const_iterator cbegin() const noexcept { @@ -258,8 +269,7 @@ namespace ecs_hpp } const_iterator cend() const noexcept { - using dt = typename std::iterator_traits::difference_type; - return cbegin() + static_cast
(size_); + return dense_.cend(); } public: sparse_set(const Indexer& indexer = Indexer()) @@ -270,12 +280,11 @@ namespace ecs_hpp return false; } const std::size_t vi = indexer_(v); - if ( vi >= capacity_ ) { - reserve(new_capacity_for_(vi + 1u)); + if ( vi >= sparse_.size() ) { + sparse_.resize(new_sparse_size_for_(vi + 1u)); } - dense_[size_] = std::move(v); - sparse_[vi] = size_; - ++size_; + dense_.push_back(std::move(v)); + sparse_[vi] = dense_.size() - 1u; return true; } @@ -284,12 +293,11 @@ namespace ecs_hpp return false; } const std::size_t vi = indexer_(v); - if ( vi >= capacity_ ) { - reserve(new_capacity_for_(vi + 1u)); + if ( vi >= sparse_.size() ) { + sparse_.resize(new_sparse_size_for_(vi + 1u)); } - dense_[size_] = v; - sparse_[vi] = size_; - ++size_; + dense_.push_back(v); + sparse_[vi] = dense_.size() - 1u; return true; } @@ -305,21 +313,21 @@ namespace ecs_hpp return false; } const std::size_t vi = indexer_(v); - const std::size_t index = sparse_[vi]; - dense_[index] = std::move(dense_[size_ - 1u]); - sparse_[indexer_(dense_[index])] = index; - --size_; + const std::size_t dense_index = sparse_[vi]; + dense_[dense_index] = std::move(dense_.back()); + sparse_[indexer_(dense_[dense_index])] = dense_index; + dense_.pop_back(); return true; } void clear() noexcept { - size_ = 0u; + dense_.clear(); } bool has(const T& v) const noexcept { const std::size_t vi = indexer_(v); - return vi < capacity_ - && sparse_[vi] < size_ + return vi < sparse_.size() + && sparse_[vi] < dense_.size() && dense_[sparse_[vi]] == v; } @@ -345,49 +353,27 @@ namespace ecs_hpp } bool empty() const noexcept { - return size_ == 0u; - } - - void reserve(std::size_t ncapacity) { - if ( ncapacity > capacity_ ) { - std::vector ndense(ncapacity); - std::vector nsparse(ncapacity); - std::copy(dense_.begin(), dense_.end(), ndense.begin()); - std::copy(sparse_.begin(), sparse_.end(), nsparse.begin()); - ndense.swap(dense_); - nsparse.swap(sparse_); - capacity_ = ncapacity; - } + return dense_.empty(); } std::size_t size() const noexcept { - return size_; - } - - std::size_t max_size() const noexcept { - return std::min(dense_.max_size(), sparse_.max_size()); - } - - std::size_t capacity() const noexcept { - return capacity_; + return dense_.size(); } private: - std::size_t new_capacity_for_(std::size_t nsize) const { - const std::size_t ms = max_size(); + std::size_t new_sparse_size_for_(std::size_t nsize) const { + const std::size_t ms = sparse_.max_size(); if ( nsize > ms ) { throw std::length_error("ecs_hpp::sparse_set"); } - if ( capacity_ >= ms / 2u ) { + if ( sparse_.size() >= ms / 2u ) { return ms; } - return std::max(capacity_ * 2u, nsize); + return std::max(sparse_.size() * 2u, nsize); } private: Indexer indexer_; std::vector dense_; std::vector sparse_; - std::size_t size_{0u}; - std::size_t capacity_{0u}; }; } } @@ -484,8 +470,8 @@ namespace ecs_hpp if ( !keys_.has(k) ) { return false; } - const std::size_t index = keys_.get_dense_index(k); - values_[index] = std::move(values_.back()); + const std::size_t value_index = keys_.get_dense_index(k); + values_[value_index] = std::move(values_.back()); values_.pop_back(); keys_.unordered_erase(k); return true; @@ -509,16 +495,16 @@ namespace ecs_hpp } T* find(const K& k) noexcept { - const auto ip = keys_.find_dense_index(k); - return ip.second - ? &values_[ip.first] + const auto value_index_p = keys_.find_dense_index(k); + return value_index_p.second + ? &values_[value_index_p.first] : nullptr; } const T* find(const K& k) const noexcept { - const auto ip = keys_.find_dense_index(k); - return ip.second - ? &values_[ip.first] + const auto value_index_p = keys_.find_dense_index(k); + return value_index_p.second + ? &values_[value_index_p.first] : nullptr; } @@ -526,22 +512,9 @@ namespace ecs_hpp return values_.empty(); } - void reserve(std::size_t ncapacity) { - keys_.reserve(ncapacity); - values_.reserve(ncapacity); - } - std::size_t size() const noexcept { return values_.size(); } - - std::size_t max_size() const noexcept { - return std::min(keys_.max_size(), values_.max_size()); - } - - std::size_t capacity() const noexcept { - return values_.capacity(); - } private: sparse_set keys_; std::vector values_; @@ -649,7 +622,7 @@ namespace ecs_hpp template < typename F > void component_storage::for_each_component(F&& f) const noexcept { for ( const auto id : components_ ) { - f(entity(owner_, id), components_.get(id)); + f(const_entity(owner_, id), components_.get(id)); } } } @@ -665,8 +638,8 @@ namespace ecs_hpp { class entity final { public: - entity(registry& owner); - entity(registry& owner, entity_id id); + entity(registry& owner) noexcept; + entity(registry& owner, entity_id id) noexcept; const registry& owner() const noexcept; entity_id id() const noexcept; @@ -727,6 +700,61 @@ namespace std }; } +// ----------------------------------------------------------------------------- +// +// const_entity +// +// ----------------------------------------------------------------------------- + +namespace ecs_hpp +{ + class const_entity final { + public: + const_entity(const entity& ent) noexcept; + + const_entity(const registry& owner) noexcept; + const_entity(const registry& owner, entity_id id) noexcept; + + const registry& owner() const noexcept; + entity_id id() const noexcept; + + bool is_alive() const noexcept; + + template < typename T > + bool exists_component() const noexcept; + + template < typename T > + const T& get_component() const; + + template < typename T > + const T* find_component() const noexcept; + + template < typename... Ts > + std::tuple get_components() const; + + template < typename... Ts > + std::tuple find_components() const noexcept; + private: + const registry* owner_; + entity_id id_{0u}; + }; + + bool operator==(const const_entity& l, const const_entity& r) noexcept; + bool operator!=(const const_entity& l, const const_entity& r) noexcept; +} + +namespace std +{ + template <> + struct hash + : std::unary_function + { + std::size_t operator()(const ecs_hpp::const_entity& ent) const noexcept { + return std::hash()(ent.id()); + } + }; +} + // ----------------------------------------------------------------------------- // // system @@ -757,7 +785,7 @@ namespace ecs_hpp entity create_entity(); bool destroy_entity(const entity& ent); - bool is_entity_alive(const entity& ent) const noexcept; + bool is_entity_alive(const const_entity& ent) const noexcept; template < typename T, typename... Args > bool assign_component(const entity& ent, Args&&... args); @@ -766,29 +794,34 @@ namespace ecs_hpp bool remove_component(const entity& ent); template < typename T > - bool exists_component(const entity& ent) const noexcept; + bool exists_component(const const_entity& ent) const noexcept; - std::size_t remove_all_components(const entity& ent) const noexcept; + std::size_t remove_all_components(const entity& ent) noexcept; template < typename T > T& get_component(const entity& ent); template < typename T > - const T& get_component(const entity& ent) const; + const T& get_component(const const_entity& ent) const; template < typename T > T* find_component(const entity& ent) noexcept; template < typename T > - const T* find_component(const entity& ent) const noexcept; + const T* find_component(const const_entity& ent) const noexcept; template < typename... Ts > std::tuple get_components(const entity& ent); template < typename... Ts > - std::tuple get_components(const entity& ent) const; + std::tuple get_components(const const_entity& ent) const; template < typename... Ts > std::tuple find_components(const entity& ent) noexcept; template < typename... Ts > - std::tuple find_components(const entity& ent) const noexcept; + std::tuple find_components(const const_entity& ent) const noexcept; + + template < typename F > + void for_each_entity(F&& f); + template < typename F > + void for_each_entity(F&& f) const; template < typename T, typename F > void for_each_component(F&& f); @@ -812,13 +845,13 @@ namespace ecs_hpp template < typename T > detail::component_storage& get_or_create_storage_(); - bool is_entity_alive_impl_(const entity& ent) const noexcept; - std::size_t remove_all_components_impl_(const entity& ent) const noexcept; + bool is_entity_alive_impl_(const const_entity& ent) const noexcept; + std::size_t remove_all_components_impl_(const entity& ent) noexcept; template < typename T > T* find_component_impl_(const entity& ent) noexcept; template < typename T > - const T* find_component_impl_(const entity& ent) const noexcept; + const T* find_component_impl_(const const_entity& ent) const noexcept; template < typename T, typename... Ts, typename F, std::size_t I, std::size_t... Is > void for_joined_components_impl_(F&& f, std::index_sequence iseq); @@ -843,7 +876,7 @@ namespace ecs_hpp , typename Ss , typename... Cs > void for_joined_components_impl_( - const entity& e, + const const_entity& e, const F& f, const Ss& ss, const Cs&... cs) const; @@ -857,7 +890,7 @@ namespace ecs_hpp template < typename F, typename... Cs > void for_joined_components_impl_( - const entity& e, + const const_entity& e, const F& f, const std::tuple<>& ss, const Cs&... cs) const; @@ -882,10 +915,10 @@ namespace ecs_hpp namespace ecs_hpp { - inline entity::entity(registry& owner) + inline entity::entity(registry& owner) noexcept : owner_(&owner) {} - inline entity::entity(registry& owner, entity_id id) + inline entity::entity(registry& owner, entity_id id) noexcept : owner_(&owner) , id_(id) {} @@ -976,6 +1009,72 @@ namespace ecs_hpp } } +// ----------------------------------------------------------------------------- +// +// const_entity impl +// +// ----------------------------------------------------------------------------- + +namespace ecs_hpp +{ + inline const_entity::const_entity(const entity& ent) noexcept + : owner_(&ent.owner()) + , id_(ent.id()) {} + + inline const_entity::const_entity(const registry& owner) noexcept + : owner_(&owner) {} + + inline const_entity::const_entity(const registry& owner, entity_id id) noexcept + : owner_(&owner) + , id_(id) {} + + inline const registry& const_entity::owner() const noexcept { + return *owner_; + } + + inline entity_id const_entity::id() const noexcept { + return id_; + } + + inline bool const_entity::is_alive() const noexcept { + return (*owner_).is_entity_alive(*this); + } + + template < typename T > + bool const_entity::exists_component() const noexcept { + return (*owner_).exists_component(*this); + } + + template < typename T > + const T& const_entity::get_component() const { + return (*owner_).get_component(*this); + } + + template < typename T > + const T* const_entity::find_component() const noexcept { + return (*owner_).find_component(*this); + } + + template < typename... Ts > + std::tuple const_entity::get_components() const { + return (*owner_).get_components(*this); + } + + template < typename... Ts > + std::tuple const_entity::find_components() const noexcept { + return (*owner_).find_components(*this); + } + + inline bool operator==(const const_entity& l, const const_entity& r) noexcept { + return &l.owner() == &r.owner() + && l.id() == r.id(); + } + + inline bool operator!=(const const_entity& l, const const_entity& r) noexcept { + return !(l == r); + } +} + // ----------------------------------------------------------------------------- // // registry impl @@ -1011,7 +1110,7 @@ namespace ecs_hpp return false; } - inline bool registry::is_entity_alive(const entity& ent) const noexcept { + inline bool registry::is_entity_alive(const const_entity& ent) const noexcept { return is_entity_alive_impl_(ent); } @@ -1038,7 +1137,7 @@ namespace ecs_hpp } template < typename T > - bool registry::exists_component(const entity& ent) const noexcept { + bool registry::exists_component(const const_entity& ent) const noexcept { if ( !is_entity_alive_impl_(ent) ) { return false; } @@ -1048,7 +1147,7 @@ namespace ecs_hpp : false; } - inline std::size_t registry::remove_all_components(const entity& ent) const noexcept { + inline std::size_t registry::remove_all_components(const entity& ent) noexcept { return remove_all_components_impl_(ent); } @@ -1062,7 +1161,7 @@ namespace ecs_hpp } template < typename T > - const T& registry::get_component(const entity& ent) const { + const T& registry::get_component(const const_entity& ent) const { const T* component = find_component_impl_(ent); if ( component ) { return *component; @@ -1076,7 +1175,7 @@ namespace ecs_hpp } template < typename T > - const T* registry::find_component(const entity& ent) const noexcept { + const T* registry::find_component(const const_entity& ent) const noexcept { return find_component_impl_(ent); } @@ -1086,7 +1185,7 @@ namespace ecs_hpp } template < typename... Ts > - std::tuple registry::get_components(const entity& ent) const { + std::tuple registry::get_components(const const_entity& ent) const { return std::make_tuple(std::cref(get_component(ent))...); } @@ -1096,10 +1195,24 @@ namespace ecs_hpp } template < typename... Ts > - std::tuple registry::find_components(const entity& ent) const noexcept { + std::tuple registry::find_components(const const_entity& ent) const noexcept { return std::make_tuple(find_component(ent)...); } + template < typename F > + void registry::for_each_entity(F&& f) { + for ( const auto id : entity_ids_ ) { + f(entity(*this, id)); + } + } + + template < typename F > + void registry::for_each_entity(F&& f) const { + for ( const auto id : entity_ids_ ) { + f(const_entity(*this, id)); + } + } + template < typename T, typename F > void registry::for_each_component(F&& f) { detail::component_storage* storage = find_storage_(); @@ -1146,8 +1259,9 @@ namespace ecs_hpp detail::component_storage* registry::find_storage_() noexcept { const auto family = detail::type_family::id(); using raw_storage_ptr = detail::component_storage*; - return storages_.has(family) - ? static_cast(storages_.get(family).get()) + const storage_uptr* storage_uptr_ptr = storages_.find(family); + return storage_uptr_ptr && *storage_uptr_ptr + ? static_cast(storage_uptr_ptr->get()) : nullptr; } @@ -1155,8 +1269,9 @@ namespace ecs_hpp const detail::component_storage* registry::find_storage_() const noexcept { const auto family = detail::type_family::id(); using raw_storage_ptr = const detail::component_storage*; - return storages_.has(family) - ? static_cast(storages_.get(family).get()) + const storage_uptr* storage_uptr_ptr = storages_.find(family); + return storage_uptr_ptr && *storage_uptr_ptr + ? static_cast(storage_uptr_ptr->get()) : nullptr; } @@ -1174,11 +1289,11 @@ namespace ecs_hpp storages_.get(family).get()); } - inline bool registry::is_entity_alive_impl_(const entity& ent) const noexcept { + inline bool registry::is_entity_alive_impl_(const const_entity& ent) const noexcept { return entity_ids_.has(ent.id()); } - inline std::size_t registry::remove_all_components_impl_(const entity& ent) const noexcept { + inline std::size_t registry::remove_all_components_impl_(const entity& ent) noexcept { if ( !is_entity_alive_impl_(ent) ) { return 0u; } @@ -1200,7 +1315,7 @@ namespace ecs_hpp } template < typename T > - const T* registry::find_component_impl_(const entity& ent) const noexcept { + const T* registry::find_component_impl_(const const_entity& ent) const noexcept { const detail::component_storage* storage = find_storage_(); return storage ? storage->find(ent.id()) @@ -1237,7 +1352,7 @@ namespace ecs_hpp (void)iseq; const auto ss = std::make_tuple(find_storage_()...); if ( !detail::tuple_contains(ss, nullptr) ) { - for_each_component([this, &f, &ss](const entity& e, const T& t) { + for_each_component([this, &f, &ss](const const_entity& e, const T& t) { detail::as_const(*this).for_joined_components_impl_(e, f, ss, t); }); } @@ -1271,7 +1386,7 @@ namespace ecs_hpp , typename Ss , typename... Cs > void registry::for_joined_components_impl_( - const entity& e, + const const_entity& e, const F& f, const Ss& ss, const Cs&... cs) const @@ -1300,7 +1415,7 @@ namespace ecs_hpp template < typename F, typename... Cs > void registry::for_joined_components_impl_( - const entity& e, + const const_entity& e, const F& f, const std::tuple<>& ss, const Cs&... cs) const diff --git a/ecs_tests.cpp b/ecs_tests.cpp index 21fe6e2..0cf61ad 100644 --- a/ecs_tests.cpp +++ b/ecs_tests.cpp @@ -80,6 +80,31 @@ TEST_CASE("detail") { REQUIRE(tuple_tail(t3) == std::make_tuple(2, 3)); } } + SECTION("tuple_contains") { + using namespace ecs::detail; + { + REQUIRE_FALSE(tuple_contains(std::make_tuple(), nullptr)); + REQUIRE_FALSE(tuple_contains(std::make_tuple(1), 0)); + REQUIRE_FALSE(tuple_contains(std::make_tuple(1), 2)); + REQUIRE(tuple_contains(std::make_tuple(1), 1)); + REQUIRE(tuple_contains(std::make_tuple(1,2,3), 1)); + REQUIRE(tuple_contains(std::make_tuple(1,2,3), 2)); + REQUIRE(tuple_contains(std::make_tuple(1,2,3), 3)); + REQUIRE_FALSE(tuple_contains(std::make_tuple(1,2,3), 0)); + REQUIRE_FALSE(tuple_contains(std::make_tuple(1,2,3), 4)); + } + } + SECTION("entity_id") { + using namespace ecs::detail; + { + REQUIRE(entity_id_index(entity_id_join(10u, 20u)) == 10u); + REQUIRE(entity_id_version(entity_id_join(10u, 20u)) == 20u); + REQUIRE(upgrade_entity_id(entity_id_join(10u, 20u)) == entity_id_join(10u, 21u)); + REQUIRE(upgrade_entity_id(entity_id_join(0u, 1023u)) == entity_id_join(0u, 0u)); + REQUIRE(upgrade_entity_id(entity_id_join(1u, 1023u)) == entity_id_join(1u, 0u)); + REQUIRE(upgrade_entity_id(entity_id_join(2048u, 1023u)) == entity_id_join(2048u, 0u)); + } + } SECTION("sparse_set") { using namespace ecs::detail; { @@ -87,7 +112,6 @@ TEST_CASE("detail") { REQUIRE(s.empty()); REQUIRE_FALSE(s.size()); - REQUIRE(s.capacity() == 0u); REQUIRE_FALSE(s.has(42u)); REQUIRE(s.find(42u) == s.end()); REQUIRE_FALSE(s.find_dense_index(42u).second); @@ -97,7 +121,6 @@ TEST_CASE("detail") { REQUIRE_FALSE(s.empty()); REQUIRE(s.size() == 1u); - REQUIRE(s.capacity() == 85u); REQUIRE(s.has(42u)); REQUIRE_FALSE(s.has(84u)); @@ -121,7 +144,6 @@ TEST_CASE("detail") { REQUIRE_FALSE(s.has(84u)); REQUIRE(s.empty()); REQUIRE_FALSE(s.size()); - REQUIRE(s.capacity() == 85u * 2); s.insert(42u); s.insert(84u); @@ -175,7 +197,6 @@ TEST_CASE("detail") { REQUIRE(m.empty()); REQUIRE_FALSE(m.size()); - REQUIRE(m.capacity() == 0u); REQUIRE_FALSE(m.has(42u)); REQUIRE_THROWS(m.get(42u)); REQUIRE_THROWS(as_const(m).get(42u)); @@ -198,7 +219,6 @@ TEST_CASE("detail") { REQUIRE_FALSE(m.empty()); REQUIRE(m.size() == 3u); - REQUIRE(m.capacity() >= 3u); REQUIRE(m.has(21u)); REQUIRE(m.has(42u)); REQUIRE(m.has(84u)); @@ -459,12 +479,6 @@ TEST_CASE("registry") { REQUIRE_THROWS_AS(ww.get_component(e1), std::logic_error); REQUIRE_THROWS_AS(ww.get_component(e2), std::logic_error); - - ww.remove_all_components(e1); - ww.remove_all_components(e2); - - REQUIRE_FALSE(ww.find_component(e1)); - REQUIRE_FALSE(ww.find_component(e2)); } } { @@ -545,6 +559,30 @@ TEST_CASE("registry") { REQUIRE(e1.get_component().y == 40); } } + SECTION("for_each_entity") { + { + ecs::registry w; + + auto e1 = w.create_entity(); + auto e2 = w.create_entity(); + + { + ecs::entity_id acc1 = 0; + w.for_each_entity([&acc1](const ecs::entity& e){ + acc1 += e.id(); + }); + REQUIRE(acc1 == e1.id() + e2.id()); + } + { + const ecs::registry& ww = w; + ecs::entity_id acc1 = 0; + ww.for_each_entity([&acc1](const ecs::const_entity& e){ + acc1 += e.id(); + }); + REQUIRE(acc1 == e1.id() + e2.id()); + } + } + } SECTION("for_each_component") { { ecs::registry w; @@ -572,7 +610,7 @@ TEST_CASE("registry") { const ecs::registry& ww = w; ecs::entity_id acc1 = 0; int acc2 = 0; - ww.for_each_component([&acc1, &acc2](ecs::entity e, const position_c& p){ + ww.for_each_component([&acc1, &acc2](ecs::const_entity e, const position_c& p){ acc1 += e.id(); acc2 += p.x; }); @@ -645,7 +683,7 @@ TEST_CASE("registry") { ecs::entity_id acc1 = 0; int acc2 = 0; ww.for_joined_components([&acc1, &acc2]( - ecs::entity e, const position_c& p, const velocity_c& v) + ecs::const_entity e, const position_c& p, const velocity_c& v) { acc1 += e.id(); acc2 += p.x + v.x; @@ -747,6 +785,7 @@ TEST_CASE("example") { ecs_hpp::registry world; world.add_system(); + world.add_system(9.8f); auto entity_one = world.create_entity(); world.assign_component(entity_one, 4.f, 2.f);