mirror of
https://github.com/BlackMATov/ecs.hpp.git
synced 2025-12-13 10:35:39 +07:00
271
ecs.hpp
271
ecs.hpp
@@ -37,6 +37,12 @@ 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;
|
||||
|
||||
static_assert(
|
||||
std::is_unsigned<family_id>::value,
|
||||
"ecs_hpp::family_id must be an unsigned integer");
|
||||
@@ -44,6 +50,22 @@ namespace ecs_hpp
|
||||
static_assert(
|
||||
std::is_unsigned<entity_id>::value,
|
||||
"ecs_hpp::entity_id must be an unsigned integer");
|
||||
|
||||
static_assert(
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -72,13 +94,19 @@ namespace ecs_hpp
|
||||
namespace impl
|
||||
{
|
||||
template < typename T, typename... Ts, std::size_t... Is >
|
||||
std::tuple<Ts...> tuple_tail_impl(std::tuple<T, Ts...>&& t, std::index_sequence<Is...> iseq) {
|
||||
std::tuple<Ts...> tuple_tail_impl(
|
||||
std::tuple<T, Ts...>&& t,
|
||||
std::index_sequence<Is...> iseq)
|
||||
{
|
||||
(void)iseq;
|
||||
return std::make_tuple(std::move(std::get<Is + 1u>(t))...);
|
||||
}
|
||||
|
||||
template < typename T, typename... Ts, std::size_t... Is >
|
||||
std::tuple<Ts...> tuple_tail_impl(const std::tuple<T, Ts...>& t, std::index_sequence<Is...> iseq) {
|
||||
std::tuple<Ts...> tuple_tail_impl(
|
||||
const std::tuple<T, Ts...>& t,
|
||||
std::index_sequence<Is...> iseq)
|
||||
{
|
||||
(void)iseq;
|
||||
return std::make_tuple(std::get<Is + 1u>(t)...);
|
||||
}
|
||||
@@ -86,12 +114,16 @@ namespace ecs_hpp
|
||||
|
||||
template < typename T, typename... Ts >
|
||||
std::tuple<Ts...> tuple_tail(std::tuple<T, Ts...>&& t) {
|
||||
return impl::tuple_tail_impl(std::move(t), std::make_index_sequence<sizeof...(Ts)>());
|
||||
return impl::tuple_tail_impl(
|
||||
std::move(t),
|
||||
std::make_index_sequence<sizeof...(Ts)>());
|
||||
}
|
||||
|
||||
template < typename T, typename... Ts >
|
||||
std::tuple<Ts...> tuple_tail(const std::tuple<T, Ts...>& t) {
|
||||
return impl::tuple_tail_impl(t, std::make_index_sequence<sizeof...(Ts)>());
|
||||
return impl::tuple_tail_impl(
|
||||
t,
|
||||
std::make_index_sequence<sizeof...(Ts)>());
|
||||
}
|
||||
|
||||
//
|
||||
@@ -149,6 +181,33 @@ namespace ecs_hpp
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// detail::sparse_indexer
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
namespace ecs_hpp
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
template < typename T
|
||||
, bool = std::is_unsigned<T>::value && sizeof(T) <= sizeof(std::size_t) >
|
||||
struct sparse_unsigned_indexer {
|
||||
std::size_t operator()(const T v) const noexcept {
|
||||
return static_cast<std::size_t>(v);
|
||||
}
|
||||
};
|
||||
|
||||
template < typename T >
|
||||
struct sparse_unsigned_indexer<T, false> {};
|
||||
|
||||
template < typename T >
|
||||
struct sparse_indexer
|
||||
: public sparse_unsigned_indexer<T> {};
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// detail::sparse_set
|
||||
@@ -159,12 +218,10 @@ namespace ecs_hpp
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
template < typename T >
|
||||
template < typename T
|
||||
, typename Indexer = sparse_indexer<T> >
|
||||
class sparse_set final {
|
||||
public:
|
||||
static_assert(
|
||||
std::is_unsigned<T>::value,
|
||||
"sparse_set<T> can contain an unsigned integers only");
|
||||
using iterator = typename std::vector<T>::iterator;
|
||||
using const_iterator = typename std::vector<T>::const_iterator;
|
||||
public:
|
||||
@@ -195,27 +252,52 @@ namespace ecs_hpp
|
||||
return cbegin() + static_cast<dt>(size_);
|
||||
}
|
||||
public:
|
||||
bool insert(const T v) {
|
||||
sparse_set(const Indexer& indexer = Indexer())
|
||||
: indexer_(indexer) {}
|
||||
|
||||
bool insert(T&& v) {
|
||||
if ( has(v) ) {
|
||||
return false;
|
||||
}
|
||||
if ( v >= capacity_ ) {
|
||||
reserve(new_capacity_for_(v + 1u));
|
||||
const std::size_t vi = indexer_(v);
|
||||
if ( vi >= capacity_ ) {
|
||||
reserve(new_capacity_for_(vi + 1u));
|
||||
}
|
||||
dense_[size_] = v;
|
||||
sparse_[v] = size_;
|
||||
dense_[size_] = std::move(v);
|
||||
sparse_[vi] = size_;
|
||||
++size_;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool unordered_erase(const T v) noexcept {
|
||||
bool insert(const T& v) {
|
||||
if ( has(v) ) {
|
||||
return false;
|
||||
}
|
||||
const std::size_t vi = indexer_(v);
|
||||
if ( vi >= capacity_ ) {
|
||||
reserve(new_capacity_for_(vi + 1u));
|
||||
}
|
||||
dense_[size_] = v;
|
||||
sparse_[vi] = size_;
|
||||
++size_;
|
||||
return true;
|
||||
}
|
||||
|
||||
template < typename... Args >
|
||||
bool emplace(Args&&... args) {
|
||||
return insert(T(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
bool unordered_erase(const T& v)
|
||||
noexcept(std::is_nothrow_move_assignable<T>::value)
|
||||
{
|
||||
if ( !has(v) ) {
|
||||
return false;
|
||||
}
|
||||
const std::size_t index = sparse_[v];
|
||||
const T last = dense_[size_ - 1u];
|
||||
dense_[index] = last;
|
||||
sparse_[last] = index;
|
||||
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_;
|
||||
return true;
|
||||
}
|
||||
@@ -224,29 +306,31 @@ namespace ecs_hpp
|
||||
size_ = 0u;
|
||||
}
|
||||
|
||||
bool has(const T v) const noexcept {
|
||||
return v < capacity_
|
||||
&& sparse_[v] < size_
|
||||
&& dense_[sparse_[v]] == v;
|
||||
bool has(const T& v) const noexcept {
|
||||
const std::size_t vi = indexer_(v);
|
||||
return vi < capacity_
|
||||
&& sparse_[vi] < size_
|
||||
&& dense_[sparse_[vi]] == v;
|
||||
}
|
||||
|
||||
const_iterator find(const T v) const noexcept {
|
||||
const_iterator find(const T& v) const noexcept {
|
||||
const std::size_t vi = indexer_(v);
|
||||
return has(v)
|
||||
? begin() + sparse_[v]
|
||||
? begin() + sparse_[vi]
|
||||
: end();
|
||||
}
|
||||
|
||||
std::size_t get_index(const T v) const {
|
||||
std::size_t get_index(const T& v) const {
|
||||
const auto p = find_index(v);
|
||||
if ( p.second ) {
|
||||
return p.first;
|
||||
}
|
||||
throw std::out_of_range("sparse_set<T>");
|
||||
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_index(const T& v) const noexcept {
|
||||
return has(v)
|
||||
? std::make_pair(sparse_[v], true)
|
||||
? std::make_pair(sparse_[indexer_(v)], true)
|
||||
: std::make_pair(std::size_t(-1), false);
|
||||
}
|
||||
|
||||
@@ -281,7 +365,7 @@ namespace ecs_hpp
|
||||
std::size_t new_capacity_for_(std::size_t nsize) const {
|
||||
const std::size_t ms = max_size();
|
||||
if ( nsize > ms ) {
|
||||
throw std::length_error("sparse_set<T>");
|
||||
throw std::length_error("ecs_hpp::sparse_set");
|
||||
}
|
||||
if ( capacity_ >= ms / 2u ) {
|
||||
return ms;
|
||||
@@ -289,6 +373,7 @@ namespace ecs_hpp
|
||||
return std::max(capacity_ * 2u, nsize);
|
||||
}
|
||||
private:
|
||||
Indexer indexer_;
|
||||
std::vector<T> dense_;
|
||||
std::vector<std::size_t> sparse_;
|
||||
std::size_t size_{0u};
|
||||
@@ -307,12 +392,11 @@ namespace ecs_hpp
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
template < typename K, typename T >
|
||||
template < typename K
|
||||
, typename T
|
||||
, typename Indexer = sparse_indexer<K> >
|
||||
class sparse_map final {
|
||||
public:
|
||||
static_assert(
|
||||
std::is_unsigned<K>::value,
|
||||
"sparse_map<K,T> can contain unsigned integers keys only");
|
||||
using iterator = typename std::vector<K>::iterator;
|
||||
using const_iterator = typename std::vector<K>::const_iterator;
|
||||
public:
|
||||
@@ -340,7 +424,10 @@ namespace ecs_hpp
|
||||
return keys_.cend();
|
||||
}
|
||||
public:
|
||||
bool insert(const K k, const T& v) {
|
||||
sparse_map(const Indexer& indexer = Indexer())
|
||||
: keys_(indexer) {}
|
||||
|
||||
bool insert(const K& k, const T& v) {
|
||||
if ( keys_.has(k) ) {
|
||||
return false;
|
||||
}
|
||||
@@ -353,7 +440,7 @@ namespace ecs_hpp
|
||||
}
|
||||
}
|
||||
|
||||
bool insert(const K k, T&& v) {
|
||||
bool insert(const K& k, T&& v) {
|
||||
if ( keys_.has(k) ) {
|
||||
return false;
|
||||
}
|
||||
@@ -367,7 +454,7 @@ namespace ecs_hpp
|
||||
}
|
||||
|
||||
template < typename... Args >
|
||||
bool emplace(const K k, Args&&... args) {
|
||||
bool emplace(const K& k, Args&&... args) {
|
||||
if ( keys_.has(k) ) {
|
||||
return false;
|
||||
}
|
||||
@@ -380,7 +467,10 @@ namespace ecs_hpp
|
||||
}
|
||||
}
|
||||
|
||||
bool unordered_erase(const K k) {
|
||||
std::enable_if_t<
|
||||
std::is_nothrow_move_assignable<K>::value,
|
||||
bool>
|
||||
unordered_erase(const K& k) {
|
||||
if ( !keys_.has(k) ) {
|
||||
return false;
|
||||
}
|
||||
@@ -396,26 +486,26 @@ namespace ecs_hpp
|
||||
values_.clear();
|
||||
}
|
||||
|
||||
bool has(const K k) const noexcept {
|
||||
bool has(const K& k) const noexcept {
|
||||
return keys_.has(k);
|
||||
}
|
||||
|
||||
T& get_value(const K k) {
|
||||
T& get_value(const K& k) {
|
||||
return values_[keys_.get_index(k)];
|
||||
}
|
||||
|
||||
const T& get_value(const K k) const {
|
||||
const T& get_value(const K& k) const {
|
||||
return values_[keys_.get_index(k)];
|
||||
}
|
||||
|
||||
T* find_value(const K k) noexcept {
|
||||
T* find_value(const K& k) noexcept {
|
||||
const auto ip = keys_.find_index(k);
|
||||
return ip.second
|
||||
? &values_[ip.first]
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
const T* find_value(const K k) const noexcept {
|
||||
const T* find_value(const K& k) const noexcept {
|
||||
const auto ip = keys_.find_index(k);
|
||||
return ip.second
|
||||
? &values_[ip.first]
|
||||
@@ -443,12 +533,30 @@ namespace ecs_hpp
|
||||
return values_.capacity();
|
||||
}
|
||||
private:
|
||||
sparse_set<K> keys_;
|
||||
sparse_set<K, Indexer> keys_;
|
||||
std::vector<T> values_;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// detail::entity_id_indexer
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
namespace ecs_hpp
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
struct entity_id_indexer {
|
||||
std::size_t operator()(entity_id id) const noexcept {
|
||||
return entity_id_index(id);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// detail::component_storage
|
||||
@@ -484,7 +592,7 @@ namespace ecs_hpp
|
||||
void for_each_component(F&& f) const noexcept;
|
||||
private:
|
||||
registry& owner_;
|
||||
detail::sparse_map<entity_id, T> components_;
|
||||
detail::sparse_map<entity_id, T, entity_id_indexer> components_;
|
||||
};
|
||||
|
||||
template < typename T >
|
||||
@@ -537,21 +645,6 @@ namespace ecs_hpp
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// exceptions
|
||||
//
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
namespace ecs_hpp
|
||||
{
|
||||
class basic_exception : public std::logic_error {
|
||||
public:
|
||||
basic_exception(const char* msg)
|
||||
: std::logic_error(msg) {}
|
||||
};
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// entity
|
||||
@@ -604,7 +697,7 @@ namespace ecs_hpp
|
||||
template < typename... Ts >
|
||||
std::tuple<const Ts*...> find_components() const noexcept;
|
||||
private:
|
||||
registry& owner_;
|
||||
registry* owner_;
|
||||
entity_id id_{0u};
|
||||
};
|
||||
|
||||
@@ -761,7 +854,7 @@ namespace ecs_hpp
|
||||
private:
|
||||
entity_id last_entity_id_{0u};
|
||||
std::vector<entity_id> free_entity_ids_;
|
||||
detail::sparse_set<entity_id> entity_ids_;
|
||||
detail::sparse_set<entity_id, detail::entity_id_indexer> entity_ids_;
|
||||
|
||||
using storage_uptr = std::unique_ptr<detail::component_storage_base>;
|
||||
detail::sparse_map<family_id, storage_uptr> storages_;
|
||||
@@ -780,14 +873,14 @@ namespace ecs_hpp
|
||||
namespace ecs_hpp
|
||||
{
|
||||
inline entity::entity(registry& owner)
|
||||
: owner_(owner) {}
|
||||
: owner_(&owner) {}
|
||||
|
||||
inline entity::entity(registry& owner, entity_id id)
|
||||
: owner_(owner)
|
||||
: owner_(&owner)
|
||||
, id_(id) {}
|
||||
|
||||
inline const registry& entity::owner() const noexcept {
|
||||
return owner_;
|
||||
return *owner_;
|
||||
}
|
||||
|
||||
inline entity_id entity::id() const noexcept {
|
||||
@@ -795,72 +888,72 @@ namespace ecs_hpp
|
||||
}
|
||||
|
||||
inline bool entity::destroy() {
|
||||
return owner_.destroy_entity(*this);
|
||||
return (*owner_).destroy_entity(*this);
|
||||
}
|
||||
|
||||
inline bool entity::is_alive() const noexcept {
|
||||
return detail::as_const(owner_).is_entity_alive(*this);
|
||||
return detail::as_const(*owner_).is_entity_alive(*this);
|
||||
}
|
||||
|
||||
template < typename T, typename... Args >
|
||||
bool entity::assign_component(Args&&... args) {
|
||||
return owner_.assign_component<T>(
|
||||
return (*owner_).assign_component<T>(
|
||||
*this,
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
bool entity::remove_component() {
|
||||
return owner_.remove_component<T>(*this);
|
||||
return (*owner_).remove_component<T>(*this);
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
bool entity::exists_component() const noexcept {
|
||||
return detail::as_const(owner_).exists_component<T>(*this);
|
||||
return detail::as_const(*owner_).exists_component<T>(*this);
|
||||
}
|
||||
|
||||
inline std::size_t entity::remove_all_components() noexcept {
|
||||
return owner_.remove_all_components(*this);
|
||||
return (*owner_).remove_all_components(*this);
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
T& entity::get_component() {
|
||||
return owner_.get_component<T>(*this);
|
||||
return (*owner_).get_component<T>(*this);
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
const T& entity::get_component() const {
|
||||
return detail::as_const(owner_).get_component<T>(*this);
|
||||
return detail::as_const(*owner_).get_component<T>(*this);
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
T* entity::find_component() noexcept {
|
||||
return owner_.find_component<T>(*this);
|
||||
return (*owner_).find_component<T>(*this);
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
const T* entity::find_component() const noexcept {
|
||||
return detail::as_const(owner_).find_component<T>(*this);
|
||||
return detail::as_const(*owner_).find_component<T>(*this);
|
||||
}
|
||||
|
||||
template < typename... Ts >
|
||||
std::tuple<Ts&...> entity::get_components() {
|
||||
return owner_.get_components<Ts...>(*this);
|
||||
return (*owner_).get_components<Ts...>(*this);
|
||||
}
|
||||
|
||||
template < typename... Ts >
|
||||
std::tuple<const Ts&...> entity::get_components() const {
|
||||
return detail::as_const(owner_).get_components<Ts...>(*this);
|
||||
return detail::as_const(*owner_).get_components<Ts...>(*this);
|
||||
}
|
||||
|
||||
template < typename... Ts >
|
||||
std::tuple<Ts*...> entity::find_components() noexcept {
|
||||
return owner_.find_components<Ts...>(*this);
|
||||
return (*owner_).find_components<Ts...>(*this);
|
||||
}
|
||||
|
||||
template < typename... Ts >
|
||||
std::tuple<const Ts*...> entity::find_components() const noexcept {
|
||||
return detail::as_const(owner_).find_components<Ts...>(*this);
|
||||
return detail::as_const(*owner_).find_components<Ts...>(*this);
|
||||
}
|
||||
|
||||
inline bool operator==(const entity& l, const entity& r) noexcept {
|
||||
@@ -883,16 +976,22 @@ namespace ecs_hpp
|
||||
{
|
||||
inline entity registry::create_entity() {
|
||||
if ( !free_entity_ids_.empty() ) {
|
||||
auto ent = entity(*this, free_entity_ids_.back());
|
||||
entity_ids_.insert(ent.id());
|
||||
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);
|
||||
auto ent = entity(*this, new_ent_id);
|
||||
entity_ids_.insert(new_ent_id);
|
||||
free_entity_ids_.pop_back();
|
||||
return ent;
|
||||
|
||||
}
|
||||
assert(last_entity_id_ < std::numeric_limits<entity_id>::max());
|
||||
auto ent = entity(*this, ++last_entity_id_);
|
||||
entity_ids_.insert(ent.id());
|
||||
return ent;
|
||||
if ( last_entity_id_ < 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)");
|
||||
}
|
||||
|
||||
inline bool registry::destroy_entity(const entity& ent) {
|
||||
@@ -951,7 +1050,7 @@ namespace ecs_hpp
|
||||
if ( component ) {
|
||||
return *component;
|
||||
}
|
||||
throw basic_exception("component not found");
|
||||
throw std::logic_error("ecs_hpp::registry(component not found)");
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
@@ -960,7 +1059,7 @@ namespace ecs_hpp
|
||||
if ( component ) {
|
||||
return *component;
|
||||
}
|
||||
throw basic_exception("component not found");
|
||||
throw std::logic_error("ecs_hpp::registry(component not found)");
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
|
||||
125
ecs_tests.cpp
125
ecs_tests.cpp
@@ -37,6 +37,19 @@ namespace
|
||||
return l.x == r.x
|
||||
&& l.y == r.y;
|
||||
}
|
||||
|
||||
struct mult_indexer {
|
||||
template < typename T >
|
||||
std::size_t operator()(const T& v) const noexcept {
|
||||
return static_cast<std::size_t>(v * 2);
|
||||
}
|
||||
};
|
||||
|
||||
struct position_c_indexer {
|
||||
std::size_t operator()(const position_c& v) const noexcept {
|
||||
return static_cast<std::size_t>(v.x);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
TEST_CASE("detail") {
|
||||
@@ -70,7 +83,7 @@ TEST_CASE("detail") {
|
||||
SECTION("sparse_set") {
|
||||
using namespace ecs::detail;
|
||||
{
|
||||
sparse_set<unsigned> s;
|
||||
sparse_set<unsigned, mult_indexer> s{mult_indexer{}};
|
||||
|
||||
REQUIRE(s.empty());
|
||||
REQUIRE_FALSE(s.size());
|
||||
@@ -84,7 +97,7 @@ TEST_CASE("detail") {
|
||||
|
||||
REQUIRE_FALSE(s.empty());
|
||||
REQUIRE(s.size() == 1u);
|
||||
REQUIRE(s.capacity() == 43u);
|
||||
REQUIRE(s.capacity() == 85u);
|
||||
REQUIRE(s.has(42u));
|
||||
REQUIRE_FALSE(s.has(84u));
|
||||
|
||||
@@ -108,7 +121,7 @@ TEST_CASE("detail") {
|
||||
REQUIRE_FALSE(s.has(84u));
|
||||
REQUIRE(s.empty());
|
||||
REQUIRE_FALSE(s.size());
|
||||
REQUIRE(s.capacity() == 43u * 2);
|
||||
REQUIRE(s.capacity() == 85u * 2);
|
||||
|
||||
s.insert(42u);
|
||||
s.insert(84u);
|
||||
@@ -133,6 +146,22 @@ TEST_CASE("detail") {
|
||||
REQUIRE_THROWS(s.get_index(42u));
|
||||
REQUIRE(s.get_index(84u) == 0u);
|
||||
}
|
||||
{
|
||||
sparse_set<position_c, position_c_indexer> s{position_c_indexer()};
|
||||
REQUIRE(s.insert(position_c(1,2)));
|
||||
REQUIRE_FALSE(s.insert(position_c(1,2)));
|
||||
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.unordered_erase(position_c(1,2)));
|
||||
REQUIRE(s.get_index(position_c(3,4)) == 0);
|
||||
}
|
||||
}
|
||||
SECTION("sparse_map") {
|
||||
using namespace ecs::detail;
|
||||
@@ -210,6 +239,27 @@ TEST_CASE("detail") {
|
||||
REQUIRE_FALSE(m.has(42u));
|
||||
REQUIRE_FALSE(m.has(84u));
|
||||
}
|
||||
{
|
||||
struct obj_t {
|
||||
int x;
|
||||
obj_t(int nx) : x(nx) {}
|
||||
};
|
||||
|
||||
sparse_map<position_c, obj_t, position_c_indexer> s{position_c_indexer()};
|
||||
REQUIRE(s.insert(position_c(1,2), obj_t{1}));
|
||||
REQUIRE_FALSE(s.insert(position_c(1,2), obj_t{1}));
|
||||
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.unordered_erase(position_c(1,2)));
|
||||
REQUIRE(s.get_value(position_c(3,4)).x == 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,9 +303,40 @@ TEST_CASE("registry") {
|
||||
ecs::registry w;
|
||||
|
||||
const auto e1 = w.create_entity();
|
||||
|
||||
w.destroy_entity(e1);
|
||||
const auto e2 = w.create_entity();
|
||||
REQUIRE(e1 == e2);
|
||||
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()));
|
||||
|
||||
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()));
|
||||
}
|
||||
{
|
||||
ecs::registry w;
|
||||
auto e = w.create_entity();
|
||||
const auto e_id = e.id();
|
||||
for ( std::size_t i = 0; i < ecs::entity_id_version_mask; ++i ) {
|
||||
e.destroy();
|
||||
e = w.create_entity();
|
||||
REQUIRE(ecs::entity_id_version(e_id) != ecs::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()));
|
||||
}
|
||||
{
|
||||
ecs::registry w;
|
||||
for ( std::size_t i = 0; i < ecs::entity_id_index_mask; ++i ) {
|
||||
w.create_entity();
|
||||
}
|
||||
// entity index overflow
|
||||
REQUIRE_THROWS_AS(w.create_entity(), std::logic_error);
|
||||
}
|
||||
}
|
||||
SECTION("component_assigning") {
|
||||
@@ -345,8 +426,8 @@ TEST_CASE("registry") {
|
||||
REQUIRE(e2.get_component<velocity_c>().x == 3);
|
||||
REQUIRE(e2.get_component<velocity_c>().y == 4);
|
||||
|
||||
REQUIRE_THROWS_AS(e1.get_component<velocity_c>(), ecs::basic_exception);
|
||||
REQUIRE_THROWS_AS(e2.get_component<position_c>(), ecs::basic_exception);
|
||||
REQUIRE_THROWS_AS(e1.get_component<velocity_c>(), std::logic_error);
|
||||
REQUIRE_THROWS_AS(e2.get_component<position_c>(), std::logic_error);
|
||||
}
|
||||
{
|
||||
ecs::registry w;
|
||||
@@ -371,8 +452,8 @@ TEST_CASE("registry") {
|
||||
REQUIRE(ww.get_component<velocity_c>(e2).x == 3);
|
||||
REQUIRE(ww.get_component<velocity_c>(e2).y == 4);
|
||||
|
||||
REQUIRE_THROWS_AS(ww.get_component<velocity_c>(e1), ecs::basic_exception);
|
||||
REQUIRE_THROWS_AS(ww.get_component<position_c>(e2), ecs::basic_exception);
|
||||
REQUIRE_THROWS_AS(ww.get_component<velocity_c>(e1), std::logic_error);
|
||||
REQUIRE_THROWS_AS(ww.get_component<position_c>(e2), std::logic_error);
|
||||
|
||||
ww.remove_all_components(e1);
|
||||
ww.remove_all_components(e2);
|
||||
@@ -494,6 +575,34 @@ TEST_CASE("registry") {
|
||||
REQUIRE(acc2 == 6);
|
||||
}
|
||||
}
|
||||
{
|
||||
ecs::registry w;
|
||||
|
||||
{
|
||||
auto e1 = w.create_entity();
|
||||
auto e2 = w.create_entity();
|
||||
|
||||
e1.destroy();
|
||||
e2.destroy();
|
||||
}
|
||||
|
||||
auto e3 = w.create_entity();
|
||||
auto e4 = w.create_entity();
|
||||
|
||||
e3.assign_component<position_c>(1, 2);
|
||||
e4.assign_component<position_c>(3, 4);
|
||||
|
||||
{
|
||||
ecs::entity_id acc1 = 0;
|
||||
int acc2 = 0;
|
||||
w.for_each_component<position_c>([&acc1, &acc2](ecs::entity e, position_c& p){
|
||||
acc1 += e.id();
|
||||
acc2 += p.x;
|
||||
});
|
||||
REQUIRE(acc1 == e3.id() + e4.id());
|
||||
REQUIRE(acc2 == 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
SECTION("for_joined_components") {
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user