mirror of
https://github.com/BlackMATov/ecs.hpp.git
synced 2025-12-15 11:53:51 +07:00
542
ecs.hpp
542
ecs.hpp
@@ -37,9 +37,14 @@ namespace ecs_hpp
|
|||||||
template < typename T >
|
template < typename T >
|
||||||
class const_component;
|
class const_component;
|
||||||
|
|
||||||
|
class prototype;
|
||||||
|
|
||||||
class system;
|
class system;
|
||||||
class registry;
|
class registry;
|
||||||
|
|
||||||
|
class entity_filler;
|
||||||
|
class registry_filler;
|
||||||
|
|
||||||
using family_id = std::uint16_t;
|
using family_id = std::uint16_t;
|
||||||
using entity_id = std::uint32_t;
|
using entity_id = std::uint32_t;
|
||||||
using priority_t = std::int32_t;
|
using priority_t = std::int32_t;
|
||||||
@@ -157,6 +162,44 @@ namespace ecs_hpp
|
|||||||
return impl::tuple_contains_impl<0>(t, v);
|
return impl::tuple_contains_impl<0>(t, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// tiny_tuple_apply
|
||||||
|
//
|
||||||
|
|
||||||
|
namespace impl
|
||||||
|
{
|
||||||
|
template < typename F, typename Tuple, std::size_t... I >
|
||||||
|
void tiny_tuple_apply_impl(F&& f, Tuple&& args, std::index_sequence<I...>) {
|
||||||
|
std::forward<F>(f)(std::get<I>(std::forward<Tuple>(args))...);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template < typename F, typename Tuple >
|
||||||
|
void tiny_tuple_apply(F&& f, Tuple&& args) {
|
||||||
|
impl::tiny_tuple_apply_impl(
|
||||||
|
std::forward<F>(f),
|
||||||
|
std::forward<Tuple>(args),
|
||||||
|
std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>());
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// next_capacity_size
|
||||||
|
//
|
||||||
|
|
||||||
|
inline std::size_t next_capacity_size(
|
||||||
|
std::size_t cur_size,
|
||||||
|
std::size_t min_size,
|
||||||
|
std::size_t max_size)
|
||||||
|
{
|
||||||
|
if ( min_size > max_size ) {
|
||||||
|
throw std::length_error("ecs_hpp::next_capacity_size");
|
||||||
|
}
|
||||||
|
if ( cur_size >= max_size / 2u ) {
|
||||||
|
return max_size;
|
||||||
|
}
|
||||||
|
return std::max(cur_size * 2u, min_size);
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// entity_id index/version
|
// entity_id index/version
|
||||||
//
|
//
|
||||||
@@ -262,6 +305,15 @@ namespace ecs_hpp
|
|||||||
public:
|
public:
|
||||||
using iterator = typename std::vector<T>::iterator;
|
using iterator = typename std::vector<T>::iterator;
|
||||||
using const_iterator = typename std::vector<T>::const_iterator;
|
using const_iterator = typename std::vector<T>::const_iterator;
|
||||||
|
public:
|
||||||
|
static_assert(
|
||||||
|
noexcept(std::declval<Indexer>()(std::declval<T>())),
|
||||||
|
"unsupported sparse_set indexer");
|
||||||
|
static_assert(
|
||||||
|
std::is_nothrow_destructible<T>::value &&
|
||||||
|
std::is_nothrow_move_assignable<T>::value &&
|
||||||
|
noexcept(std::declval<T>() == std::declval<T>()),
|
||||||
|
"unsupported sparse_set value type");
|
||||||
public:
|
public:
|
||||||
iterator begin() noexcept {
|
iterator begin() noexcept {
|
||||||
return dense_.begin();
|
return dense_.begin();
|
||||||
@@ -290,28 +342,29 @@ namespace ecs_hpp
|
|||||||
sparse_set(const Indexer& indexer = Indexer())
|
sparse_set(const Indexer& indexer = Indexer())
|
||||||
: indexer_(indexer) {}
|
: indexer_(indexer) {}
|
||||||
|
|
||||||
bool insert(T&& v) {
|
sparse_set(const sparse_set& other) = default;
|
||||||
if ( has(v) ) {
|
sparse_set& operator=(const sparse_set& other) = default;
|
||||||
return false;
|
|
||||||
}
|
sparse_set(sparse_set&& other) noexcept = default;
|
||||||
const std::size_t vi = indexer_(v);
|
sparse_set& operator=(sparse_set&& other) noexcept = default;
|
||||||
if ( vi >= sparse_.size() ) {
|
|
||||||
sparse_.resize(new_sparse_size_for_(vi + 1u));
|
void swap(sparse_set& other) noexcept {
|
||||||
}
|
using std::swap;
|
||||||
dense_.push_back(std::move(v));
|
swap(dense_, other.dense_);
|
||||||
sparse_[vi] = dense_.size() - 1u;
|
swap(sparse_, other.sparse_);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool insert(const T& v) {
|
template < typename UT >
|
||||||
|
bool insert(UT&& v) {
|
||||||
if ( has(v) ) {
|
if ( has(v) ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const std::size_t vi = indexer_(v);
|
const std::size_t vi = indexer_(v);
|
||||||
if ( vi >= sparse_.size() ) {
|
if ( vi >= sparse_.size() ) {
|
||||||
sparse_.resize(new_sparse_size_for_(vi + 1u));
|
sparse_.resize(next_capacity_size(
|
||||||
|
sparse_.size(), vi + 1u, sparse_.max_size()));
|
||||||
}
|
}
|
||||||
dense_.push_back(v);
|
dense_.push_back(std::forward<UT>(v));
|
||||||
sparse_[vi] = dense_.size() - 1u;
|
sparse_[vi] = dense_.size() - 1u;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -321,16 +374,16 @@ namespace ecs_hpp
|
|||||||
return insert(T(std::forward<Args>(args)...));
|
return insert(T(std::forward<Args>(args)...));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool unordered_erase(const T& v)
|
bool unordered_erase(const T& v) noexcept {
|
||||||
noexcept(std::is_nothrow_move_assignable<T>::value)
|
|
||||||
{
|
|
||||||
if ( !has(v) ) {
|
if ( !has(v) ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const std::size_t vi = indexer_(v);
|
const std::size_t vi = indexer_(v);
|
||||||
const std::size_t dense_index = sparse_[vi];
|
const std::size_t dense_index = sparse_[vi];
|
||||||
|
if ( dense_index != dense_.size() - 1 ) {
|
||||||
dense_[dense_index] = std::move(dense_.back());
|
dense_[dense_index] = std::move(dense_.back());
|
||||||
sparse_[indexer_(dense_[dense_index])] = dense_index;
|
sparse_[indexer_(dense_[dense_index])] = dense_index;
|
||||||
|
}
|
||||||
dense_.pop_back();
|
dense_.pop_back();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -347,9 +400,8 @@ namespace ecs_hpp
|
|||||||
}
|
}
|
||||||
|
|
||||||
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)
|
return has(v)
|
||||||
? begin() + sparse_[vi]
|
? begin() + sparse_[indexer_(v)]
|
||||||
: end();
|
: end();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -374,22 +426,20 @@ namespace ecs_hpp
|
|||||||
std::size_t size() const noexcept {
|
std::size_t size() const noexcept {
|
||||||
return dense_.size();
|
return dense_.size();
|
||||||
}
|
}
|
||||||
private:
|
|
||||||
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 ( sparse_.size() >= ms / 2u ) {
|
|
||||||
return ms;
|
|
||||||
}
|
|
||||||
return std::max(sparse_.size() * 2u, nsize);
|
|
||||||
}
|
|
||||||
private:
|
private:
|
||||||
Indexer indexer_;
|
Indexer indexer_;
|
||||||
std::vector<T> dense_;
|
std::vector<T> dense_;
|
||||||
std::vector<std::size_t> sparse_;
|
std::vector<std::size_t> sparse_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template < typename T
|
||||||
|
, typename Indexer >
|
||||||
|
void swap(
|
||||||
|
sparse_set<T, Indexer>& l,
|
||||||
|
sparse_set<T, Indexer>& r) noexcept
|
||||||
|
{
|
||||||
|
l.swap(r);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -410,6 +460,11 @@ namespace ecs_hpp
|
|||||||
public:
|
public:
|
||||||
using iterator = typename std::vector<K>::iterator;
|
using iterator = typename std::vector<K>::iterator;
|
||||||
using const_iterator = typename std::vector<K>::const_iterator;
|
using const_iterator = typename std::vector<K>::const_iterator;
|
||||||
|
public:
|
||||||
|
static_assert(
|
||||||
|
std::is_nothrow_destructible<T>::value &&
|
||||||
|
std::is_nothrow_move_assignable<T>::value,
|
||||||
|
"unsupported sparse_map value type");
|
||||||
public:
|
public:
|
||||||
iterator begin() noexcept {
|
iterator begin() noexcept {
|
||||||
return keys_.begin();
|
return keys_.begin();
|
||||||
@@ -438,57 +493,65 @@ namespace ecs_hpp
|
|||||||
sparse_map(const Indexer& indexer = Indexer())
|
sparse_map(const Indexer& indexer = Indexer())
|
||||||
: keys_(indexer) {}
|
: keys_(indexer) {}
|
||||||
|
|
||||||
bool insert(const K& k, const T& v) {
|
sparse_map(const sparse_map& other) = default;
|
||||||
|
sparse_map& operator=(const sparse_map& other) = default;
|
||||||
|
|
||||||
|
sparse_map(sparse_map&& other) noexcept = default;
|
||||||
|
sparse_map& operator=(sparse_map&& other) noexcept = default;
|
||||||
|
|
||||||
|
void swap(sparse_map& other) noexcept {
|
||||||
|
using std::swap;
|
||||||
|
swap(keys_, other.keys_);
|
||||||
|
swap(values_, other.values_);
|
||||||
|
}
|
||||||
|
|
||||||
|
template < typename UK, typename UT >
|
||||||
|
bool insert(UK&& k, UT&& v) {
|
||||||
if ( keys_.has(k) ) {
|
if ( keys_.has(k) ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
values_.push_back(v);
|
values_.push_back(std::forward<UT>(v));
|
||||||
try {
|
try {
|
||||||
return keys_.insert(k);
|
return keys_.insert(std::forward<UK>(k));
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
values_.pop_back();
|
values_.pop_back();
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool insert(const K& k, T&& v) {
|
template < typename UK, typename... Args >
|
||||||
if ( keys_.has(k) ) {
|
bool emplace(UK&& k, Args&&... args) {
|
||||||
return false;
|
|
||||||
}
|
|
||||||
values_.push_back(std::move(v));
|
|
||||||
try {
|
|
||||||
return keys_.insert(k);
|
|
||||||
} catch (...) {
|
|
||||||
values_.pop_back();
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template < typename... Args >
|
|
||||||
bool emplace(const K& k, Args&&... args) {
|
|
||||||
if ( keys_.has(k) ) {
|
if ( keys_.has(k) ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
values_.emplace_back(std::forward<Args>(args)...);
|
values_.emplace_back(std::forward<Args>(args)...);
|
||||||
try {
|
try {
|
||||||
return keys_.insert(k);
|
return keys_.insert(std::forward<UK>(k));
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
values_.pop_back();
|
values_.pop_back();
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::enable_if_t<
|
template < typename UK, typename UT >
|
||||||
std::is_nothrow_move_assignable<K>::value,
|
bool insert_or_assign(UK&& k, UT&& v) {
|
||||||
bool>
|
if ( keys_.has(k) ) {
|
||||||
unordered_erase(const K& k)
|
get(k) = std::forward<UT>(v);
|
||||||
noexcept(std::is_nothrow_move_assignable<T>::value)
|
return false;
|
||||||
{
|
} else {
|
||||||
if ( !keys_.has(k) ) {
|
insert(std::forward<UK>(k), std::forward<UT>(v));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool unordered_erase(const K& k) noexcept {
|
||||||
|
const auto value_index_p = keys_.find_dense_index(k);
|
||||||
|
if ( !value_index_p.second ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const std::size_t value_index = keys_.get_dense_index(k);
|
if ( value_index_p.first != values_.size() - 1 ) {
|
||||||
values_[value_index] = std::move(values_.back());
|
values_[value_index_p.first] = std::move(values_.back());
|
||||||
|
}
|
||||||
values_.pop_back();
|
values_.pop_back();
|
||||||
keys_.unordered_erase(k);
|
keys_.unordered_erase(k);
|
||||||
return true;
|
return true;
|
||||||
@@ -536,6 +599,16 @@ namespace ecs_hpp
|
|||||||
sparse_set<K, Indexer> keys_;
|
sparse_set<K, Indexer> keys_;
|
||||||
std::vector<T> values_;
|
std::vector<T> values_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template < typename K
|
||||||
|
, typename T
|
||||||
|
, typename Indexer >
|
||||||
|
void swap(
|
||||||
|
sparse_map<K, T, Indexer>& l,
|
||||||
|
sparse_map<K, T, Indexer>& r) noexcept
|
||||||
|
{
|
||||||
|
l.swap(r);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -572,6 +645,7 @@ namespace ecs_hpp
|
|||||||
virtual ~component_storage_base() = default;
|
virtual ~component_storage_base() = default;
|
||||||
virtual bool remove(entity_id id) noexcept = 0;
|
virtual bool remove(entity_id id) noexcept = 0;
|
||||||
virtual bool has(entity_id id) const noexcept = 0;
|
virtual bool has(entity_id id) const noexcept = 0;
|
||||||
|
virtual void clone(entity_id from, entity_id to) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
template < typename T >
|
template < typename T >
|
||||||
@@ -580,7 +654,7 @@ namespace ecs_hpp
|
|||||||
component_storage(registry& owner);
|
component_storage(registry& owner);
|
||||||
|
|
||||||
template < typename... Args >
|
template < typename... Args >
|
||||||
void assign(entity_id id, Args&&... args);
|
T& assign(entity_id id, Args&&... args);
|
||||||
bool exists(entity_id id) const noexcept;
|
bool exists(entity_id id) const noexcept;
|
||||||
bool remove(entity_id id) noexcept override;
|
bool remove(entity_id id) noexcept override;
|
||||||
|
|
||||||
@@ -589,6 +663,7 @@ namespace ecs_hpp
|
|||||||
|
|
||||||
std::size_t count() const noexcept;
|
std::size_t count() const noexcept;
|
||||||
bool has(entity_id id) const noexcept override;
|
bool has(entity_id id) const noexcept override;
|
||||||
|
void clone(entity_id from, entity_id to) override;
|
||||||
|
|
||||||
template < typename F >
|
template < typename F >
|
||||||
void for_each_component(F&& f);
|
void for_each_component(F&& f);
|
||||||
@@ -605,10 +680,9 @@ namespace ecs_hpp
|
|||||||
|
|
||||||
template < typename T >
|
template < typename T >
|
||||||
template < typename... Args >
|
template < typename... Args >
|
||||||
void component_storage<T>::assign(entity_id id, Args&&... args) {
|
T& component_storage<T>::assign(entity_id id, Args&&... args) {
|
||||||
if ( !components_.emplace(id, std::forward<Args>(args)...) ) {
|
components_.insert_or_assign(id, T(std::forward<Args>(args)...));
|
||||||
components_.get(id) = T(std::forward<Args>(args)...);
|
return components_.get(id);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template < typename T >
|
template < typename T >
|
||||||
@@ -641,10 +715,18 @@ namespace ecs_hpp
|
|||||||
return components_.has(id);
|
return components_.has(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template < typename T >
|
||||||
|
void component_storage<T>::clone(entity_id from, entity_id to) {
|
||||||
|
const T* c = components_.find(from);
|
||||||
|
if ( c ) {
|
||||||
|
components_.insert_or_assign(to, *c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template < typename T >
|
template < typename T >
|
||||||
template < typename F >
|
template < typename F >
|
||||||
void component_storage<T>::for_each_component(F&& f) {
|
void component_storage<T>::for_each_component(F&& f) {
|
||||||
for ( const auto id : components_ ) {
|
for ( const entity_id id : components_ ) {
|
||||||
f(id, components_.get(id));
|
f(id, components_.get(id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -652,7 +734,7 @@ namespace ecs_hpp
|
|||||||
template < typename T >
|
template < typename T >
|
||||||
template < typename F >
|
template < typename F >
|
||||||
void component_storage<T>::for_each_component(F&& f) const {
|
void component_storage<T>::for_each_component(F&& f) const {
|
||||||
for ( const auto id : components_ ) {
|
for ( const entity_id id : components_ ) {
|
||||||
f(id, components_.get(id));
|
f(id, components_.get(id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -683,11 +765,12 @@ namespace ecs_hpp
|
|||||||
|
|
||||||
entity_id id() const noexcept;
|
entity_id id() const noexcept;
|
||||||
|
|
||||||
bool destroy();
|
entity clone() const;
|
||||||
bool alive() const noexcept;
|
void destroy() noexcept;
|
||||||
|
bool valid() const noexcept;
|
||||||
|
|
||||||
template < typename T, typename... Args >
|
template < typename T, typename... Args >
|
||||||
bool assign_component(Args&&... args);
|
T& assign_component(Args&&... args);
|
||||||
|
|
||||||
template < typename T >
|
template < typename T >
|
||||||
bool remove_component();
|
bool remove_component();
|
||||||
@@ -718,6 +801,8 @@ namespace ecs_hpp
|
|||||||
std::tuple<Ts*...> find_components() noexcept;
|
std::tuple<Ts*...> find_components() noexcept;
|
||||||
template < typename... Ts >
|
template < typename... Ts >
|
||||||
std::tuple<const Ts*...> find_components() const noexcept;
|
std::tuple<const Ts*...> find_components() const noexcept;
|
||||||
|
|
||||||
|
std::size_t component_count() const noexcept;
|
||||||
private:
|
private:
|
||||||
registry* owner_{nullptr};
|
registry* owner_{nullptr};
|
||||||
entity_id id_{0u};
|
entity_id id_{0u};
|
||||||
@@ -770,7 +855,7 @@ namespace ecs_hpp
|
|||||||
const registry& owner() const noexcept;
|
const registry& owner() const noexcept;
|
||||||
entity_id id() const noexcept;
|
entity_id id() const noexcept;
|
||||||
|
|
||||||
bool alive() const noexcept;
|
bool valid() const noexcept;
|
||||||
|
|
||||||
template < typename T >
|
template < typename T >
|
||||||
bool exists_component() const noexcept;
|
bool exists_component() const noexcept;
|
||||||
@@ -786,6 +871,8 @@ namespace ecs_hpp
|
|||||||
|
|
||||||
template < typename... Ts >
|
template < typename... Ts >
|
||||||
std::tuple<const Ts*...> find_components() const noexcept;
|
std::tuple<const Ts*...> find_components() const noexcept;
|
||||||
|
|
||||||
|
std::size_t component_count() const noexcept;
|
||||||
private:
|
private:
|
||||||
const registry* owner_{nullptr};
|
const registry* owner_{nullptr};
|
||||||
entity_id id_{0u};
|
entity_id id_{0u};
|
||||||
@@ -840,7 +927,7 @@ namespace ecs_hpp
|
|||||||
bool exists() const noexcept;
|
bool exists() const noexcept;
|
||||||
|
|
||||||
template < typename... Args >
|
template < typename... Args >
|
||||||
bool assign(Args&&... args);
|
T& assign(Args&&... args);
|
||||||
|
|
||||||
T& get();
|
T& get();
|
||||||
const T& get() const;
|
const T& get() const;
|
||||||
@@ -933,6 +1020,71 @@ namespace std
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// prototype
|
||||||
|
//
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace ecs_hpp
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
class component_applier_base;
|
||||||
|
using component_applier_uptr = std::unique_ptr<component_applier_base>;
|
||||||
|
|
||||||
|
class component_applier_base {
|
||||||
|
public:
|
||||||
|
virtual ~component_applier_base() = default;
|
||||||
|
virtual component_applier_uptr clone() const = 0;
|
||||||
|
virtual void apply(entity& ent, bool override) const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
template < typename T, typename... Args >
|
||||||
|
class component_applier final : public component_applier_base {
|
||||||
|
public:
|
||||||
|
component_applier(std::tuple<Args...>&& args);
|
||||||
|
component_applier(const std::tuple<Args...>& args);
|
||||||
|
component_applier_uptr clone() const override;
|
||||||
|
void apply(entity& ent, bool override) const override;
|
||||||
|
private:
|
||||||
|
std::tuple<Args...> args_;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class prototype final {
|
||||||
|
public:
|
||||||
|
prototype() = default;
|
||||||
|
~prototype() noexcept = default;
|
||||||
|
|
||||||
|
prototype(const prototype& other);
|
||||||
|
prototype& operator=(const prototype& other);
|
||||||
|
|
||||||
|
prototype(prototype&& other) noexcept;
|
||||||
|
prototype& operator=(prototype&& other) noexcept;
|
||||||
|
|
||||||
|
void clear() noexcept;
|
||||||
|
bool empty() const noexcept;
|
||||||
|
void swap(prototype& other) noexcept;
|
||||||
|
|
||||||
|
template < typename T, typename... Args >
|
||||||
|
prototype& assign_component(Args&&... args) &;
|
||||||
|
template < typename T, typename... Args >
|
||||||
|
prototype&& assign_component(Args&&... args) &&;
|
||||||
|
|
||||||
|
prototype& merge(const prototype& other, bool override) &;
|
||||||
|
prototype&& merge(const prototype& other, bool override) &&;
|
||||||
|
|
||||||
|
void apply(entity& ent, bool override) const;
|
||||||
|
private:
|
||||||
|
detail::sparse_map<
|
||||||
|
family_id,
|
||||||
|
detail::component_applier_uptr> appliers_;
|
||||||
|
};
|
||||||
|
|
||||||
|
void swap(prototype& l, prototype& r) noexcept;
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
//
|
//
|
||||||
// system
|
// system
|
||||||
@@ -1018,12 +1170,14 @@ namespace ecs_hpp
|
|||||||
const_component<T> wrap_component(const const_uentity& ent) const noexcept;
|
const_component<T> wrap_component(const const_uentity& ent) const noexcept;
|
||||||
|
|
||||||
entity create_entity();
|
entity create_entity();
|
||||||
|
entity create_entity(const prototype& proto);
|
||||||
|
entity create_entity(const const_uentity& proto);
|
||||||
|
|
||||||
bool destroy_entity(const uentity& ent);
|
void destroy_entity(const uentity& ent) noexcept;
|
||||||
bool alive_entity(const const_uentity& ent) const noexcept;
|
bool valid_entity(const const_uentity& ent) const noexcept;
|
||||||
|
|
||||||
template < typename T, typename... Args >
|
template < typename T, typename... Args >
|
||||||
bool assign_component(const uentity& ent, Args&&... args);
|
T& assign_component(const uentity& ent, Args&&... args);
|
||||||
|
|
||||||
template < typename T >
|
template < typename T >
|
||||||
bool remove_component(const uentity& ent);
|
bool remove_component(const uentity& ent);
|
||||||
@@ -1055,7 +1209,7 @@ namespace ecs_hpp
|
|||||||
|
|
||||||
template < typename T >
|
template < typename T >
|
||||||
std::size_t component_count() const noexcept;
|
std::size_t component_count() const noexcept;
|
||||||
template < typename T >
|
std::size_t entity_count() const noexcept;
|
||||||
std::size_t entity_component_count(const const_uentity& ent) const noexcept;
|
std::size_t entity_component_count(const const_uentity& ent) const noexcept;
|
||||||
|
|
||||||
template < typename F >
|
template < typename F >
|
||||||
@@ -1222,16 +1376,20 @@ namespace ecs_hpp
|
|||||||
return id_;
|
return id_;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool entity::destroy() {
|
inline entity entity::clone() const {
|
||||||
return (*owner_).destroy_entity(id_);
|
return (*owner_).create_entity(id_);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool entity::alive() const noexcept {
|
inline void entity::destroy() noexcept {
|
||||||
return detail::as_const(*owner_).alive_entity(id_);
|
(*owner_).destroy_entity(id_);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool entity::valid() const noexcept {
|
||||||
|
return detail::as_const(*owner_).valid_entity(id_);
|
||||||
}
|
}
|
||||||
|
|
||||||
template < typename T, typename... Args >
|
template < typename T, typename... Args >
|
||||||
bool entity::assign_component(Args&&... args) {
|
T& entity::assign_component(Args&&... args) {
|
||||||
return (*owner_).assign_component<T>(
|
return (*owner_).assign_component<T>(
|
||||||
id_,
|
id_,
|
||||||
std::forward<Args>(args)...);
|
std::forward<Args>(args)...);
|
||||||
@@ -1291,6 +1449,10 @@ namespace ecs_hpp
|
|||||||
return detail::as_const(*owner_).find_components<Ts...>(id_);
|
return detail::as_const(*owner_).find_components<Ts...>(id_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline std::size_t entity::component_count() const noexcept {
|
||||||
|
return detail::as_const(*owner_).entity_component_count(id_);
|
||||||
|
}
|
||||||
|
|
||||||
inline bool operator<(const entity& l, const entity& r) noexcept {
|
inline bool operator<(const entity& l, const entity& r) noexcept {
|
||||||
return (&l.owner() < &r.owner())
|
return (&l.owner() < &r.owner())
|
||||||
|| (&l.owner() == &r.owner() && l.id() < r.id());
|
|| (&l.owner() == &r.owner() && l.id() < r.id());
|
||||||
@@ -1342,8 +1504,8 @@ namespace ecs_hpp
|
|||||||
return id_;
|
return id_;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool const_entity::alive() const noexcept {
|
inline bool const_entity::valid() const noexcept {
|
||||||
return (*owner_).alive_entity(id_);
|
return (*owner_).valid_entity(id_);
|
||||||
}
|
}
|
||||||
|
|
||||||
template < typename T >
|
template < typename T >
|
||||||
@@ -1371,6 +1533,10 @@ namespace ecs_hpp
|
|||||||
return (*owner_).find_components<Ts...>(id_);
|
return (*owner_).find_components<Ts...>(id_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline std::size_t const_entity::component_count() const noexcept {
|
||||||
|
return (*owner_).entity_component_count(id_);
|
||||||
|
}
|
||||||
|
|
||||||
inline bool operator<(const const_entity& l, const const_entity& r) noexcept {
|
inline bool operator<(const const_entity& l, const const_entity& r) noexcept {
|
||||||
return (&l.owner() < &r.owner())
|
return (&l.owner() < &r.owner())
|
||||||
|| (&l.owner() == &r.owner() && l.id() < r.id());
|
|| (&l.owner() == &r.owner() && l.id() < r.id());
|
||||||
@@ -1429,7 +1595,7 @@ namespace ecs_hpp
|
|||||||
|
|
||||||
template < typename T >
|
template < typename T >
|
||||||
template < typename... Args >
|
template < typename... Args >
|
||||||
bool component<T>::assign(Args&&... args) {
|
T& component<T>::assign(Args&&... args) {
|
||||||
return owner_.assign_component<T>(
|
return owner_.assign_component<T>(
|
||||||
std::forward<Args>(args)...);
|
std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
@@ -1542,6 +1708,122 @@ namespace ecs_hpp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// prototype impl
|
||||||
|
//
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace ecs_hpp
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
template < typename T, typename... Args >
|
||||||
|
component_applier<T,Args...>::component_applier(std::tuple<Args...>&& args)
|
||||||
|
: args_(std::move(args)) {}
|
||||||
|
|
||||||
|
template < typename T, typename... Args >
|
||||||
|
component_applier<T,Args...>::component_applier(const std::tuple<Args...>& args)
|
||||||
|
: args_(args) {}
|
||||||
|
|
||||||
|
template < typename T, typename... Args >
|
||||||
|
component_applier_uptr component_applier<T,Args...>::clone() const {
|
||||||
|
return std::make_unique<component_applier>(args_);
|
||||||
|
}
|
||||||
|
|
||||||
|
template < typename T, typename... Args >
|
||||||
|
void component_applier<T,Args...>::apply(entity& ent, bool override) const {
|
||||||
|
detail::tiny_tuple_apply([&ent, override](const Args&... args){
|
||||||
|
if ( override || !ent.exists_component<T>() ) {
|
||||||
|
ent.assign_component<T>(args...);
|
||||||
|
}
|
||||||
|
}, args_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline prototype::prototype(const prototype& other) {
|
||||||
|
for ( const family_id id : other.appliers_ ) {
|
||||||
|
appliers_.insert(id, other.appliers_.get(id)->clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline prototype& prototype::operator=(const prototype& other) {
|
||||||
|
if ( this != &other ) {
|
||||||
|
prototype p(other);
|
||||||
|
swap(p);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline prototype::prototype(prototype&& other) noexcept
|
||||||
|
: appliers_(std::move(other.appliers_)) {}
|
||||||
|
|
||||||
|
inline prototype& prototype::operator=(prototype&& other) noexcept {
|
||||||
|
if ( this != &other ) {
|
||||||
|
swap(other);
|
||||||
|
other.clear();
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void prototype::clear() noexcept {
|
||||||
|
appliers_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool prototype::empty() const noexcept {
|
||||||
|
return appliers_.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void prototype::swap(prototype& other) noexcept {
|
||||||
|
using std::swap;
|
||||||
|
swap(appliers_, other.appliers_);
|
||||||
|
}
|
||||||
|
|
||||||
|
template < typename T, typename... Args >
|
||||||
|
prototype& prototype::assign_component(Args&&... args) & {
|
||||||
|
using applier_t = detail::component_applier<
|
||||||
|
T,
|
||||||
|
std::decay_t<Args>...>;
|
||||||
|
auto applier = std::make_unique<applier_t>(
|
||||||
|
std::make_tuple(std::forward<Args>(args)...));
|
||||||
|
const auto family = detail::type_family<T>::id();
|
||||||
|
appliers_.emplace(family, std::move(applier));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template < typename T, typename... Args >
|
||||||
|
prototype&& prototype::assign_component(Args&&... args) && {
|
||||||
|
assign_component<T>(std::forward<Args>(args)...);
|
||||||
|
return std::move(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
prototype& prototype::merge(const prototype& other, bool override) & {
|
||||||
|
for ( const auto family_id : other.appliers_ ) {
|
||||||
|
if ( override || !appliers_.has(family_id) ) {
|
||||||
|
appliers_.insert_or_assign(
|
||||||
|
family_id,
|
||||||
|
other.appliers_.get(family_id)->clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
prototype&& prototype::merge(const prototype& other, bool override) && {
|
||||||
|
merge(other, override);
|
||||||
|
return std::move(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void prototype::apply(entity& ent, bool override) const {
|
||||||
|
for ( const auto family_id : appliers_ ) {
|
||||||
|
appliers_.get(family_id)->apply(ent, override);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void swap(prototype& l, prototype& r) noexcept {
|
||||||
|
l.swap(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
//
|
//
|
||||||
// registry impl
|
// registry impl
|
||||||
@@ -1670,46 +1952,70 @@ namespace ecs_hpp
|
|||||||
return wrap_entity(new_ent_id);
|
return wrap_entity(new_ent_id);
|
||||||
|
|
||||||
}
|
}
|
||||||
if ( last_entity_id_ < detail::entity_id_index_mask ) {
|
if ( last_entity_id_ >= detail::entity_id_index_mask ) {
|
||||||
|
throw std::logic_error("ecs_hpp::registry (entity index overlow)");
|
||||||
|
}
|
||||||
|
if ( free_entity_ids_.capacity() <= entity_ids_.size() ) {
|
||||||
|
// ensure free entity ids capacity for safe (noexcept) entity destroying
|
||||||
|
free_entity_ids_.reserve(detail::next_capacity_size(
|
||||||
|
free_entity_ids_.capacity(),
|
||||||
|
entity_ids_.size() + 1,
|
||||||
|
free_entity_ids_.max_size()));
|
||||||
|
}
|
||||||
entity_ids_.insert(last_entity_id_ + 1);
|
entity_ids_.insert(last_entity_id_ + 1);
|
||||||
return wrap_entity(++last_entity_id_);
|
return wrap_entity(++last_entity_id_);
|
||||||
}
|
}
|
||||||
throw std::logic_error("ecs_hpp::registry (entity index overlow)");
|
|
||||||
|
inline entity registry::create_entity(const prototype& proto) {
|
||||||
|
auto ent = create_entity();
|
||||||
|
try {
|
||||||
|
proto.apply(ent, true);
|
||||||
|
} catch (...) {
|
||||||
|
destroy_entity(ent);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
return ent;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool registry::destroy_entity(const uentity& ent) {
|
inline entity registry::create_entity(const const_uentity& proto) {
|
||||||
assert(ent.check_owner(this));
|
assert(valid_entity(proto));
|
||||||
|
entity ent = create_entity();
|
||||||
|
try {
|
||||||
|
for ( const auto family_id : storages_ ) {
|
||||||
|
storages_.get(family_id)->clone(proto, ent.id());
|
||||||
|
}
|
||||||
|
} catch (...) {
|
||||||
|
destroy_entity(ent);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
return ent;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void registry::destroy_entity(const uentity& ent) noexcept {
|
||||||
|
assert(valid_entity(ent));
|
||||||
remove_all_components(ent);
|
remove_all_components(ent);
|
||||||
if ( entity_ids_.unordered_erase(ent) ) {
|
if ( entity_ids_.unordered_erase(ent) ) {
|
||||||
|
assert(free_entity_ids_.size() < free_entity_ids_.capacity());
|
||||||
free_entity_ids_.push_back(ent);
|
free_entity_ids_.push_back(ent);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool registry::alive_entity(const const_uentity& ent) const noexcept {
|
inline bool registry::valid_entity(const const_uentity& ent) const noexcept {
|
||||||
assert(ent.check_owner(this));
|
assert(ent.check_owner(this));
|
||||||
return entity_ids_.has(ent);
|
return entity_ids_.has(ent);
|
||||||
}
|
}
|
||||||
|
|
||||||
template < typename T, typename... Args >
|
template < typename T, typename... Args >
|
||||||
bool registry::assign_component(const uentity& ent, Args&&... args) {
|
T& registry::assign_component(const uentity& ent, Args&&... args) {
|
||||||
assert(ent.check_owner(this));
|
assert(valid_entity(ent));
|
||||||
if ( !alive_entity(ent) ) {
|
return get_or_create_storage_<T>().assign(
|
||||||
return false;
|
|
||||||
}
|
|
||||||
get_or_create_storage_<T>().assign(
|
|
||||||
ent,
|
ent,
|
||||||
std::forward<Args>(args)...);
|
std::forward<Args>(args)...);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template < typename T >
|
template < typename T >
|
||||||
bool registry::remove_component(const uentity& ent) {
|
bool registry::remove_component(const uentity& ent) {
|
||||||
assert(ent.check_owner(this));
|
assert(valid_entity(ent));
|
||||||
if ( !alive_entity(ent) ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
detail::component_storage<T>* storage = find_storage_<T>();
|
detail::component_storage<T>* storage = find_storage_<T>();
|
||||||
return storage
|
return storage
|
||||||
? storage->remove(ent)
|
? storage->remove(ent)
|
||||||
@@ -1718,10 +2024,7 @@ namespace ecs_hpp
|
|||||||
|
|
||||||
template < typename T >
|
template < typename T >
|
||||||
bool registry::exists_component(const const_uentity& ent) const noexcept {
|
bool registry::exists_component(const const_uentity& ent) const noexcept {
|
||||||
assert(ent.check_owner(this));
|
assert(valid_entity(ent));
|
||||||
if ( !alive_entity(ent) ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const detail::component_storage<T>* storage = find_storage_<T>();
|
const detail::component_storage<T>* storage = find_storage_<T>();
|
||||||
return storage
|
return storage
|
||||||
? storage->exists(ent)
|
? storage->exists(ent)
|
||||||
@@ -1729,10 +2032,7 @@ namespace ecs_hpp
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline std::size_t registry::remove_all_components(const uentity& ent) noexcept {
|
inline std::size_t registry::remove_all_components(const uentity& ent) noexcept {
|
||||||
assert(ent.check_owner(this));
|
assert(valid_entity(ent));
|
||||||
if ( !alive_entity(ent) ) {
|
|
||||||
return 0u;
|
|
||||||
}
|
|
||||||
std::size_t removed_count = 0u;
|
std::size_t removed_count = 0u;
|
||||||
for ( const auto family_id : storages_ ) {
|
for ( const auto family_id : storages_ ) {
|
||||||
if ( storages_.get(family_id)->remove(ent) ) {
|
if ( storages_.get(family_id)->remove(ent) ) {
|
||||||
@@ -1744,7 +2044,7 @@ namespace ecs_hpp
|
|||||||
|
|
||||||
template < typename T >
|
template < typename T >
|
||||||
T& registry::get_component(const uentity& ent) {
|
T& registry::get_component(const uentity& ent) {
|
||||||
assert(ent.check_owner(this));
|
assert(valid_entity(ent));
|
||||||
T* component = find_component<T>(ent);
|
T* component = find_component<T>(ent);
|
||||||
if ( component ) {
|
if ( component ) {
|
||||||
return *component;
|
return *component;
|
||||||
@@ -1754,7 +2054,7 @@ namespace ecs_hpp
|
|||||||
|
|
||||||
template < typename T >
|
template < typename T >
|
||||||
const T& registry::get_component(const const_uentity& ent) const {
|
const T& registry::get_component(const const_uentity& ent) const {
|
||||||
assert(ent.check_owner(this));
|
assert(valid_entity(ent));
|
||||||
const T* component = find_component<T>(ent);
|
const T* component = find_component<T>(ent);
|
||||||
if ( component ) {
|
if ( component ) {
|
||||||
return *component;
|
return *component;
|
||||||
@@ -1764,7 +2064,7 @@ namespace ecs_hpp
|
|||||||
|
|
||||||
template < typename T >
|
template < typename T >
|
||||||
T* registry::find_component(const uentity& ent) noexcept {
|
T* registry::find_component(const uentity& ent) noexcept {
|
||||||
assert(ent.check_owner(this));
|
assert(valid_entity(ent));
|
||||||
detail::component_storage<T>* storage = find_storage_<T>();
|
detail::component_storage<T>* storage = find_storage_<T>();
|
||||||
return storage
|
return storage
|
||||||
? storage->find(ent)
|
? storage->find(ent)
|
||||||
@@ -1773,7 +2073,7 @@ namespace ecs_hpp
|
|||||||
|
|
||||||
template < typename T >
|
template < typename T >
|
||||||
const T* registry::find_component(const const_uentity& ent) const noexcept {
|
const T* registry::find_component(const const_uentity& ent) const noexcept {
|
||||||
assert(ent.check_owner(this));
|
assert(valid_entity(ent));
|
||||||
const detail::component_storage<T>* storage = find_storage_<T>();
|
const detail::component_storage<T>* storage = find_storage_<T>();
|
||||||
return storage
|
return storage
|
||||||
? storage->find(ent)
|
? storage->find(ent)
|
||||||
@@ -1782,25 +2082,25 @@ namespace ecs_hpp
|
|||||||
|
|
||||||
template < typename... Ts >
|
template < typename... Ts >
|
||||||
std::tuple<Ts&...> registry::get_components(const uentity& ent) {
|
std::tuple<Ts&...> registry::get_components(const uentity& ent) {
|
||||||
assert(ent.check_owner(this));
|
assert(valid_entity(ent));
|
||||||
return std::make_tuple(std::ref(get_component<Ts>(ent))...);
|
return std::make_tuple(std::ref(get_component<Ts>(ent))...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template < typename... Ts >
|
template < typename... Ts >
|
||||||
std::tuple<const Ts&...> registry::get_components(const const_uentity& ent) const {
|
std::tuple<const Ts&...> registry::get_components(const const_uentity& ent) const {
|
||||||
assert(ent.check_owner(this));
|
assert(valid_entity(ent));
|
||||||
return std::make_tuple(std::cref(get_component<Ts>(ent))...);
|
return std::make_tuple(std::cref(get_component<Ts>(ent))...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template < typename... Ts >
|
template < typename... Ts >
|
||||||
std::tuple<Ts*...> registry::find_components(const uentity& ent) noexcept {
|
std::tuple<Ts*...> registry::find_components(const uentity& ent) noexcept {
|
||||||
assert(ent.check_owner(this));
|
assert(valid_entity(ent));
|
||||||
return std::make_tuple(find_component<Ts>(ent)...);
|
return std::make_tuple(find_component<Ts>(ent)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template < typename... Ts >
|
template < typename... Ts >
|
||||||
std::tuple<const Ts*...> registry::find_components(const const_uentity& ent) const noexcept {
|
std::tuple<const Ts*...> registry::find_components(const const_uentity& ent) const noexcept {
|
||||||
assert(ent.check_owner(this));
|
assert(valid_entity(ent));
|
||||||
return std::make_tuple(find_component<Ts>(ent)...);
|
return std::make_tuple(find_component<Ts>(ent)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1812,12 +2112,12 @@ namespace ecs_hpp
|
|||||||
: 0u;
|
: 0u;
|
||||||
}
|
}
|
||||||
|
|
||||||
template < typename T >
|
inline std::size_t registry::entity_count() const noexcept {
|
||||||
std::size_t registry::entity_component_count(const const_uentity& ent) const noexcept {
|
return entity_ids_.size();
|
||||||
assert(ent.check_owner(this));
|
|
||||||
if ( !alive_entity(ent) ) {
|
|
||||||
return 0u;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline std::size_t registry::entity_component_count(const const_uentity& ent) const noexcept {
|
||||||
|
assert(valid_entity(ent));
|
||||||
std::size_t component_count = 0u;
|
std::size_t component_count = 0u;
|
||||||
for ( const auto family_id : storages_ ) {
|
for ( const auto family_id : storages_ ) {
|
||||||
if ( storages_.get(family_id)->has(ent) ) {
|
if ( storages_.get(family_id)->has(ent) ) {
|
||||||
|
|||||||
251
ecs_tests.cpp
251
ecs_tests.cpp
@@ -118,6 +118,7 @@ TEST_CASE("detail") {
|
|||||||
REQUIRE_THROWS(s.get_dense_index(42u));
|
REQUIRE_THROWS(s.get_dense_index(42u));
|
||||||
|
|
||||||
REQUIRE(s.insert(42u));
|
REQUIRE(s.insert(42u));
|
||||||
|
REQUIRE_FALSE(s.insert(42u));
|
||||||
|
|
||||||
REQUIRE_FALSE(s.empty());
|
REQUIRE_FALSE(s.empty());
|
||||||
REQUIRE(s.size() == 1u);
|
REQUIRE(s.size() == 1u);
|
||||||
@@ -280,6 +281,25 @@ TEST_CASE("detail") {
|
|||||||
REQUIRE(s.unordered_erase(position_c(1,2)));
|
REQUIRE(s.unordered_erase(position_c(1,2)));
|
||||||
REQUIRE(s.get(position_c(3,4)).x == 3);
|
REQUIRE(s.get(position_c(3,4)).x == 3);
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
struct obj_t {
|
||||||
|
int x;
|
||||||
|
obj_t(int nx) : x(nx) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
sparse_map<unsigned, obj_t> m;
|
||||||
|
REQUIRE(m.insert_or_assign(42, obj_t(42)));
|
||||||
|
REQUIRE(m.has(42));
|
||||||
|
REQUIRE(m.get(42).x == 42);
|
||||||
|
REQUIRE_FALSE(m.insert_or_assign(42, obj_t(21)));
|
||||||
|
REQUIRE(m.has(42));
|
||||||
|
REQUIRE(m.get(42).x == 21);
|
||||||
|
REQUIRE(m.size() == 1);
|
||||||
|
REQUIRE(m.insert_or_assign(84, obj_t(84)));
|
||||||
|
REQUIRE(m.has(84));
|
||||||
|
REQUIRE(m.get(84).x == 84);
|
||||||
|
REQUIRE(m.size() == 2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -311,12 +331,9 @@ TEST_CASE("registry") {
|
|||||||
REQUIRE_FALSE(e1 != e2);
|
REQUIRE_FALSE(e1 != e2);
|
||||||
REQUIRE_FALSE(e2 != e3);
|
REQUIRE_FALSE(e2 != e3);
|
||||||
|
|
||||||
REQUIRE_FALSE(w.alive_entity(e1));
|
REQUIRE_FALSE(w.valid_entity(e1));
|
||||||
REQUIRE_FALSE(w.alive_entity(e2));
|
REQUIRE_FALSE(w.valid_entity(e2));
|
||||||
REQUIRE_FALSE(w.alive_entity(e3));
|
REQUIRE_FALSE(w.valid_entity(e3));
|
||||||
|
|
||||||
REQUIRE_FALSE(w.destroy_entity(e1));
|
|
||||||
REQUIRE_FALSE(w.destroy_entity(e2));
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
ecs::registry w;
|
ecs::registry w;
|
||||||
@@ -334,26 +351,22 @@ TEST_CASE("registry") {
|
|||||||
REQUIRE_FALSE(e2 == e3);
|
REQUIRE_FALSE(e2 == e3);
|
||||||
REQUIRE(e3 == ee3);
|
REQUIRE(e3 == ee3);
|
||||||
|
|
||||||
REQUIRE(w.alive_entity(e1));
|
REQUIRE(w.valid_entity(e1));
|
||||||
REQUIRE(w.alive_entity(e2));
|
REQUIRE(w.valid_entity(e2));
|
||||||
REQUIRE(w.alive_entity(e3));
|
REQUIRE(w.valid_entity(e3));
|
||||||
REQUIRE(w.alive_entity(ee3));
|
REQUIRE(w.valid_entity(ee3));
|
||||||
|
|
||||||
REQUIRE(w.destroy_entity(e1));
|
w.destroy_entity(e1);
|
||||||
REQUIRE_FALSE(w.alive_entity(e1));
|
REQUIRE_FALSE(w.valid_entity(e1));
|
||||||
REQUIRE(w.alive_entity(e2));
|
REQUIRE(w.valid_entity(e2));
|
||||||
|
|
||||||
REQUIRE(w.destroy_entity(e2));
|
w.destroy_entity(e2);
|
||||||
REQUIRE_FALSE(w.alive_entity(e1));
|
REQUIRE_FALSE(w.valid_entity(e1));
|
||||||
REQUIRE_FALSE(w.alive_entity(e2));
|
REQUIRE_FALSE(w.valid_entity(e2));
|
||||||
|
|
||||||
REQUIRE(w.destroy_entity(ee3));
|
w.destroy_entity(ee3);
|
||||||
REQUIRE_FALSE(w.alive_entity(e3));
|
REQUIRE_FALSE(w.valid_entity(e3));
|
||||||
REQUIRE_FALSE(w.alive_entity(ee3));
|
REQUIRE_FALSE(w.valid_entity(ee3));
|
||||||
|
|
||||||
REQUIRE_FALSE(w.destroy_entity(e1));
|
|
||||||
REQUIRE_FALSE(w.destroy_entity(e2));
|
|
||||||
REQUIRE_FALSE(w.destroy_entity(ee3));
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
ecs::registry w;
|
ecs::registry w;
|
||||||
@@ -393,11 +406,27 @@ TEST_CASE("registry") {
|
|||||||
ecs::registry w;
|
ecs::registry w;
|
||||||
using namespace ecs::detail;
|
using namespace ecs::detail;
|
||||||
|
|
||||||
|
std::vector<ecs::entity> entities;
|
||||||
for ( std::size_t i = 0; i < entity_id_index_mask; ++i ) {
|
for ( std::size_t i = 0; i < entity_id_index_mask; ++i ) {
|
||||||
w.create_entity();
|
entities.push_back(w.create_entity());
|
||||||
}
|
}
|
||||||
// entity index overflow
|
// entity index overflow
|
||||||
REQUIRE_THROWS_AS(w.create_entity(), std::logic_error);
|
REQUIRE_THROWS_AS(w.create_entity(), std::logic_error);
|
||||||
|
for ( auto& ent : entities ) {
|
||||||
|
ent.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ecs::registry w;
|
||||||
|
REQUIRE_FALSE(w.entity_count());
|
||||||
|
auto e1 = w.create_entity();
|
||||||
|
REQUIRE(w.entity_count() == 1u);
|
||||||
|
auto e2 = w.create_entity();
|
||||||
|
REQUIRE(w.entity_count() == 2u);
|
||||||
|
e1.destroy();
|
||||||
|
REQUIRE(w.entity_count() == 1u);
|
||||||
|
e2.destroy();
|
||||||
|
REQUIRE_FALSE(w.entity_count());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SECTION("components") {
|
SECTION("components") {
|
||||||
@@ -450,7 +479,7 @@ TEST_CASE("registry") {
|
|||||||
REQUIRE_THROWS_AS(c1.get(), std::logic_error);
|
REQUIRE_THROWS_AS(c1.get(), std::logic_error);
|
||||||
REQUIRE_THROWS_AS(c2.get(), std::logic_error);
|
REQUIRE_THROWS_AS(c2.get(), std::logic_error);
|
||||||
|
|
||||||
REQUIRE(c1.assign(4,2));
|
REQUIRE(c1.assign(4,2) == position_c(4,2));
|
||||||
|
|
||||||
REQUIRE(c1.exists());
|
REQUIRE(c1.exists());
|
||||||
REQUIRE(c2.exists());
|
REQUIRE(c2.exists());
|
||||||
@@ -463,7 +492,7 @@ TEST_CASE("registry") {
|
|||||||
REQUIRE(c2.get().x == 4);
|
REQUIRE(c2.get().x == 4);
|
||||||
REQUIRE(c2.get().y == 2);
|
REQUIRE(c2.get().y == 2);
|
||||||
|
|
||||||
REQUIRE(c1.assign(2,4));
|
REQUIRE(&c1.assign(2,4) == &c1.get());
|
||||||
|
|
||||||
REQUIRE(c1.find()->x == 2);
|
REQUIRE(c1.find()->x == 2);
|
||||||
REQUIRE(c1.find()->y == 4);
|
REQUIRE(c1.find()->y == 4);
|
||||||
@@ -486,6 +515,92 @@ TEST_CASE("registry") {
|
|||||||
REQUIRE_FALSE(c1.remove());
|
REQUIRE_FALSE(c1.remove());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
SECTION("prototypes") {
|
||||||
|
{
|
||||||
|
ecs::prototype p;
|
||||||
|
p.assign_component<position_c>(1, 2);
|
||||||
|
|
||||||
|
ecs::registry w;
|
||||||
|
const auto e1 = w.create_entity(p);
|
||||||
|
const auto e2 = w.create_entity(p);
|
||||||
|
|
||||||
|
REQUIRE(w.entity_count() == 2u);
|
||||||
|
REQUIRE(w.component_count<position_c>() == 2u);
|
||||||
|
REQUIRE(w.component_count<velocity_c>() == 0u);
|
||||||
|
|
||||||
|
REQUIRE(e1.component_count() == 1u);
|
||||||
|
REQUIRE(e1.get_component<position_c>() == position_c(1,2));
|
||||||
|
|
||||||
|
REQUIRE(e2.component_count() == 1u);
|
||||||
|
REQUIRE(e2.get_component<position_c>() == position_c(1,2));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const auto p = ecs::prototype()
|
||||||
|
.assign_component<position_c>(1,2)
|
||||||
|
.assign_component<velocity_c>(3,4);
|
||||||
|
|
||||||
|
ecs::registry w;
|
||||||
|
const auto e1 = w.create_entity(p);
|
||||||
|
const auto e2 = w.create_entity(p);
|
||||||
|
|
||||||
|
REQUIRE(w.entity_count() == 2u);
|
||||||
|
REQUIRE(w.component_count<position_c>() == 2u);
|
||||||
|
REQUIRE(w.component_count<velocity_c>() == 2u);
|
||||||
|
|
||||||
|
REQUIRE(e1.component_count() == 2u);
|
||||||
|
REQUIRE(e1.get_component<position_c>() == position_c(1,2));
|
||||||
|
REQUIRE(e1.get_component<velocity_c>() == velocity_c(3,4));
|
||||||
|
|
||||||
|
REQUIRE(e2.component_count() == 2u);
|
||||||
|
REQUIRE(e2.get_component<position_c>() == position_c(1,2));
|
||||||
|
REQUIRE(e2.get_component<velocity_c>() == velocity_c(3,4));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const auto p1 = ecs::prototype()
|
||||||
|
.assign_component<position_c>(1,2)
|
||||||
|
.assign_component<velocity_c>(3,4);
|
||||||
|
|
||||||
|
ecs::prototype p2 = p1;
|
||||||
|
ecs::prototype p3;
|
||||||
|
p3 = p2;
|
||||||
|
|
||||||
|
ecs::registry w;
|
||||||
|
const auto e3 = w.create_entity(p3);
|
||||||
|
REQUIRE(e3.get_component<position_c>() == position_c(1,2));
|
||||||
|
REQUIRE(e3.get_component<velocity_c>() == velocity_c(3,4));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const auto p1 = ecs::prototype()
|
||||||
|
.assign_component<position_c>(1,2)
|
||||||
|
.merge(ecs::prototype().assign_component<position_c>(3,4), false);
|
||||||
|
|
||||||
|
const auto p2 = ecs::prototype()
|
||||||
|
.assign_component<position_c>(1,2)
|
||||||
|
.merge(ecs::prototype().assign_component<position_c>(3,4), true);
|
||||||
|
|
||||||
|
const auto p3 = ecs::prototype()
|
||||||
|
.assign_component<position_c>(1,2)
|
||||||
|
.merge(ecs::prototype().assign_component<velocity_c>(3,4), false);
|
||||||
|
|
||||||
|
const auto p4 = ecs::prototype()
|
||||||
|
.assign_component<position_c>(1,2)
|
||||||
|
.merge(ecs::prototype().assign_component<velocity_c>(3,4), true);
|
||||||
|
|
||||||
|
ecs::registry w;
|
||||||
|
|
||||||
|
const auto e1 = w.create_entity(p1);
|
||||||
|
REQUIRE(e1.get_component<position_c>() == position_c(1,2));
|
||||||
|
const auto e2 = w.create_entity(p2);
|
||||||
|
REQUIRE(e2.get_component<position_c>() == position_c(3,4));
|
||||||
|
|
||||||
|
const auto e3 = w.create_entity(p3);
|
||||||
|
REQUIRE(e3.get_component<position_c>() == position_c(1,2));
|
||||||
|
REQUIRE(e3.get_component<velocity_c>() == velocity_c(3,4));
|
||||||
|
const auto e4 = w.create_entity(p4);
|
||||||
|
REQUIRE(e4.get_component<position_c>() == position_c(1,2));
|
||||||
|
REQUIRE(e4.get_component<velocity_c>() == velocity_c(3,4));
|
||||||
|
}
|
||||||
|
}
|
||||||
SECTION("component_assigning") {
|
SECTION("component_assigning") {
|
||||||
{
|
{
|
||||||
ecs::registry w;
|
ecs::registry w;
|
||||||
@@ -495,20 +610,23 @@ TEST_CASE("registry") {
|
|||||||
REQUIRE_FALSE(w.exists_component<position_c>(e1));
|
REQUIRE_FALSE(w.exists_component<position_c>(e1));
|
||||||
REQUIRE_FALSE(w.exists_component<velocity_c>(e1));
|
REQUIRE_FALSE(w.exists_component<velocity_c>(e1));
|
||||||
REQUIRE_FALSE(w.component_count<position_c>());
|
REQUIRE_FALSE(w.component_count<position_c>());
|
||||||
REQUIRE_FALSE(w.entity_component_count<position_c>(e1));
|
REQUIRE_FALSE(w.entity_component_count(e1));
|
||||||
|
REQUIRE_FALSE(e1.component_count());
|
||||||
|
|
||||||
REQUIRE(w.assign_component<position_c>(e1));
|
REQUIRE(w.assign_component<position_c>(e1) == position_c());
|
||||||
|
|
||||||
REQUIRE(w.exists_component<position_c>(e1));
|
REQUIRE(w.exists_component<position_c>(e1));
|
||||||
REQUIRE_FALSE(w.exists_component<velocity_c>(e1));
|
REQUIRE_FALSE(w.exists_component<velocity_c>(e1));
|
||||||
REQUIRE(w.component_count<position_c>() == 1u);
|
REQUIRE(w.component_count<position_c>() == 1u);
|
||||||
REQUIRE(w.component_count<velocity_c>() == 0u);
|
REQUIRE(w.component_count<velocity_c>() == 0u);
|
||||||
REQUIRE(w.entity_component_count<position_c>(e1) == 1u);
|
REQUIRE(w.entity_component_count(e1) == 1u);
|
||||||
|
REQUIRE(e1.component_count() == 1u);
|
||||||
|
|
||||||
REQUIRE(w.assign_component<velocity_c>(e1));
|
REQUIRE(w.assign_component<velocity_c>(e1) == velocity_c());
|
||||||
REQUIRE(w.component_count<position_c>() == 1u);
|
REQUIRE(w.component_count<position_c>() == 1u);
|
||||||
REQUIRE(w.component_count<velocity_c>() == 1u);
|
REQUIRE(w.component_count<velocity_c>() == 1u);
|
||||||
REQUIRE(w.entity_component_count<position_c>(e1) == 2u);
|
REQUIRE(w.entity_component_count(e1) == 2u);
|
||||||
|
REQUIRE(e1.component_count() == 2u);
|
||||||
|
|
||||||
REQUIRE(w.exists_component<position_c>(e1));
|
REQUIRE(w.exists_component<position_c>(e1));
|
||||||
REQUIRE(w.exists_component<velocity_c>(e1));
|
REQUIRE(w.exists_component<velocity_c>(e1));
|
||||||
@@ -519,30 +637,28 @@ TEST_CASE("registry") {
|
|||||||
REQUIRE_FALSE(w.exists_component<velocity_c>(e1));
|
REQUIRE_FALSE(w.exists_component<velocity_c>(e1));
|
||||||
REQUIRE_FALSE(w.component_count<position_c>());
|
REQUIRE_FALSE(w.component_count<position_c>());
|
||||||
REQUIRE_FALSE(w.component_count<velocity_c>());
|
REQUIRE_FALSE(w.component_count<velocity_c>());
|
||||||
REQUIRE_FALSE(w.entity_component_count<position_c>(e1));
|
REQUIRE_FALSE(w.entity_component_count(e1));
|
||||||
|
REQUIRE_FALSE(e1.component_count());
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
REQUIRE_FALSE(e1.exists_component<position_c>());
|
REQUIRE_FALSE(e1.exists_component<position_c>());
|
||||||
REQUIRE_FALSE(e1.exists_component<velocity_c>());
|
REQUIRE_FALSE(e1.exists_component<velocity_c>());
|
||||||
|
|
||||||
REQUIRE(e1.assign_component<position_c>());
|
REQUIRE(e1.assign_component<position_c>() == position_c());
|
||||||
|
|
||||||
REQUIRE(e1.exists_component<position_c>());
|
REQUIRE(e1.exists_component<position_c>());
|
||||||
REQUIRE_FALSE(e1.exists_component<velocity_c>());
|
REQUIRE_FALSE(e1.exists_component<velocity_c>());
|
||||||
|
|
||||||
REQUIRE(e1.assign_component<velocity_c>());
|
REQUIRE(e1.assign_component<velocity_c>() == velocity_c());
|
||||||
|
|
||||||
REQUIRE(e1.exists_component<position_c>());
|
REQUIRE(e1.exists_component<position_c>());
|
||||||
REQUIRE(e1.exists_component<velocity_c>());
|
REQUIRE(e1.exists_component<velocity_c>());
|
||||||
|
|
||||||
REQUIRE(e1.destroy());
|
e1.destroy();
|
||||||
|
|
||||||
REQUIRE_FALSE(e1.exists_component<position_c>());
|
|
||||||
REQUIRE_FALSE(e1.exists_component<velocity_c>());
|
|
||||||
REQUIRE_FALSE(w.component_count<position_c>());
|
REQUIRE_FALSE(w.component_count<position_c>());
|
||||||
REQUIRE_FALSE(w.component_count<velocity_c>());
|
REQUIRE_FALSE(w.component_count<velocity_c>());
|
||||||
REQUIRE_FALSE(w.entity_component_count<position_c>(e1));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
@@ -551,39 +667,32 @@ TEST_CASE("registry") {
|
|||||||
auto e1 = w.create_entity();
|
auto e1 = w.create_entity();
|
||||||
auto e2 = w.create_entity();
|
auto e2 = w.create_entity();
|
||||||
|
|
||||||
REQUIRE(w.assign_component<position_c>(e1));
|
w.assign_component<position_c>(e1);
|
||||||
REQUIRE(w.assign_component<velocity_c>(e1));
|
w.assign_component<velocity_c>(e1);
|
||||||
|
|
||||||
REQUIRE(w.assign_component<position_c>(e2));
|
w.assign_component<position_c>(e2);
|
||||||
REQUIRE(w.assign_component<velocity_c>(e2));
|
w.assign_component<velocity_c>(e2);
|
||||||
|
|
||||||
REQUIRE(w.destroy_entity(e1));
|
w.destroy_entity(e1);
|
||||||
|
|
||||||
REQUIRE_FALSE(w.exists_component<position_c>(e1));
|
|
||||||
REQUIRE_FALSE(w.exists_component<velocity_c>(e1));
|
|
||||||
|
|
||||||
REQUIRE(w.exists_component<position_c>(e2));
|
REQUIRE(w.exists_component<position_c>(e2));
|
||||||
REQUIRE(w.exists_component<velocity_c>(e2));
|
REQUIRE(w.exists_component<velocity_c>(e2));
|
||||||
}
|
}
|
||||||
{
|
|
||||||
ecs::registry w;
|
|
||||||
auto e1 = w.create_entity();
|
|
||||||
REQUIRE(e1.destroy());
|
|
||||||
REQUIRE_FALSE(e1.assign_component<position_c>());
|
|
||||||
REQUIRE_FALSE(w.exists_component<position_c>(e1));
|
|
||||||
}
|
|
||||||
{
|
{
|
||||||
ecs::registry w;
|
ecs::registry w;
|
||||||
|
|
||||||
auto e1 = w.create_entity();
|
auto e1 = w.create_entity();
|
||||||
auto e2 = w.create_entity();
|
auto e2 = w.create_entity();
|
||||||
|
|
||||||
REQUIRE(w.assign_component<position_c>(e1));
|
const position_c& e1_pos = w.assign_component<position_c>(e1);
|
||||||
|
REQUIRE(&e1_pos == &w.get_component<position_c>(e1));
|
||||||
|
|
||||||
REQUIRE(w.assign_component<position_c>(e2));
|
const position_c& e2_pos = w.assign_component<position_c>(e2);
|
||||||
REQUIRE(w.assign_component<velocity_c>(e2));
|
const velocity_c& e2_vel = w.assign_component<velocity_c>(e2);
|
||||||
|
REQUIRE(&e2_pos == &w.get_component<position_c>(e2));
|
||||||
|
REQUIRE(&e2_vel == &w.get_component<velocity_c>(e2));
|
||||||
|
|
||||||
REQUIRE(e1.destroy());
|
e1.destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SECTION("component_accessing") {
|
SECTION("component_accessing") {
|
||||||
@@ -713,6 +822,34 @@ TEST_CASE("registry") {
|
|||||||
REQUIRE(e1.get_component<velocity_c>().y == 40);
|
REQUIRE(e1.get_component<velocity_c>().y == 40);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
SECTION("cloning") {
|
||||||
|
{
|
||||||
|
ecs::registry w;
|
||||||
|
|
||||||
|
auto e1 = w.create_entity();
|
||||||
|
ecs::entity_filler(e1)
|
||||||
|
.component<position_c>(1, 2)
|
||||||
|
.component<velocity_c>(3, 4);
|
||||||
|
|
||||||
|
auto e2 = w.create_entity(e1);
|
||||||
|
REQUIRE(w.component_count<position_c>() == 2);
|
||||||
|
REQUIRE(w.component_count<velocity_c>() == 2);
|
||||||
|
REQUIRE(e2.exists_component<position_c>());
|
||||||
|
REQUIRE(e2.exists_component<velocity_c>());
|
||||||
|
REQUIRE(e2.get_component<position_c>() == position_c(1, 2));
|
||||||
|
REQUIRE(e2.get_component<velocity_c>() == velocity_c(3, 4));
|
||||||
|
|
||||||
|
e2.remove_component<velocity_c>();
|
||||||
|
auto e3 = e2.clone();
|
||||||
|
|
||||||
|
REQUIRE(w.component_count<position_c>() == 3);
|
||||||
|
REQUIRE(w.component_count<velocity_c>() == 1);
|
||||||
|
|
||||||
|
REQUIRE(e3.exists_component<position_c>());
|
||||||
|
REQUIRE_FALSE(e3.exists_component<velocity_c>());
|
||||||
|
REQUIRE(e3.get_component<position_c>() == position_c(1, 2));
|
||||||
|
}
|
||||||
|
}
|
||||||
SECTION("for_each_entity") {
|
SECTION("for_each_entity") {
|
||||||
{
|
{
|
||||||
ecs::registry w;
|
ecs::registry w;
|
||||||
|
|||||||
Reference in New Issue
Block a user