From 98bfc99a0ddf90e927d10d854ab559bcee2752a0 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Fri, 28 Dec 2018 09:23:38 +0700 Subject: [PATCH 1/2] name refactoring --- ecs.hpp | 110 ++++++++++++++++++++++++---------------------- ecs_tests.cpp | 119 ++++++++++++++++++++++++++------------------------ 2 files changed, 121 insertions(+), 108 deletions(-) diff --git a/ecs.hpp b/ecs.hpp index c801ba2..9fa9a01 100644 --- a/ecs.hpp +++ b/ecs.hpp @@ -13,10 +13,8 @@ #include #include #include -#include #include #include -#include #include #include #include @@ -37,35 +35,22 @@ namespace ecs_hpp using family_id = std::uint16_t; using entity_id = std::uint32_t; - const std::size_t entity_id_index_bits = 22; - const std::size_t entity_id_index_mask = (1 << entity_id_index_bits) - 1; - - const std::size_t entity_id_version_bits = 10; - const std::size_t entity_id_version_mask = (1 << entity_id_version_bits) - 1; + constexpr std::size_t entity_id_index_bits = 22u; + constexpr std::size_t entity_id_version_bits = 10u; static_assert( std::is_unsigned::value, - "ecs_hpp::family_id must be an unsigned integer"); + "ecs_hpp (family_id must be an unsigned integer)"); static_assert( std::is_unsigned::value, - "ecs_hpp::entity_id must be an unsigned integer"); + "ecs_hpp (entity_id must be an unsigned integer)"); static_assert( + entity_id_index_bits > 0u && + entity_id_version_bits > 0u && sizeof(entity_id) == (entity_id_index_bits + entity_id_version_bits) / 8u, - "ecs_hpp::entity_id mismatch index and version bits"); - - constexpr inline entity_id entity_id_index(entity_id id) noexcept { - return id & entity_id_index_mask; - } - - constexpr inline entity_id entity_id_version(entity_id id) noexcept { - return (id >> entity_id_index_bits) & entity_id_version_mask; - } - - constexpr inline entity_id entity_id_join(entity_id index, entity_id version) noexcept { - return index | (version << entity_id_index_bits); - } + "ecs_hpp (invalid entity id index and version bits)"); } // ----------------------------------------------------------------------------- @@ -144,6 +129,31 @@ namespace ecs_hpp } return tuple_contains(tuple_tail(t), v); } + + // + // entity_id index/version + // + + constexpr std::size_t entity_id_index_mask = (1u << entity_id_index_bits) - 1u; + constexpr std::size_t entity_id_version_mask = (1u << entity_id_version_bits) - 1u; + + constexpr inline entity_id entity_id_index(entity_id id) noexcept { + return id & entity_id_index_mask; + } + + constexpr inline entity_id entity_id_version(entity_id id) noexcept { + return (id >> entity_id_index_bits) & entity_id_version_mask; + } + + constexpr inline entity_id entity_id_join(entity_id index, entity_id version) noexcept { + return index | (version << entity_id_index_bits); + } + + constexpr inline entity_id upgrade_entity_id(entity_id id) noexcept { + return entity_id_join( + entity_id_index(id), + entity_id_version(id) + 1u); + } } } @@ -320,15 +330,15 @@ namespace ecs_hpp : end(); } - std::size_t get_index(const T& v) const { - const auto p = find_index(v); + std::size_t get_dense_index(const T& v) const { + const auto p = find_dense_index(v); if ( p.second ) { return p.first; } - throw std::logic_error("ecs_hpp::sparse_set(value not found)"); + throw std::logic_error("ecs_hpp::sparse_set (value not found)"); } - std::pair find_index(const T& v) const noexcept { + std::pair find_dense_index(const T& v) const noexcept { return has(v) ? std::make_pair(sparse_[indexer_(v)], true) : std::make_pair(std::size_t(-1), false); @@ -474,7 +484,7 @@ namespace ecs_hpp if ( !keys_.has(k) ) { return false; } - const std::size_t index = keys_.get_index(k); + const std::size_t index = keys_.get_dense_index(k); values_[index] = std::move(values_.back()); values_.pop_back(); keys_.unordered_erase(k); @@ -490,23 +500,23 @@ namespace ecs_hpp return keys_.has(k); } - T& get_value(const K& k) { - return values_[keys_.get_index(k)]; + T& get(const K& k) { + return values_[keys_.get_dense_index(k)]; } - const T& get_value(const K& k) const { - return values_[keys_.get_index(k)]; + const T& get(const K& k) const { + return values_[keys_.get_dense_index(k)]; } - T* find_value(const K& k) noexcept { - const auto ip = keys_.find_index(k); + T* find(const K& k) noexcept { + const auto ip = keys_.find_dense_index(k); return ip.second ? &values_[ip.first] : nullptr; } - const T* find_value(const K& k) const noexcept { - const auto ip = keys_.find_index(k); + const T* find(const K& k) const noexcept { + const auto ip = keys_.find_dense_index(k); return ip.second ? &values_[ip.first] : nullptr; @@ -603,7 +613,7 @@ namespace ecs_hpp template < typename... Args > void component_storage::assign(entity_id id, Args&&... args) { if ( !components_.emplace(id, std::forward(args)...) ) { - components_.get_value(id) = T(std::forward(args)...); + components_.get(id) = T(std::forward(args)...); } } @@ -619,19 +629,19 @@ namespace ecs_hpp template < typename T > T* component_storage::find(entity_id id) noexcept { - return components_.find_value(id); + return components_.find(id); } template < typename T > const T* component_storage::find(entity_id id) const noexcept { - return components_.find_value(id); + return components_.find(id); } template < typename T > template < typename F > void component_storage::for_each_component(F&& f) noexcept { for ( const auto id : components_ ) { - f(entity(owner_, id), components_.get_value(id)); + f(entity(owner_, id), components_.get(id)); } } @@ -639,7 +649,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_value(id)); + f(entity(owner_, id), components_.get(id)); } } } @@ -977,21 +987,19 @@ namespace ecs_hpp inline entity registry::create_entity() { if ( !free_entity_ids_.empty() ) { const auto free_ent_id = free_entity_ids_.back(); - const auto new_ent_id = entity_id_join( - entity_id_index(free_ent_id), - entity_id_version(free_ent_id) + 1u); + const auto new_ent_id = detail::upgrade_entity_id(free_ent_id); auto ent = entity(*this, new_ent_id); entity_ids_.insert(new_ent_id); free_entity_ids_.pop_back(); return ent; } - if ( last_entity_id_ < entity_id_index_mask ) { + if ( last_entity_id_ < detail::entity_id_index_mask ) { auto ent = entity(*this, ++last_entity_id_); entity_ids_.insert(ent.id()); return ent; } - throw std::logic_error("ecs_hpp::registry(entity index overlow)"); + throw std::logic_error("ecs_hpp::registry (entity index overlow)"); } inline bool registry::destroy_entity(const entity& ent) { @@ -1050,7 +1058,7 @@ namespace ecs_hpp if ( component ) { return *component; } - throw std::logic_error("ecs_hpp::registry(component not found)"); + throw std::logic_error("ecs_hpp::registry (component not found)"); } template < typename T > @@ -1059,7 +1067,7 @@ namespace ecs_hpp if ( component ) { return *component; } - throw std::logic_error("ecs_hpp::registry(component not found)"); + throw std::logic_error("ecs_hpp::registry (component not found)"); } template < typename T > @@ -1139,7 +1147,7 @@ namespace ecs_hpp const auto family = detail::type_family::id(); using raw_storage_ptr = detail::component_storage*; return storages_.has(family) - ? static_cast(storages_.get_value(family).get()) + ? static_cast(storages_.get(family).get()) : nullptr; } @@ -1148,7 +1156,7 @@ namespace ecs_hpp const auto family = detail::type_family::id(); using raw_storage_ptr = const detail::component_storage*; return storages_.has(family) - ? static_cast(storages_.get_value(family).get()) + ? static_cast(storages_.get(family).get()) : nullptr; } @@ -1163,7 +1171,7 @@ namespace ecs_hpp family, std::make_unique>(*this)); return *static_cast*>( - storages_.get_value(family).get()); + storages_.get(family).get()); } inline bool registry::is_entity_alive_impl_(const entity& ent) const noexcept { @@ -1176,7 +1184,7 @@ namespace ecs_hpp } std::size_t removed_components = 0u; for ( const auto id : storages_ ) { - if ( storages_.get_value(id)->remove(ent.id()) ) { + if ( storages_.get(id)->remove(ent.id()) ) { ++removed_components; } } diff --git a/ecs_tests.cpp b/ecs_tests.cpp index 277dd58..e36c0cc 100644 --- a/ecs_tests.cpp +++ b/ecs_tests.cpp @@ -90,8 +90,8 @@ TEST_CASE("detail") { REQUIRE(s.capacity() == 0u); REQUIRE_FALSE(s.has(42u)); REQUIRE(s.find(42u) == s.end()); - REQUIRE_FALSE(s.find_index(42u).second); - REQUIRE_THROWS(s.get_index(42u)); + REQUIRE_FALSE(s.find_dense_index(42u).second); + REQUIRE_THROWS(s.get_dense_index(42u)); REQUIRE(s.insert(42u)); @@ -102,9 +102,9 @@ TEST_CASE("detail") { REQUIRE_FALSE(s.has(84u)); REQUIRE(s.find(42u) == s.begin()); - REQUIRE(s.find_index(42u).second); - REQUIRE(s.find_index(42u).first == 0u); - REQUIRE(s.get_index(42u) == 0u); + REQUIRE(s.find_dense_index(42u).second); + REQUIRE(s.find_dense_index(42u).first == 0u); + REQUIRE(s.get_dense_index(42u) == 0u); s.clear(); @@ -129,22 +129,22 @@ TEST_CASE("detail") { REQUIRE(s.has(42u)); REQUIRE(s.has(84u)); REQUIRE(s.size() == 2u); - REQUIRE(s.find_index(42u).second); - REQUIRE(s.find_index(42u).first == 0u); - REQUIRE(s.find_index(84u).second); - REQUIRE(s.find_index(84u).first == 1u); - REQUIRE(s.get_index(42u) == 0u); - REQUIRE(s.get_index(84u) == 1u); + REQUIRE(s.find_dense_index(42u).second); + REQUIRE(s.find_dense_index(42u).first == 0u); + REQUIRE(s.find_dense_index(84u).second); + REQUIRE(s.find_dense_index(84u).first == 1u); + REQUIRE(s.get_dense_index(42u) == 0u); + REQUIRE(s.get_dense_index(84u) == 1u); REQUIRE(s.unordered_erase(42u)); REQUIRE_FALSE(s.has(42u)); REQUIRE(s.has(84u)); REQUIRE(s.size() == 1u); - REQUIRE(s.find_index(84u).second); - REQUIRE(s.find_index(84u).first == 0u); - REQUIRE_THROWS(s.get_index(42u)); - REQUIRE(s.get_index(84u) == 0u); + REQUIRE(s.find_dense_index(84u).second); + REQUIRE(s.find_dense_index(84u).first == 0u); + REQUIRE_THROWS(s.get_dense_index(42u)); + REQUIRE(s.get_dense_index(84u) == 0u); } { sparse_set s{position_c_indexer()}; @@ -153,14 +153,14 @@ TEST_CASE("detail") { REQUIRE(s.has(position_c(1,2))); REQUIRE(s.emplace(3,4)); REQUIRE(s.has(position_c(3,4))); - REQUIRE(s.get_index(position_c(1,2)) == 0); - REQUIRE(s.get_index(position_c(3,4)) == 1); - REQUIRE(s.find_index(position_c(1,2)).first == 0); - REQUIRE(s.find_index(position_c(3,4)).first == 1); - REQUIRE(s.find_index(position_c(1,2)).second); - REQUIRE(s.find_index(position_c(3,4)).second); + REQUIRE(s.get_dense_index(position_c(1,2)) == 0); + REQUIRE(s.get_dense_index(position_c(3,4)) == 1); + REQUIRE(s.find_dense_index(position_c(1,2)).first == 0); + REQUIRE(s.find_dense_index(position_c(3,4)).first == 1); + REQUIRE(s.find_dense_index(position_c(1,2)).second); + REQUIRE(s.find_dense_index(position_c(3,4)).second); REQUIRE(s.unordered_erase(position_c(1,2))); - REQUIRE(s.get_index(position_c(3,4)) == 0); + REQUIRE(s.get_dense_index(position_c(3,4)) == 0); } } SECTION("sparse_map") { @@ -177,10 +177,10 @@ TEST_CASE("detail") { REQUIRE_FALSE(m.size()); REQUIRE(m.capacity() == 0u); REQUIRE_FALSE(m.has(42u)); - REQUIRE_THROWS(m.get_value(42u)); - REQUIRE_THROWS(as_const(m).get_value(42u)); - REQUIRE_FALSE(m.find_value(42u)); - REQUIRE_FALSE(as_const(m).find_value(42u)); + REQUIRE_THROWS(m.get(42u)); + REQUIRE_THROWS(as_const(m).get(42u)); + REQUIRE_FALSE(m.find(42u)); + REQUIRE_FALSE(as_const(m).find(42u)); { obj_t o{21u}; @@ -206,23 +206,23 @@ TEST_CASE("detail") { REQUIRE_FALSE(m.has(25u)); REQUIRE_FALSE(m.has(99u)); - REQUIRE(m.get_value(21u).x == 21u); - REQUIRE(m.get_value(42u).x == 42u); - REQUIRE(m.get_value(84u).x == 84u); - REQUIRE(as_const(m).get_value(84u).x == 84u); - REQUIRE_THROWS(m.get_value(11u)); - REQUIRE_THROWS(m.get_value(25u)); - REQUIRE_THROWS(m.get_value(99u)); - REQUIRE_THROWS(as_const(m).get_value(99u)); + REQUIRE(m.get(21u).x == 21u); + REQUIRE(m.get(42u).x == 42u); + REQUIRE(m.get(84u).x == 84u); + REQUIRE(as_const(m).get(84u).x == 84u); + REQUIRE_THROWS(m.get(11u)); + REQUIRE_THROWS(m.get(25u)); + REQUIRE_THROWS(m.get(99u)); + REQUIRE_THROWS(as_const(m).get(99u)); - REQUIRE(m.find_value(21u)->x == 21u); - REQUIRE(m.find_value(42u)->x == 42u); - REQUIRE(m.find_value(84u)->x == 84u); - REQUIRE(as_const(m).find_value(84u)->x == 84u); - REQUIRE_FALSE(m.find_value(11u)); - REQUIRE_FALSE(m.find_value(25u)); - REQUIRE_FALSE(m.find_value(99u)); - REQUIRE_FALSE(as_const(m).find_value(99u)); + REQUIRE(m.find(21u)->x == 21u); + REQUIRE(m.find(42u)->x == 42u); + REQUIRE(m.find(84u)->x == 84u); + REQUIRE(as_const(m).find(84u)->x == 84u); + REQUIRE_FALSE(m.find(11u)); + REQUIRE_FALSE(m.find(25u)); + REQUIRE_FALSE(m.find(99u)); + REQUIRE_FALSE(as_const(m).find(99u)); REQUIRE(m.unordered_erase(42u)); REQUIRE_FALSE(m.unordered_erase(42u)); @@ -251,14 +251,14 @@ TEST_CASE("detail") { REQUIRE(s.has(position_c(1,2))); REQUIRE(s.emplace(position_c(3,4), obj_t{3})); REQUIRE(s.has(position_c(3,4))); - REQUIRE(s.get_value(position_c(1,2)).x == 1); - REQUIRE(s.get_value(position_c(3,4)).x == 3); - REQUIRE(s.find_value(position_c(1,2))->x == 1); - REQUIRE(s.find_value(position_c(3,4))->x == 3); - REQUIRE(s.find_value(position_c(1,2))); - REQUIRE(s.find_value(position_c(3,4))); + REQUIRE(s.get(position_c(1,2)).x == 1); + REQUIRE(s.get(position_c(3,4)).x == 3); + REQUIRE(s.find(position_c(1,2))->x == 1); + REQUIRE(s.find(position_c(3,4))->x == 3); + REQUIRE(s.find(position_c(1,2))); + REQUIRE(s.find(position_c(3,4))); REQUIRE(s.unordered_erase(position_c(1,2))); - REQUIRE(s.get_value(position_c(3,4)).x == 3); + REQUIRE(s.get(position_c(3,4)).x == 3); } } } @@ -301,38 +301,43 @@ TEST_CASE("registry") { } { ecs::registry w; + using namespace ecs::detail; const auto e1 = w.create_entity(); w.destroy_entity(e1); const auto e2 = w.create_entity(); REQUIRE(e1 != e2); - REQUIRE(ecs::entity_id_index(e1.id()) == ecs::entity_id_index(e2.id())); - REQUIRE(ecs::entity_id_version(e1.id()) + 1 == ecs::entity_id_version(e2.id())); + REQUIRE(entity_id_index(e1.id()) == entity_id_index(e2.id())); + REQUIRE(entity_id_version(e1.id()) + 1 == entity_id_version(e2.id())); w.destroy_entity(e2); const auto e3 = w.create_entity(); REQUIRE(e3 != e2); - REQUIRE(ecs::entity_id_index(e2.id()) == ecs::entity_id_index(e3.id())); - REQUIRE(ecs::entity_id_version(e2.id()) + 1 == ecs::entity_id_version(e3.id())); + REQUIRE(entity_id_index(e2.id()) == entity_id_index(e3.id())); + REQUIRE(entity_id_version(e2.id()) + 1 == entity_id_version(e3.id())); } { ecs::registry w; + using namespace ecs::detail; + auto e = w.create_entity(); const auto e_id = e.id(); - for ( std::size_t i = 0; i < ecs::entity_id_version_mask; ++i ) { + for ( std::size_t i = 0; i < entity_id_version_mask; ++i ) { e.destroy(); e = w.create_entity(); - REQUIRE(ecs::entity_id_version(e_id) != ecs::entity_id_version(e.id())); + REQUIRE(entity_id_version(e_id) != entity_id_version(e.id())); } // entity version wraps around e.destroy(); e = w.create_entity(); - REQUIRE(ecs::entity_id_version(e_id) == ecs::entity_id_version(e.id())); + REQUIRE(entity_id_version(e_id) == entity_id_version(e.id())); } { ecs::registry w; - for ( std::size_t i = 0; i < ecs::entity_id_index_mask; ++i ) { + using namespace ecs::detail; + + for ( std::size_t i = 0; i < entity_id_index_mask; ++i ) { w.create_entity(); } // entity index overflow From 3393a4479ec9d7ddaa3ceb39b20bf0af353cf010 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Fri, 28 Dec 2018 09:42:34 +0700 Subject: [PATCH 2/2] basic usage example --- README.md | 61 +++++++++++++++++++++++++++++++++++++++++++++++++-- ecs_tests.cpp | 59 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9adeaf2..5ac6014 100644 --- a/README.md +++ b/README.md @@ -31,9 +31,66 @@ #include "ecs.hpp" ``` -## Examples +## Basic usage -> coming soon! +```cpp +struct position_component { + float x{0}; + float y{0}; + position_component(float nx, float ny) + : x(nx), y(ny) {} +}; + +struct velocity_component { + float dx{0}; + float dy{0}; + velocity_component(float ndx, float ndy) + : dx(ndx), dy(ndy) {} +}; + +class movement_system : public ecs_hpp::system { +public: + void process(ecs_hpp::registry& owner) override { + owner.for_joined_components< + position_component, + velocity_component + >([](const ecs_hpp::entity& e, position_component& p, const velocity_component& v) { + p.x += v.dx; + p.y += v.dy; + }); + } +}; + +class gravity_system : public ecs_hpp::system { +public: + gravity_system(float gravity) + : gravity_(gravity) {} + + void process(ecs_hpp::registry& owner) override { + owner.for_each_component< + velocity_component + >([this](const ecs_hpp::entity& e, velocity_component& v) { + v.dx += gravity_; + v.dy += gravity_; + }); + } +private: + float gravity_; +}; + +ecs_hpp::registry world; +world.add_system(); + +auto entity_one = world.create_entity(); +world.assign_component(entity_one, 4.f, 2.f); +world.assign_component(entity_one, 10.f, 20.f); + +auto entity_two = world.create_entity(); +entity_two.assign_component(4.f, 2.f); +entity_two.assign_component(10.f, 20.f); + +world.process_systems(); +``` ## API diff --git a/ecs_tests.cpp b/ecs_tests.cpp index e36c0cc..21fe6e2 100644 --- a/ecs_tests.cpp +++ b/ecs_tests.cpp @@ -699,3 +699,62 @@ TEST_CASE("registry") { } } } + +TEST_CASE("example") { + struct position_component { + float x{0}; + float y{0}; + position_component(float nx, float ny) + : x(nx), y(ny) {} + }; + + struct velocity_component { + float dx{0}; + float dy{0}; + velocity_component(float ndx, float ndy) + : dx(ndx), dy(ndy) {} + }; + + class movement_system : public ecs_hpp::system { + public: + void process(ecs_hpp::registry& owner) override { + owner.for_joined_components< + position_component, + velocity_component + >([](const ecs_hpp::entity& e, position_component& p, const velocity_component& v) { + p.x += v.dx; + p.y += v.dy; + }); + } + }; + + class gravity_system : public ecs_hpp::system { + public: + gravity_system(float gravity) + : gravity_(gravity) {} + + void process(ecs_hpp::registry& owner) override { + owner.for_each_component< + velocity_component + >([this](const ecs_hpp::entity& e, velocity_component& v) { + v.dx += gravity_; + v.dy += gravity_; + }); + } + private: + float gravity_; + }; + + ecs_hpp::registry world; + world.add_system(); + + auto entity_one = world.create_entity(); + world.assign_component(entity_one, 4.f, 2.f); + world.assign_component(entity_one, 10.f, 20.f); + + auto entity_two = world.create_entity(); + entity_two.assign_component(4.f, 2.f); + entity_two.assign_component(10.f, 20.f); + + world.process_systems(); +}