mirror of
https://github.com/BlackMATov/ecs.hpp.git
synced 2025-12-16 22:19:21 +07:00
add basic prototype functionality
This commit is contained in:
243
ecs.hpp
243
ecs.hpp
@@ -157,6 +157,26 @@ 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
|
// next_capacity_size
|
||||||
//
|
//
|
||||||
@@ -317,6 +337,18 @@ namespace ecs_hpp
|
|||||||
sparse_set(const Indexer& indexer = Indexer())
|
sparse_set(const Indexer& indexer = Indexer())
|
||||||
: indexer_(indexer) {}
|
: indexer_(indexer) {}
|
||||||
|
|
||||||
|
sparse_set(const sparse_set& other) = default;
|
||||||
|
sparse_set& operator=(const sparse_set& other) = default;
|
||||||
|
|
||||||
|
sparse_set(sparse_set&& other) noexcept = default;
|
||||||
|
sparse_set& operator=(sparse_set&& other) noexcept = default;
|
||||||
|
|
||||||
|
void swap(sparse_set& other) noexcept {
|
||||||
|
using std::swap;
|
||||||
|
swap(dense_, other.dense_);
|
||||||
|
swap(sparse_, other.sparse_);
|
||||||
|
}
|
||||||
|
|
||||||
template < typename UT >
|
template < typename UT >
|
||||||
bool insert(UT&& v) {
|
bool insert(UT&& v) {
|
||||||
if ( has(v) ) {
|
if ( has(v) ) {
|
||||||
@@ -394,6 +426,11 @@ namespace ecs_hpp
|
|||||||
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -447,6 +484,18 @@ namespace ecs_hpp
|
|||||||
sparse_map(const Indexer& indexer = Indexer())
|
sparse_map(const Indexer& indexer = Indexer())
|
||||||
: keys_(indexer) {}
|
: keys_(indexer) {}
|
||||||
|
|
||||||
|
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 >
|
template < typename UK, typename UT >
|
||||||
bool insert(UK&& k, UT&& v) {
|
bool insert(UK&& k, UT&& v) {
|
||||||
if ( keys_.has(k) ) {
|
if ( keys_.has(k) ) {
|
||||||
@@ -541,6 +590,11 @@ 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -952,6 +1006,72 @@ 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) &&;
|
||||||
|
|
||||||
|
entity create_entity(registry& owner) const;
|
||||||
|
private:
|
||||||
|
detail::sparse_map<
|
||||||
|
family_id,
|
||||||
|
detail::component_applier_uptr> appliers_;
|
||||||
|
};
|
||||||
|
|
||||||
|
void swap(prototype& l, prototype& r) noexcept;
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
//
|
//
|
||||||
// system
|
// system
|
||||||
@@ -1574,6 +1694,129 @@ 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 entity prototype::create_entity(registry& owner) const {
|
||||||
|
auto ent = owner.create_entity();
|
||||||
|
try {
|
||||||
|
for ( const auto family_id : appliers_ ) {
|
||||||
|
appliers_.get(family_id)->apply(ent, true);
|
||||||
|
}
|
||||||
|
} catch (...) {
|
||||||
|
owner.destroy_entity(ent);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
return ent;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void swap(prototype& l, prototype& r) noexcept {
|
||||||
|
l.swap(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
//
|
//
|
||||||
// registry impl
|
// registry impl
|
||||||
|
|||||||
@@ -514,6 +514,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 = p.create_entity(w);
|
||||||
|
const auto e2 = p.create_entity(w);
|
||||||
|
|
||||||
|
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 = p.create_entity(w);
|
||||||
|
const auto e2 = p.create_entity(w);
|
||||||
|
|
||||||
|
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 = p3.create_entity(w);
|
||||||
|
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 = p1.create_entity(w);
|
||||||
|
REQUIRE(e1.get_component<position_c>() == position_c(1,2));
|
||||||
|
const auto e2 = p2.create_entity(w);
|
||||||
|
REQUIRE(e2.get_component<position_c>() == position_c(3,4));
|
||||||
|
|
||||||
|
const auto e3 = p3.create_entity(w);
|
||||||
|
REQUIRE(e3.get_component<position_c>() == position_c(1,2));
|
||||||
|
REQUIRE(e3.get_component<velocity_c>() == velocity_c(3,4));
|
||||||
|
const auto e4 = p4.create_entity(w);
|
||||||
|
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;
|
||||||
|
|||||||
Reference in New Issue
Block a user