mirror of
https://github.com/BlackMATov/ecs.hpp.git
synced 2026-01-04 15:16:50 +07:00
Merge pull request #4 from BlackMATov/feature/sparse_set
Feature/sparse set
This commit is contained in:
607
ecs.hpp
607
ecs.hpp
@@ -10,16 +10,18 @@
|
|||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include <tuple>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#include <iterator>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <algorithm>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <unordered_set>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
//
|
//
|
||||||
@@ -29,8 +31,9 @@
|
|||||||
|
|
||||||
namespace ecs_hpp
|
namespace ecs_hpp
|
||||||
{
|
{
|
||||||
class world;
|
|
||||||
class entity;
|
class entity;
|
||||||
|
class system;
|
||||||
|
class registry;
|
||||||
|
|
||||||
using family_id = std::uint16_t;
|
using family_id = std::uint16_t;
|
||||||
using entity_id = std::uint32_t;
|
using entity_id = std::uint32_t;
|
||||||
@@ -44,6 +47,23 @@ namespace ecs_hpp
|
|||||||
"ecs_hpp::entity_id must be an unsigned integer");
|
"ecs_hpp::entity_id must be an unsigned integer");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// utilities
|
||||||
|
//
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace ecs_hpp
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
template < typename T >
|
||||||
|
constexpr std::add_const_t<T>& as_const(T& t) noexcept {
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
//
|
//
|
||||||
// detail::type_family
|
// detail::type_family
|
||||||
@@ -78,6 +98,306 @@ namespace ecs_hpp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// detail::sparse_set
|
||||||
|
//
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace ecs_hpp
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
template < typename 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:
|
||||||
|
iterator begin() noexcept {
|
||||||
|
return dense_.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator end() noexcept {
|
||||||
|
using dt = typename std::iterator_traits<iterator>::difference_type;
|
||||||
|
return begin() + static_cast<dt>(size_);
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator begin() const noexcept {
|
||||||
|
return dense_.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator end() const noexcept {
|
||||||
|
using dt = typename std::iterator_traits<const_iterator>::difference_type;
|
||||||
|
return begin() + static_cast<dt>(size_);
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator cbegin() const noexcept {
|
||||||
|
return dense_.cbegin();
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator cend() const noexcept {
|
||||||
|
using dt = typename std::iterator_traits<const_iterator>::difference_type;
|
||||||
|
return cbegin() + static_cast<dt>(size_);
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
bool insert(const T v) {
|
||||||
|
if ( has(v) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ( v >= capacity_ ) {
|
||||||
|
reserve(new_capacity_for_(v + 1u));
|
||||||
|
}
|
||||||
|
dense_[size_] = v;
|
||||||
|
sparse_[v] = size_;
|
||||||
|
++size_;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool unordered_erase(const T v) noexcept {
|
||||||
|
if ( !has(v) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const std::size_t index = sparse_[v];
|
||||||
|
const T last = dense_[size_ - 1u];
|
||||||
|
dense_[index] = last;
|
||||||
|
sparse_[last] = index;
|
||||||
|
--size_;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() noexcept {
|
||||||
|
size_ = 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has(const T v) const noexcept {
|
||||||
|
return v < capacity_
|
||||||
|
&& sparse_[v] < size_
|
||||||
|
&& dense_[sparse_[v]] == v;
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator find(const T v) const noexcept {
|
||||||
|
return has(v)
|
||||||
|
? begin() + sparse_[v]
|
||||||
|
: end();
|
||||||
|
}
|
||||||
|
|
||||||
|
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>");
|
||||||
|
}
|
||||||
|
|
||||||
|
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(std::size_t(-1), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() const noexcept {
|
||||||
|
return size_ == 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reserve(std::size_t ncapacity) {
|
||||||
|
if ( ncapacity > capacity_ ) {
|
||||||
|
std::vector<T> ndense(ncapacity);
|
||||||
|
std::vector<std::size_t> nsparse(ncapacity);
|
||||||
|
std::copy(dense_.begin(), dense_.end(), ndense.begin());
|
||||||
|
std::copy(sparse_.begin(), sparse_.end(), nsparse.begin());
|
||||||
|
ndense.swap(dense_);
|
||||||
|
nsparse.swap(sparse_);
|
||||||
|
capacity_ = ncapacity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t size() const noexcept {
|
||||||
|
return size_;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t max_size() const noexcept {
|
||||||
|
return std::min(dense_.max_size(), sparse_.max_size());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t capacity() const noexcept {
|
||||||
|
return capacity_;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
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>");
|
||||||
|
}
|
||||||
|
if ( capacity_ >= ms / 2u ) {
|
||||||
|
return ms;
|
||||||
|
}
|
||||||
|
return std::max(capacity_ * 2u, nsize);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::vector<T> dense_;
|
||||||
|
std::vector<std::size_t> sparse_;
|
||||||
|
std::size_t size_{0u};
|
||||||
|
std::size_t capacity_{0u};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// detail::sparse_map
|
||||||
|
//
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace ecs_hpp
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
template < typename K, typename T >
|
||||||
|
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:
|
||||||
|
iterator begin() noexcept {
|
||||||
|
return keys_.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator end() noexcept {
|
||||||
|
return keys_.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator begin() const noexcept {
|
||||||
|
return keys_.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator end() const noexcept {
|
||||||
|
return keys_.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator cbegin() const noexcept {
|
||||||
|
return keys_.cbegin();
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator cend() const noexcept {
|
||||||
|
return keys_.cend();
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
bool insert(const K k, const T& v) {
|
||||||
|
if ( keys_.has(k) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
values_.push_back(v);
|
||||||
|
try {
|
||||||
|
return keys_.insert(k);
|
||||||
|
} catch (...) {
|
||||||
|
values_.pop_back();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool insert(const K k, T&& v) {
|
||||||
|
if ( keys_.has(k) ) {
|
||||||
|
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) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
values_.emplace_back(std::forward<Args>(args)...);
|
||||||
|
try {
|
||||||
|
return keys_.insert(k);
|
||||||
|
} catch (...) {
|
||||||
|
values_.pop_back();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool unordered_erase(const K k) {
|
||||||
|
if ( !keys_.has(k) ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const std::size_t index = keys_.get_index(k);
|
||||||
|
values_[index] = std::move(values_.back());
|
||||||
|
values_.pop_back();
|
||||||
|
keys_.unordered_erase(k);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() noexcept {
|
||||||
|
keys_.clear();
|
||||||
|
values_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has(const K k) const noexcept {
|
||||||
|
return keys_.has(k);
|
||||||
|
}
|
||||||
|
|
||||||
|
T& get_value(const K k) {
|
||||||
|
return values_[keys_.get_index(k)];
|
||||||
|
}
|
||||||
|
|
||||||
|
const T& get_value(const K k) const {
|
||||||
|
return values_[keys_.get_index(k)];
|
||||||
|
}
|
||||||
|
|
||||||
|
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 auto ip = keys_.find_index(k);
|
||||||
|
return ip.second
|
||||||
|
? &values_[ip.first]
|
||||||
|
: nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() const noexcept {
|
||||||
|
return values_.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void reserve(std::size_t ncapacity) {
|
||||||
|
keys_.reserve(ncapacity);
|
||||||
|
values_.reserve(ncapacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t size() const noexcept {
|
||||||
|
return values_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t max_size() const noexcept {
|
||||||
|
return std::min(keys_.max_size(), values_.max_size());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t capacity() const noexcept {
|
||||||
|
return values_.capacity();
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
sparse_set<K> keys_;
|
||||||
|
std::vector<T> values_;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
//
|
//
|
||||||
// detail::component_storage
|
// detail::component_storage
|
||||||
@@ -98,7 +418,7 @@ namespace ecs_hpp
|
|||||||
template < typename T >
|
template < typename T >
|
||||||
class component_storage : public component_storage_base {
|
class component_storage : public component_storage_base {
|
||||||
public:
|
public:
|
||||||
component_storage(world& owner);
|
component_storage(registry& owner);
|
||||||
|
|
||||||
template < typename... Args >
|
template < typename... Args >
|
||||||
void assign(entity_id id, Args&&... args);
|
void assign(entity_id id, Args&&... args);
|
||||||
@@ -112,59 +432,55 @@ namespace ecs_hpp
|
|||||||
template < typename F >
|
template < typename F >
|
||||||
void for_each_component(F&& f) const noexcept;
|
void for_each_component(F&& f) const noexcept;
|
||||||
private:
|
private:
|
||||||
world& owner_;
|
registry& owner_;
|
||||||
std::unordered_map<entity_id, T> components_;
|
detail::sparse_map<entity_id, T> components_;
|
||||||
};
|
};
|
||||||
|
|
||||||
template < typename T >
|
template < typename T >
|
||||||
component_storage<T>::component_storage(world& owner)
|
component_storage<T>::component_storage(registry& owner)
|
||||||
: owner_(owner) {}
|
: owner_(owner) {}
|
||||||
|
|
||||||
template < typename T >
|
template < typename T >
|
||||||
template < typename... Args >
|
template < typename... Args >
|
||||||
void component_storage<T>::assign(entity_id id, Args&&... args) {
|
void component_storage<T>::assign(entity_id id, Args&&... args) {
|
||||||
components_[id] = T(std::forward<Args>(args)...);
|
if ( !components_.emplace(id, std::forward<Args>(args)...) ) {
|
||||||
|
components_.get_value(id) = T(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template < typename T >
|
template < typename T >
|
||||||
bool component_storage<T>::remove(entity_id id) noexcept {
|
bool component_storage<T>::remove(entity_id id) noexcept {
|
||||||
return components_.erase(id) > 0u;
|
return components_.unordered_erase(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
template < typename T >
|
template < typename T >
|
||||||
bool component_storage<T>::exists(entity_id id) const noexcept {
|
bool component_storage<T>::exists(entity_id id) const noexcept {
|
||||||
return components_.find(id) != components_.end();
|
return components_.has(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
template < typename T >
|
template < typename T >
|
||||||
T* component_storage<T>::find(entity_id id) noexcept {
|
T* component_storage<T>::find(entity_id id) noexcept {
|
||||||
const auto iter = components_.find(id);
|
return components_.find_value(id);
|
||||||
return iter != components_.end()
|
|
||||||
? &iter->second
|
|
||||||
: nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template < typename T >
|
template < typename T >
|
||||||
const T* component_storage<T>::find(entity_id id) const noexcept {
|
const T* component_storage<T>::find(entity_id id) const noexcept {
|
||||||
const auto iter = components_.find(id);
|
return components_.find_value(id);
|
||||||
return iter != components_.end()
|
|
||||||
? &iter->second
|
|
||||||
: nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template < typename T >
|
template < typename T >
|
||||||
template < typename F >
|
template < typename F >
|
||||||
void component_storage<T>::for_each_component(F&& f) noexcept {
|
void component_storage<T>::for_each_component(F&& f) noexcept {
|
||||||
for ( auto& component_pair : components_ ) {
|
for ( const auto id : components_ ) {
|
||||||
f(entity(owner_, component_pair.first), component_pair.second);
|
f(entity(owner_, id), components_.get_value(id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template < typename T >
|
template < typename T >
|
||||||
template < typename F >
|
template < typename F >
|
||||||
void component_storage<T>::for_each_component(F&& f) const noexcept {
|
void component_storage<T>::for_each_component(F&& f) const noexcept {
|
||||||
for ( auto& component_pair : components_ ) {
|
for ( const auto id : components_ ) {
|
||||||
f(entity(owner_, component_pair.first), component_pair.second);
|
f(entity(owner_, id), components_.get_value(id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -195,10 +511,10 @@ namespace ecs_hpp
|
|||||||
{
|
{
|
||||||
class entity final {
|
class entity final {
|
||||||
public:
|
public:
|
||||||
entity(world& owner);
|
entity(registry& owner);
|
||||||
entity(world& owner, entity_id id);
|
entity(registry& owner, entity_id id);
|
||||||
|
|
||||||
const world& owner() const noexcept;
|
const registry& owner() const noexcept;
|
||||||
entity_id id() const noexcept;
|
entity_id id() const noexcept;
|
||||||
|
|
||||||
bool destroy();
|
bool destroy();
|
||||||
@@ -226,8 +542,18 @@ namespace ecs_hpp
|
|||||||
|
|
||||||
template < typename T >
|
template < typename T >
|
||||||
const T* find_component() const noexcept;
|
const T* find_component() const noexcept;
|
||||||
|
|
||||||
|
template < typename... Ts >
|
||||||
|
std::tuple<Ts&...> get_components();
|
||||||
|
template < typename... Ts >
|
||||||
|
std::tuple<const Ts&...> get_components() const;
|
||||||
|
|
||||||
|
template < typename... Ts >
|
||||||
|
std::tuple<Ts*...> find_components() noexcept;
|
||||||
|
template < typename... Ts >
|
||||||
|
std::tuple<const Ts*...> find_components() const noexcept;
|
||||||
private:
|
private:
|
||||||
world& owner_;
|
registry& owner_;
|
||||||
entity_id id_{0u};
|
entity_id id_{0u};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -249,16 +575,31 @@ namespace std
|
|||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
//
|
//
|
||||||
// world
|
// system
|
||||||
//
|
//
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace ecs_hpp
|
namespace ecs_hpp
|
||||||
{
|
{
|
||||||
class world final {
|
class system {
|
||||||
public:
|
public:
|
||||||
world();
|
virtual ~system() = default;
|
||||||
~world() noexcept;
|
virtual void process(registry& owner) = 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// registry
|
||||||
|
//
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace ecs_hpp
|
||||||
|
{
|
||||||
|
class registry final {
|
||||||
|
public:
|
||||||
|
registry() = default;
|
||||||
|
~registry() noexcept = default;
|
||||||
|
|
||||||
entity create_entity();
|
entity create_entity();
|
||||||
bool destroy_entity(const entity& ent);
|
bool destroy_entity(const entity& ent);
|
||||||
@@ -285,6 +626,16 @@ namespace ecs_hpp
|
|||||||
template < typename T >
|
template < typename T >
|
||||||
const T* find_component(const entity& ent) const noexcept;
|
const T* find_component(const entity& ent) const noexcept;
|
||||||
|
|
||||||
|
template < typename... Ts >
|
||||||
|
std::tuple<Ts&...> get_components(const entity& ent);
|
||||||
|
template < typename... Ts >
|
||||||
|
std::tuple<const Ts&...> get_components(const entity& ent) const;
|
||||||
|
|
||||||
|
template < typename... Ts >
|
||||||
|
std::tuple<Ts*...> find_components(const entity& ent) noexcept;
|
||||||
|
template < typename... Ts >
|
||||||
|
std::tuple<const Ts*...> find_components(const entity& ent) const noexcept;
|
||||||
|
|
||||||
template < typename T, typename F >
|
template < typename T, typename F >
|
||||||
void for_each_component(F&& f);
|
void for_each_component(F&& f);
|
||||||
template < typename T, typename F >
|
template < typename T, typename F >
|
||||||
@@ -294,6 +645,10 @@ namespace ecs_hpp
|
|||||||
void for_joined_components(F&& f);
|
void for_joined_components(F&& f);
|
||||||
template < typename... Ts, typename F >
|
template < typename... Ts, typename F >
|
||||||
void for_joined_components(F&& f) const;
|
void for_joined_components(F&& f) const;
|
||||||
|
|
||||||
|
template < typename T, typename... Args >
|
||||||
|
void add_system(Args&&... args);
|
||||||
|
void process_systems();
|
||||||
private:
|
private:
|
||||||
template < typename T >
|
template < typename T >
|
||||||
detail::component_storage<T>* find_storage_() noexcept;
|
detail::component_storage<T>* find_storage_() noexcept;
|
||||||
@@ -328,10 +683,14 @@ namespace ecs_hpp
|
|||||||
mutable std::mutex mutex_;
|
mutable std::mutex mutex_;
|
||||||
|
|
||||||
entity_id last_entity_id_{0u};
|
entity_id last_entity_id_{0u};
|
||||||
std::unordered_set<entity> entities_;
|
std::vector<entity_id> free_entity_ids_;
|
||||||
|
detail::sparse_set<entity_id> entity_ids_;
|
||||||
|
|
||||||
using storage_uptr = std::unique_ptr<detail::component_storage_base>;
|
using storage_uptr = std::unique_ptr<detail::component_storage_base>;
|
||||||
std::unordered_map<family_id, storage_uptr> storages_;
|
detail::sparse_map<family_id, storage_uptr> storages_;
|
||||||
|
|
||||||
|
using system_uptr = std::unique_ptr<system>;
|
||||||
|
std::vector<system_uptr> systems_;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -343,14 +702,14 @@ namespace ecs_hpp
|
|||||||
|
|
||||||
namespace ecs_hpp
|
namespace ecs_hpp
|
||||||
{
|
{
|
||||||
inline entity::entity(world& owner)
|
inline entity::entity(registry& owner)
|
||||||
: owner_(owner) {}
|
: owner_(owner) {}
|
||||||
|
|
||||||
inline entity::entity(world& owner, entity_id id)
|
inline entity::entity(registry& owner, entity_id id)
|
||||||
: owner_(owner)
|
: owner_(owner)
|
||||||
, id_(id) {}
|
, id_(id) {}
|
||||||
|
|
||||||
inline const world& entity::owner() const noexcept {
|
inline const registry& entity::owner() const noexcept {
|
||||||
return owner_;
|
return owner_;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -363,7 +722,7 @@ namespace ecs_hpp
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline bool entity::is_alive() const noexcept {
|
inline bool entity::is_alive() const noexcept {
|
||||||
return owner_.is_entity_alive(*this);
|
return detail::as_const(owner_).is_entity_alive(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
template < typename T, typename... Args >
|
template < typename T, typename... Args >
|
||||||
@@ -380,7 +739,7 @@ namespace ecs_hpp
|
|||||||
|
|
||||||
template < typename T >
|
template < typename T >
|
||||||
bool entity::exists_component() const noexcept {
|
bool entity::exists_component() const noexcept {
|
||||||
return owner_.exists_component<T>(*this);
|
return detail::as_const(owner_).exists_component<T>(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::size_t entity::remove_all_components() noexcept {
|
inline std::size_t entity::remove_all_components() noexcept {
|
||||||
@@ -394,7 +753,7 @@ namespace ecs_hpp
|
|||||||
|
|
||||||
template < typename T >
|
template < typename T >
|
||||||
const T& entity::get_component() const {
|
const T& entity::get_component() const {
|
||||||
return owner_.get_component<T>(*this);
|
return detail::as_const(owner_).get_component<T>(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
template < typename T >
|
template < typename T >
|
||||||
@@ -404,7 +763,27 @@ namespace ecs_hpp
|
|||||||
|
|
||||||
template < typename T >
|
template < typename T >
|
||||||
const T* entity::find_component() const noexcept {
|
const T* entity::find_component() const noexcept {
|
||||||
return 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
template < typename... Ts >
|
||||||
|
std::tuple<const Ts&...> entity::get_components() const {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
template < typename... Ts >
|
||||||
|
std::tuple<const Ts*...> entity::find_components() const noexcept {
|
||||||
|
return detail::as_const(owner_).find_components<Ts...>(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool operator==(const entity& l, const entity& r) noexcept {
|
inline bool operator==(const entity& l, const entity& r) noexcept {
|
||||||
@@ -419,36 +798,44 @@ namespace ecs_hpp
|
|||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
//
|
//
|
||||||
// world impl
|
// registry impl
|
||||||
//
|
//
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace ecs_hpp
|
namespace ecs_hpp
|
||||||
{
|
{
|
||||||
inline world::world() = default;
|
inline entity registry::create_entity() {
|
||||||
inline world::~world() noexcept = default;
|
|
||||||
|
|
||||||
inline entity world::create_entity() {
|
|
||||||
std::lock_guard<std::mutex> guard(mutex_);
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
|
if ( !free_entity_ids_.empty() ) {
|
||||||
|
auto ent = entity(*this, free_entity_ids_.back());
|
||||||
|
entity_ids_.insert(ent.id());
|
||||||
|
free_entity_ids_.pop_back();
|
||||||
|
return ent;
|
||||||
|
|
||||||
|
}
|
||||||
assert(last_entity_id_ < std::numeric_limits<entity_id>::max());
|
assert(last_entity_id_ < std::numeric_limits<entity_id>::max());
|
||||||
auto ent = entity(*this, ++last_entity_id_);
|
auto ent = entity(*this, ++last_entity_id_);
|
||||||
entities_.insert(ent);
|
entity_ids_.insert(ent.id());
|
||||||
return ent;
|
return ent;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool world::destroy_entity(const entity& ent) {
|
inline bool registry::destroy_entity(const entity& ent) {
|
||||||
std::lock_guard<std::mutex> guard(mutex_);
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
remove_all_components_impl_(ent);
|
remove_all_components_impl_(ent);
|
||||||
return entities_.erase(ent) > 0u;
|
if ( entity_ids_.unordered_erase(ent.id()) ) {
|
||||||
|
free_entity_ids_.push_back(ent.id());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool world::is_entity_alive(const entity& ent) const noexcept {
|
inline bool registry::is_entity_alive(const entity& ent) const noexcept {
|
||||||
std::lock_guard<std::mutex> guard(mutex_);
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
return is_entity_alive_impl_(ent);
|
return is_entity_alive_impl_(ent);
|
||||||
}
|
}
|
||||||
|
|
||||||
template < typename T, typename... Args >
|
template < typename T, typename... Args >
|
||||||
bool world::assign_component(const entity& ent, Args&&... args) {
|
bool registry::assign_component(const entity& ent, Args&&... args) {
|
||||||
std::lock_guard<std::mutex> guard(mutex_);
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
if ( !is_entity_alive_impl_(ent) ) {
|
if ( !is_entity_alive_impl_(ent) ) {
|
||||||
return false;
|
return false;
|
||||||
@@ -460,7 +847,7 @@ namespace ecs_hpp
|
|||||||
}
|
}
|
||||||
|
|
||||||
template < typename T >
|
template < typename T >
|
||||||
bool world::remove_component(const entity& ent) {
|
bool registry::remove_component(const entity& ent) {
|
||||||
std::lock_guard<std::mutex> guard(mutex_);
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
if ( !is_entity_alive_impl_(ent) ) {
|
if ( !is_entity_alive_impl_(ent) ) {
|
||||||
return false;
|
return false;
|
||||||
@@ -472,7 +859,7 @@ namespace ecs_hpp
|
|||||||
}
|
}
|
||||||
|
|
||||||
template < typename T >
|
template < typename T >
|
||||||
bool world::exists_component(const entity& ent) const noexcept {
|
bool registry::exists_component(const entity& ent) const noexcept {
|
||||||
std::lock_guard<std::mutex> guard(mutex_);
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
if ( !is_entity_alive_impl_(ent) ) {
|
if ( !is_entity_alive_impl_(ent) ) {
|
||||||
return false;
|
return false;
|
||||||
@@ -483,13 +870,13 @@ namespace ecs_hpp
|
|||||||
: false;
|
: false;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::size_t world::remove_all_components(const entity& ent) const noexcept {
|
inline std::size_t registry::remove_all_components(const entity& ent) const noexcept {
|
||||||
std::lock_guard<std::mutex> guard(mutex_);
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
return remove_all_components_impl_(ent);
|
return remove_all_components_impl_(ent);
|
||||||
}
|
}
|
||||||
|
|
||||||
template < typename T >
|
template < typename T >
|
||||||
T& world::get_component(const entity& ent) {
|
T& registry::get_component(const entity& ent) {
|
||||||
std::lock_guard<std::mutex> guard(mutex_);
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
T* component = find_component_impl_<T>(ent);
|
T* component = find_component_impl_<T>(ent);
|
||||||
if ( component ) {
|
if ( component ) {
|
||||||
@@ -499,7 +886,7 @@ namespace ecs_hpp
|
|||||||
}
|
}
|
||||||
|
|
||||||
template < typename T >
|
template < typename T >
|
||||||
const T& world::get_component(const entity& ent) const {
|
const T& registry::get_component(const entity& ent) const {
|
||||||
std::lock_guard<std::mutex> guard(mutex_);
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
const T* component = find_component_impl_<T>(ent);
|
const T* component = find_component_impl_<T>(ent);
|
||||||
if ( component ) {
|
if ( component ) {
|
||||||
@@ -509,19 +896,39 @@ namespace ecs_hpp
|
|||||||
}
|
}
|
||||||
|
|
||||||
template < typename T >
|
template < typename T >
|
||||||
T* world::find_component(const entity& ent) noexcept {
|
T* registry::find_component(const entity& ent) noexcept {
|
||||||
std::lock_guard<std::mutex> guard(mutex_);
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
return find_component_impl_<T>(ent);
|
return find_component_impl_<T>(ent);
|
||||||
}
|
}
|
||||||
|
|
||||||
template < typename T >
|
template < typename T >
|
||||||
const T* world::find_component(const entity& ent) const noexcept {
|
const T* registry::find_component(const entity& ent) const noexcept {
|
||||||
std::lock_guard<std::mutex> guard(mutex_);
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
return find_component_impl_<T>(ent);
|
return find_component_impl_<T>(ent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template < typename... Ts >
|
||||||
|
std::tuple<Ts&...> registry::get_components(const entity& ent) {
|
||||||
|
return std::make_tuple(std::ref(get_component<Ts>(ent))...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template < typename... Ts >
|
||||||
|
std::tuple<const Ts&...> registry::get_components(const entity& ent) const {
|
||||||
|
return std::make_tuple(std::cref(get_component<Ts>(ent))...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template < typename... Ts >
|
||||||
|
std::tuple<Ts*...> registry::find_components(const entity& ent) noexcept {
|
||||||
|
return std::make_tuple(find_component<Ts>(ent)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template < typename... Ts >
|
||||||
|
std::tuple<const Ts*...> registry::find_components(const entity& ent) const noexcept {
|
||||||
|
return std::make_tuple(find_component<Ts>(ent)...);
|
||||||
|
}
|
||||||
|
|
||||||
template < typename T, typename F >
|
template < typename T, typename F >
|
||||||
void world::for_each_component(F&& f) {
|
void registry::for_each_component(F&& f) {
|
||||||
std::lock_guard<std::mutex> guard(mutex_);
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
detail::component_storage<T>* storage = find_storage_<T>();
|
detail::component_storage<T>* storage = find_storage_<T>();
|
||||||
if ( storage ) {
|
if ( storage ) {
|
||||||
@@ -530,7 +937,7 @@ namespace ecs_hpp
|
|||||||
}
|
}
|
||||||
|
|
||||||
template < typename T, typename F >
|
template < typename T, typename F >
|
||||||
void world::for_each_component(F&& f) const {
|
void registry::for_each_component(F&& f) const {
|
||||||
std::lock_guard<std::mutex> guard(mutex_);
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
const detail::component_storage<T>* storage = find_storage_<T>();
|
const detail::component_storage<T>* storage = find_storage_<T>();
|
||||||
if ( storage ) {
|
if ( storage ) {
|
||||||
@@ -539,63 +946,73 @@ namespace ecs_hpp
|
|||||||
}
|
}
|
||||||
|
|
||||||
template < typename... Ts, typename F >
|
template < typename... Ts, typename F >
|
||||||
void world::for_joined_components(F&& f) {
|
void registry::for_joined_components(F&& f) {
|
||||||
std::lock_guard<std::mutex> guard(mutex_);
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
for_joined_components_impl_<Ts...>(std::forward<F>(f));
|
for_joined_components_impl_<Ts...>(std::forward<F>(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
template < typename... Ts, typename F >
|
template < typename... Ts, typename F >
|
||||||
void world::for_joined_components(F&& f) const {
|
void registry::for_joined_components(F&& f) const {
|
||||||
std::lock_guard<std::mutex> guard(mutex_);
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
for_joined_components_impl_<Ts...>(std::forward<F>(f));
|
for_joined_components_impl_<Ts...>(std::forward<F>(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
template < typename T >
|
template < typename T, typename... Args >
|
||||||
detail::component_storage<T>* world::find_storage_() noexcept {
|
void registry::add_system(Args&&... args) {
|
||||||
const auto family = detail::type_family<T>::id();
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
const auto iter = storages_.find(family);
|
systems_.emplace_back(
|
||||||
if ( iter != storages_.end() ) {
|
std::make_unique<T>(std::forward<Args>(args)...));
|
||||||
return static_cast<detail::component_storage<T>*>(iter->second.get());
|
}
|
||||||
|
|
||||||
|
inline void registry::process_systems() {
|
||||||
|
for ( auto& s : systems_ ) {
|
||||||
|
s->process(*this);
|
||||||
}
|
}
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template < typename T >
|
template < typename T >
|
||||||
const detail::component_storage<T>* world::find_storage_() const noexcept {
|
detail::component_storage<T>* registry::find_storage_() noexcept {
|
||||||
const auto family = detail::type_family<T>::id();
|
const auto family = detail::type_family<T>::id();
|
||||||
const auto iter = storages_.find(family);
|
using raw_storage_ptr = detail::component_storage<T>*;
|
||||||
if ( iter != storages_.end() ) {
|
return storages_.has(family)
|
||||||
return static_cast<const detail::component_storage<T>*>(iter->second.get());
|
? static_cast<raw_storage_ptr>(storages_.get_value(family).get())
|
||||||
}
|
: nullptr;
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template < typename T >
|
template < typename T >
|
||||||
detail::component_storage<T>& world::get_or_create_storage_() {
|
const detail::component_storage<T>* registry::find_storage_() const noexcept {
|
||||||
|
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())
|
||||||
|
: nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template < typename T >
|
||||||
|
detail::component_storage<T>& registry::get_or_create_storage_() {
|
||||||
detail::component_storage<T>* storage = find_storage_<T>();
|
detail::component_storage<T>* storage = find_storage_<T>();
|
||||||
if ( storage ) {
|
if ( storage ) {
|
||||||
return *storage;
|
return *storage;
|
||||||
}
|
}
|
||||||
const auto family = detail::type_family<T>::id();
|
const auto family = detail::type_family<T>::id();
|
||||||
const auto emplace_r = storages_.emplace(std::make_pair(
|
storages_.emplace(
|
||||||
family,
|
family,
|
||||||
std::make_unique<detail::component_storage<T>>(*this)));
|
std::make_unique<detail::component_storage<T>>(*this));
|
||||||
assert(emplace_r.second && "unexpected internal error");
|
|
||||||
return *static_cast<detail::component_storage<T>*>(
|
return *static_cast<detail::component_storage<T>*>(
|
||||||
emplace_r.first->second.get());
|
storages_.get_value(family).get());
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool world::is_entity_alive_impl_(const entity& ent) const noexcept {
|
inline bool registry::is_entity_alive_impl_(const entity& ent) const noexcept {
|
||||||
return entities_.count(ent) > 0u;
|
return entity_ids_.has(ent.id());
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::size_t world::remove_all_components_impl_(const entity& ent) const noexcept {
|
inline std::size_t registry::remove_all_components_impl_(const entity& ent) const noexcept {
|
||||||
if ( !is_entity_alive_impl_(ent) ) {
|
if ( !is_entity_alive_impl_(ent) ) {
|
||||||
return 0u;
|
return 0u;
|
||||||
}
|
}
|
||||||
std::size_t removed_components = 0u;
|
std::size_t removed_components = 0u;
|
||||||
for ( auto& storage_p : storages_ ) {
|
for ( const auto id : storages_ ) {
|
||||||
if ( storage_p.second->remove(ent.id()) ) {
|
if ( storages_.get_value(id)->remove(ent.id()) ) {
|
||||||
++removed_components;
|
++removed_components;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -603,7 +1020,7 @@ namespace ecs_hpp
|
|||||||
}
|
}
|
||||||
|
|
||||||
template < typename T >
|
template < typename T >
|
||||||
T* world::find_component_impl_(const entity& ent) noexcept {
|
T* registry::find_component_impl_(const entity& ent) noexcept {
|
||||||
detail::component_storage<T>* storage = find_storage_<T>();
|
detail::component_storage<T>* storage = find_storage_<T>();
|
||||||
return storage
|
return storage
|
||||||
? storage->find(ent.id())
|
? storage->find(ent.id())
|
||||||
@@ -611,7 +1028,7 @@ namespace ecs_hpp
|
|||||||
}
|
}
|
||||||
|
|
||||||
template < typename T >
|
template < typename T >
|
||||||
const T* world::find_component_impl_(const entity& ent) const noexcept {
|
const T* registry::find_component_impl_(const entity& ent) const noexcept {
|
||||||
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.id())
|
? storage->find(ent.id())
|
||||||
@@ -619,14 +1036,14 @@ namespace ecs_hpp
|
|||||||
}
|
}
|
||||||
|
|
||||||
template < typename... Ts, typename F >
|
template < typename... Ts, typename F >
|
||||||
void world::for_joined_components_impl_(F&& f) {
|
void registry::for_joined_components_impl_(F&& f) {
|
||||||
for ( const auto& e : entities_ ) {
|
for ( const auto id : entity_ids_ ) {
|
||||||
for_joined_components_impl_<Ts...>(e, std::forward<F>(f));
|
for_joined_components_impl_<Ts...>(entity(*this, id), std::forward<F>(f));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template < typename T, typename... Ts, typename F, typename... Cs >
|
template < typename T, typename... Ts, typename F, typename... Cs >
|
||||||
void world::for_joined_components_impl_(const entity& e, F&& f, Cs&&... cs) {
|
void registry::for_joined_components_impl_(const entity& e, F&& f, Cs&&... cs) {
|
||||||
T* c = find_component_impl_<T>(e);
|
T* c = find_component_impl_<T>(e);
|
||||||
if ( c ) {
|
if ( c ) {
|
||||||
for_joined_components_impl_<Ts...>(
|
for_joined_components_impl_<Ts...>(
|
||||||
@@ -638,19 +1055,19 @@ namespace ecs_hpp
|
|||||||
}
|
}
|
||||||
|
|
||||||
template < typename F, typename... Cs >
|
template < typename F, typename... Cs >
|
||||||
void world::for_joined_components_impl_(const entity& e, F&& f, Cs&&... cs) {
|
void registry::for_joined_components_impl_(const entity& e, F&& f, Cs&&... cs) {
|
||||||
f(e, std::forward<Cs>(cs)...);
|
f(e, std::forward<Cs>(cs)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template < typename... Ts, typename F >
|
template < typename... Ts, typename F >
|
||||||
void world::for_joined_components_impl_(F&& f) const {
|
void registry::for_joined_components_impl_(F&& f) const {
|
||||||
for ( const auto& e : entities_ ) {
|
for ( const auto id : entity_ids_ ) {
|
||||||
for_joined_components_impl_<Ts...>(e, std::forward<F>(f));
|
for_joined_components_impl_<Ts...>(entity(const_cast<registry&>(*this), id), std::forward<F>(f));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template < typename T, typename... Ts, typename F, typename... Cs >
|
template < typename T, typename... Ts, typename F, typename... Cs >
|
||||||
void world::for_joined_components_impl_(const entity& e, F&& f, Cs&&... cs) const {
|
void registry::for_joined_components_impl_(const entity& e, F&& f, Cs&&... cs) const {
|
||||||
const T* c = find_component_impl_<T>(e);
|
const T* c = find_component_impl_<T>(e);
|
||||||
if ( c ) {
|
if ( c ) {
|
||||||
for_joined_components_impl_<Ts...>(
|
for_joined_components_impl_<Ts...>(
|
||||||
@@ -662,7 +1079,7 @@ namespace ecs_hpp
|
|||||||
}
|
}
|
||||||
|
|
||||||
template < typename F, typename... Cs >
|
template < typename F, typename... Cs >
|
||||||
void world::for_joined_components_impl_(const entity& e, F&& f, Cs&&... cs) const {
|
void registry::for_joined_components_impl_(const entity& e, F&& f, Cs&&... cs) const {
|
||||||
f(e, std::forward<Cs>(cs)...);
|
f(e, std::forward<Cs>(cs)...);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
283
ecs_tests.cpp
283
ecs_tests.cpp
@@ -27,6 +27,16 @@ namespace
|
|||||||
velocity_c() = default;
|
velocity_c() = default;
|
||||||
velocity_c(int nx, int ny) : x(nx), y(ny) {}
|
velocity_c(int nx, int ny) : x(nx), y(ny) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool operator==(const position_c& l, const position_c& r) noexcept {
|
||||||
|
return l.x == r.x
|
||||||
|
&& l.y == r.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const velocity_c& l, const velocity_c& r) noexcept {
|
||||||
|
return l.x == r.x
|
||||||
|
&& l.y == r.y;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("detail") {
|
TEST_CASE("detail") {
|
||||||
@@ -40,12 +50,155 @@ TEST_CASE("detail") {
|
|||||||
REQUIRE(ecs::detail::type_family<position_c>::id() == 1u);
|
REQUIRE(ecs::detail::type_family<position_c>::id() == 1u);
|
||||||
REQUIRE(ecs::detail::type_family<velocity_c>::id() == 2u);
|
REQUIRE(ecs::detail::type_family<velocity_c>::id() == 2u);
|
||||||
}
|
}
|
||||||
|
SECTION("sparse_set") {
|
||||||
|
using namespace ecs::detail;
|
||||||
|
{
|
||||||
|
sparse_set<unsigned> s;
|
||||||
|
|
||||||
|
REQUIRE(s.empty());
|
||||||
|
REQUIRE_FALSE(s.size());
|
||||||
|
REQUIRE(s.capacity() == 0u);
|
||||||
|
REQUIRE_FALSE(s.has(42u));
|
||||||
|
REQUIRE(s.find(42u) == s.end());
|
||||||
|
REQUIRE_FALSE(s.find_index(42u).second);
|
||||||
|
REQUIRE_THROWS(s.get_index(42u));
|
||||||
|
|
||||||
|
REQUIRE(s.insert(42u));
|
||||||
|
|
||||||
|
REQUIRE_FALSE(s.empty());
|
||||||
|
REQUIRE(s.size() == 1u);
|
||||||
|
REQUIRE(s.capacity() == 43u);
|
||||||
|
REQUIRE(s.has(42u));
|
||||||
|
REQUIRE_FALSE(s.has(84u));
|
||||||
|
|
||||||
|
REQUIRE(s.find_index(42u).second);
|
||||||
|
REQUIRE(s.find_index(42u).first == 0u);
|
||||||
|
REQUIRE(s.get_index(42u) == 0u);
|
||||||
|
|
||||||
|
s.clear();
|
||||||
|
|
||||||
|
REQUIRE(s.empty());
|
||||||
|
REQUIRE_FALSE(s.size());
|
||||||
|
REQUIRE_FALSE(s.has(42u));
|
||||||
|
|
||||||
|
REQUIRE(s.insert(84u));
|
||||||
|
REQUIRE_FALSE(s.insert(84u));
|
||||||
|
|
||||||
|
REQUIRE(s.has(84u));
|
||||||
|
REQUIRE_FALSE(s.unordered_erase(42u));
|
||||||
|
REQUIRE(s.unordered_erase(84u));
|
||||||
|
REQUIRE_FALSE(s.has(84u));
|
||||||
|
REQUIRE(s.empty());
|
||||||
|
REQUIRE_FALSE(s.size());
|
||||||
|
REQUIRE(s.capacity() == 43u * 2);
|
||||||
|
|
||||||
|
s.insert(42u);
|
||||||
|
s.insert(84u);
|
||||||
|
|
||||||
|
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.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SECTION("sparse_map") {
|
||||||
|
using namespace ecs::detail;
|
||||||
|
{
|
||||||
|
struct obj_t {
|
||||||
|
int x;
|
||||||
|
obj_t(int nx) : x(nx) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
sparse_map<unsigned, obj_t> m;
|
||||||
|
|
||||||
|
REQUIRE(m.empty());
|
||||||
|
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));
|
||||||
|
|
||||||
|
{
|
||||||
|
obj_t o{21u};
|
||||||
|
REQUIRE(m.insert(21u, o));
|
||||||
|
REQUIRE(m.insert(42u, obj_t{42u}));
|
||||||
|
REQUIRE(m.emplace(84u, 84u));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
obj_t o{21u};
|
||||||
|
REQUIRE_FALSE(m.insert(21u, o));
|
||||||
|
REQUIRE_FALSE(m.insert(42u, obj_t{42u}));
|
||||||
|
REQUIRE_FALSE(m.emplace(84u, 84u));
|
||||||
|
}
|
||||||
|
|
||||||
|
REQUIRE_FALSE(m.empty());
|
||||||
|
REQUIRE(m.size() == 3u);
|
||||||
|
REQUIRE(m.capacity() >= 3u);
|
||||||
|
REQUIRE(m.has(21u));
|
||||||
|
REQUIRE(m.has(42u));
|
||||||
|
REQUIRE(m.has(84u));
|
||||||
|
REQUIRE_FALSE(m.has(11u));
|
||||||
|
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.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.unordered_erase(42u));
|
||||||
|
REQUIRE_FALSE(m.unordered_erase(42u));
|
||||||
|
|
||||||
|
REQUIRE(m.has(21u));
|
||||||
|
REQUIRE_FALSE(m.has(42u));
|
||||||
|
REQUIRE(m.has(84u));
|
||||||
|
REQUIRE(m.size() == 2u);
|
||||||
|
|
||||||
|
m.clear();
|
||||||
|
REQUIRE(m.empty());
|
||||||
|
REQUIRE_FALSE(m.size());
|
||||||
|
REQUIRE_FALSE(m.has(21u));
|
||||||
|
REQUIRE_FALSE(m.has(42u));
|
||||||
|
REQUIRE_FALSE(m.has(84u));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("world") {
|
TEST_CASE("registry") {
|
||||||
SECTION("entities") {
|
SECTION("entities") {
|
||||||
{
|
{
|
||||||
ecs::world w;
|
ecs::registry w;
|
||||||
|
|
||||||
ecs::entity e1{w};
|
ecs::entity e1{w};
|
||||||
ecs::entity e2{w};
|
ecs::entity e2{w};
|
||||||
@@ -58,7 +211,7 @@ TEST_CASE("world") {
|
|||||||
REQUIRE_FALSE(w.destroy_entity(e2));
|
REQUIRE_FALSE(w.destroy_entity(e2));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
ecs::world w;
|
ecs::registry w;
|
||||||
|
|
||||||
auto e1 = w.create_entity();
|
auto e1 = w.create_entity();
|
||||||
auto e2 = w.create_entity();
|
auto e2 = w.create_entity();
|
||||||
@@ -81,7 +234,7 @@ TEST_CASE("world") {
|
|||||||
}
|
}
|
||||||
SECTION("component_assigning") {
|
SECTION("component_assigning") {
|
||||||
{
|
{
|
||||||
ecs::world w;
|
ecs::registry w;
|
||||||
ecs::entity e1 = w.create_entity();
|
ecs::entity e1 = w.create_entity();
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -120,7 +273,7 @@ TEST_CASE("world") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
ecs::world w;
|
ecs::registry w;
|
||||||
|
|
||||||
auto e1 = w.create_entity();
|
auto e1 = w.create_entity();
|
||||||
auto e2 = w.create_entity();
|
auto e2 = w.create_entity();
|
||||||
@@ -140,7 +293,7 @@ TEST_CASE("world") {
|
|||||||
REQUIRE(w.exists_component<velocity_c>(e2));
|
REQUIRE(w.exists_component<velocity_c>(e2));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
ecs::world w;
|
ecs::registry w;
|
||||||
auto e1 = w.create_entity();
|
auto e1 = w.create_entity();
|
||||||
REQUIRE(e1.destroy());
|
REQUIRE(e1.destroy());
|
||||||
REQUIRE_FALSE(e1.assign_component<position_c>());
|
REQUIRE_FALSE(e1.assign_component<position_c>());
|
||||||
@@ -149,7 +302,7 @@ TEST_CASE("world") {
|
|||||||
}
|
}
|
||||||
SECTION("component_accessing") {
|
SECTION("component_accessing") {
|
||||||
{
|
{
|
||||||
ecs::world w;
|
ecs::registry w;
|
||||||
|
|
||||||
auto e1 = w.create_entity();
|
auto e1 = w.create_entity();
|
||||||
auto e2 = w.create_entity();
|
auto e2 = w.create_entity();
|
||||||
@@ -170,7 +323,7 @@ TEST_CASE("world") {
|
|||||||
REQUIRE_THROWS_AS(e2.get_component<position_c>(), ecs::basic_exception);
|
REQUIRE_THROWS_AS(e2.get_component<position_c>(), ecs::basic_exception);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
ecs::world w;
|
ecs::registry w;
|
||||||
|
|
||||||
const auto e1 = w.create_entity();
|
const auto e1 = w.create_entity();
|
||||||
const auto e2 = w.create_entity();
|
const auto e2 = w.create_entity();
|
||||||
@@ -185,7 +338,7 @@ TEST_CASE("world") {
|
|||||||
REQUIRE(e2.find_component<velocity_c>()->y == 4);
|
REQUIRE(e2.find_component<velocity_c>()->y == 4);
|
||||||
|
|
||||||
{
|
{
|
||||||
const ecs::world& ww = w;
|
const ecs::registry& ww = w;
|
||||||
REQUIRE(ww.get_component<position_c>(e1).x == 1);
|
REQUIRE(ww.get_component<position_c>(e1).x == 1);
|
||||||
REQUIRE(ww.get_component<position_c>(e1).y == 2);
|
REQUIRE(ww.get_component<position_c>(e1).y == 2);
|
||||||
|
|
||||||
@@ -202,10 +355,79 @@ TEST_CASE("world") {
|
|||||||
REQUIRE_FALSE(ww.find_component<velocity_c>(e2));
|
REQUIRE_FALSE(ww.find_component<velocity_c>(e2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
ecs::registry w;
|
||||||
|
|
||||||
|
auto e1 = w.create_entity();
|
||||||
|
|
||||||
|
REQUIRE(e1.find_components<>() ==
|
||||||
|
std::make_tuple());
|
||||||
|
REQUIRE(e1.find_components<velocity_c>() ==
|
||||||
|
std::make_tuple<velocity_c*>(nullptr));
|
||||||
|
REQUIRE(e1.find_components<position_c, velocity_c>() ==
|
||||||
|
std::make_tuple<position_c*, velocity_c*>(nullptr, nullptr));
|
||||||
|
|
||||||
|
REQUIRE(e1.get_components<>() == std::make_tuple());
|
||||||
|
REQUIRE_THROWS(e1.get_components<velocity_c>());
|
||||||
|
REQUIRE_THROWS(e1.get_components<position_c, velocity_c>());
|
||||||
|
|
||||||
|
{
|
||||||
|
const auto ee1 = e1;
|
||||||
|
|
||||||
|
REQUIRE(ee1.find_components<>() ==
|
||||||
|
std::make_tuple());
|
||||||
|
REQUIRE(ee1.find_components<velocity_c>() ==
|
||||||
|
std::make_tuple<velocity_c*>(nullptr));
|
||||||
|
REQUIRE(ee1.find_components<position_c, velocity_c>() ==
|
||||||
|
std::make_tuple<position_c*, velocity_c*>(nullptr, nullptr));
|
||||||
|
|
||||||
|
REQUIRE(ee1.get_components<>() == std::make_tuple());
|
||||||
|
REQUIRE_THROWS(ee1.get_components<velocity_c>());
|
||||||
|
REQUIRE_THROWS(ee1.get_components<position_c, velocity_c>());
|
||||||
|
}
|
||||||
|
|
||||||
|
e1.assign_component<velocity_c>(3, 4);
|
||||||
|
|
||||||
|
REQUIRE(e1.find_components<velocity_c>() ==
|
||||||
|
std::make_tuple<velocity_c*>(e1.find_component<velocity_c>()));
|
||||||
|
REQUIRE(e1.find_components<position_c, velocity_c>() ==
|
||||||
|
std::make_tuple<position_c*, velocity_c*>(nullptr, e1.find_component<velocity_c>()));
|
||||||
|
|
||||||
|
REQUIRE(e1.get_components<velocity_c>() ==
|
||||||
|
std::make_tuple<velocity_c&>(e1.get_component<velocity_c>()));
|
||||||
|
REQUIRE_THROWS(e1.get_components<position_c, velocity_c>());
|
||||||
|
|
||||||
|
{
|
||||||
|
const auto ee1 = e1;
|
||||||
|
|
||||||
|
REQUIRE(ee1.find_components<velocity_c>() ==
|
||||||
|
std::make_tuple<velocity_c*>(e1.find_component<velocity_c>()));
|
||||||
|
REQUIRE(ee1.find_components<position_c, velocity_c>() ==
|
||||||
|
std::make_tuple<position_c*, velocity_c*>(nullptr, e1.find_component<velocity_c>()));
|
||||||
|
|
||||||
|
REQUIRE(ee1.get_components<velocity_c>() ==
|
||||||
|
std::make_tuple<const velocity_c&>(ee1.get_component<velocity_c>()));
|
||||||
|
REQUIRE_THROWS(ee1.get_components<position_c, velocity_c>());
|
||||||
|
}
|
||||||
|
|
||||||
|
e1.assign_component<position_c>(1, 2);
|
||||||
|
|
||||||
|
auto p = e1.get_components<position_c, velocity_c>();
|
||||||
|
std::get<0>(p).x = 10;
|
||||||
|
std::get<1>(p).x = 30;
|
||||||
|
REQUIRE(e1.get_component<position_c>().x == 10);
|
||||||
|
REQUIRE(e1.get_component<velocity_c>().x == 30);
|
||||||
|
|
||||||
|
auto p2 = e1.find_components<position_c, velocity_c>();
|
||||||
|
std::get<0>(p2)->y = 20;
|
||||||
|
std::get<1>(p2)->y = 40;
|
||||||
|
REQUIRE(e1.get_component<position_c>().y == 20);
|
||||||
|
REQUIRE(e1.get_component<velocity_c>().y == 40);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
SECTION("for_each_component") {
|
SECTION("for_each_component") {
|
||||||
{
|
{
|
||||||
ecs::world w;
|
ecs::registry w;
|
||||||
|
|
||||||
auto e1 = w.create_entity();
|
auto e1 = w.create_entity();
|
||||||
auto e2 = w.create_entity();
|
auto e2 = w.create_entity();
|
||||||
@@ -227,7 +449,7 @@ TEST_CASE("world") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const ecs::world& ww = w;
|
const ecs::registry& ww = w;
|
||||||
ecs::entity_id acc1 = 0;
|
ecs::entity_id acc1 = 0;
|
||||||
int acc2 = 0;
|
int acc2 = 0;
|
||||||
ww.for_each_component<position_c>([&acc1, &acc2](ecs::entity e, const position_c& p){
|
ww.for_each_component<position_c>([&acc1, &acc2](ecs::entity e, const position_c& p){
|
||||||
@@ -239,10 +461,9 @@ TEST_CASE("world") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("for_joined_components") {
|
SECTION("for_joined_components") {
|
||||||
{
|
{
|
||||||
ecs::world w;
|
ecs::registry w;
|
||||||
|
|
||||||
auto e1 = w.create_entity();
|
auto e1 = w.create_entity();
|
||||||
auto e2 = w.create_entity();
|
auto e2 = w.create_entity();
|
||||||
@@ -272,7 +493,7 @@ TEST_CASE("world") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const ecs::world& ww = w;
|
const ecs::registry& ww = w;
|
||||||
ecs::entity_id acc1 = 0;
|
ecs::entity_id acc1 = 0;
|
||||||
int acc2 = 0;
|
int acc2 = 0;
|
||||||
ww.for_joined_components<position_c, velocity_c>([&acc1, &acc2](
|
ww.for_joined_components<position_c, velocity_c>([&acc1, &acc2](
|
||||||
@@ -286,4 +507,38 @@ TEST_CASE("world") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
SECTION("systems") {
|
||||||
|
{
|
||||||
|
class movement_system : public ecs::system {
|
||||||
|
public:
|
||||||
|
void process(ecs::registry& owner) override {
|
||||||
|
owner.for_joined_components<position_c, velocity_c>([](
|
||||||
|
ecs::entity e, position_c& p, const velocity_c& v)
|
||||||
|
{
|
||||||
|
p.x += v.x;
|
||||||
|
p.y += v.y;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ecs::registry w;
|
||||||
|
w.add_system<movement_system>();
|
||||||
|
|
||||||
|
auto e1 = w.create_entity();
|
||||||
|
auto e2 = w.create_entity();
|
||||||
|
|
||||||
|
e1.assign_component<position_c>(1, 2);
|
||||||
|
e1.assign_component<velocity_c>(3, 4);
|
||||||
|
e2.assign_component<position_c>(5, 6);
|
||||||
|
e2.assign_component<velocity_c>(7, 8);
|
||||||
|
|
||||||
|
w.process_systems();
|
||||||
|
|
||||||
|
REQUIRE(e1.get_component<position_c>().x == 1 + 3);
|
||||||
|
REQUIRE(e1.get_component<position_c>().y == 2 + 4);
|
||||||
|
|
||||||
|
REQUIRE(e2.get_component<position_c>().x == 5 + 7);
|
||||||
|
REQUIRE(e2.get_component<position_c>().y == 6 + 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user