mirror of
https://github.com/BlackMATov/ecs.hpp.git
synced 2025-12-13 10:35:39 +07:00
61
README.md
61
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<movement_system>();
|
||||
|
||||
auto entity_one = world.create_entity();
|
||||
world.assign_component<position_component>(entity_one, 4.f, 2.f);
|
||||
world.assign_component<velocity_component>(entity_one, 10.f, 20.f);
|
||||
|
||||
auto entity_two = world.create_entity();
|
||||
entity_two.assign_component<position_component>(4.f, 2.f);
|
||||
entity_two.assign_component<velocity_component>(10.f, 20.f);
|
||||
|
||||
world.process_systems();
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
|
||||
110
ecs.hpp
110
ecs.hpp
@@ -13,10 +13,8 @@
|
||||
#include <tuple>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <limits>
|
||||
#include <utility>
|
||||
#include <iterator>
|
||||
#include <exception>
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
@@ -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<family_id>::value,
|
||||
"ecs_hpp::family_id must be an unsigned integer");
|
||||
"ecs_hpp (family_id must be an unsigned integer)");
|
||||
|
||||
static_assert(
|
||||
std::is_unsigned<entity_id>::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<std::size_t,bool> find_index(const T& v) const noexcept {
|
||||
std::pair<std::size_t,bool> 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<T>::assign(entity_id id, Args&&... args) {
|
||||
if ( !components_.emplace(id, std::forward<Args>(args)...) ) {
|
||||
components_.get_value(id) = T(std::forward<Args>(args)...);
|
||||
components_.get(id) = T(std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -619,19 +629,19 @@ namespace ecs_hpp
|
||||
|
||||
template < typename T >
|
||||
T* component_storage<T>::find(entity_id id) noexcept {
|
||||
return components_.find_value(id);
|
||||
return components_.find(id);
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
const T* component_storage<T>::find(entity_id id) const noexcept {
|
||||
return components_.find_value(id);
|
||||
return components_.find(id);
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
template < typename F >
|
||||
void component_storage<T>::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<T>::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<T>::id();
|
||||
using raw_storage_ptr = detail::component_storage<T>*;
|
||||
return storages_.has(family)
|
||||
? static_cast<raw_storage_ptr>(storages_.get_value(family).get())
|
||||
? static_cast<raw_storage_ptr>(storages_.get(family).get())
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
@@ -1148,7 +1156,7 @@ namespace ecs_hpp
|
||||
const auto family = detail::type_family<T>::id();
|
||||
using raw_storage_ptr = const detail::component_storage<T>*;
|
||||
return storages_.has(family)
|
||||
? static_cast<raw_storage_ptr>(storages_.get_value(family).get())
|
||||
? static_cast<raw_storage_ptr>(storages_.get(family).get())
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
@@ -1163,7 +1171,7 @@ namespace ecs_hpp
|
||||
family,
|
||||
std::make_unique<detail::component_storage<T>>(*this));
|
||||
return *static_cast<detail::component_storage<T>*>(
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
178
ecs_tests.cpp
178
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<position_c, position_c_indexer> 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
|
||||
@@ -694,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<movement_system>();
|
||||
|
||||
auto entity_one = world.create_entity();
|
||||
world.assign_component<position_component>(entity_one, 4.f, 2.f);
|
||||
world.assign_component<velocity_component>(entity_one, 10.f, 20.f);
|
||||
|
||||
auto entity_two = world.create_entity();
|
||||
entity_two.assign_component<position_component>(4.f, 2.f);
|
||||
entity_two.assign_component<velocity_component>(10.f, 20.f);
|
||||
|
||||
world.process_systems();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user