Implement ecs::entity_container::attach/detach
This commit is contained in:
parent
59c803d31c
commit
8b1157c641
7 changed files with 434 additions and 62 deletions
|
|
@ -10,15 +10,16 @@ namespace psemek::ecs::detail
|
|||
|
||||
struct column
|
||||
{
|
||||
column(util::uuid const & uuid)
|
||||
: uuid_(uuid)
|
||||
{}
|
||||
|
||||
std::uint8_t * data()
|
||||
{
|
||||
return data_;
|
||||
}
|
||||
|
||||
std::size_t stride() const
|
||||
{
|
||||
return stride_;
|
||||
}
|
||||
|
||||
util::uuid const & uuid() const
|
||||
{
|
||||
return uuid_;
|
||||
|
|
@ -36,7 +37,13 @@ namespace psemek::ecs::detail
|
|||
|
||||
protected:
|
||||
std::uint8_t * data_ = nullptr;
|
||||
std::size_t stride_;
|
||||
util::uuid uuid_;
|
||||
|
||||
column(std::size_t stride, util::uuid const & uuid)
|
||||
: stride_(stride)
|
||||
, uuid_(uuid)
|
||||
{}
|
||||
};
|
||||
|
||||
template <typename Component, bool Empty = detail::is_empty_v<Component>>
|
||||
|
|
@ -81,7 +88,7 @@ namespace psemek::ecs::detail
|
|||
|
||||
template <typename Component, bool Empty>
|
||||
column_impl<Component, Empty>::column_impl()
|
||||
: column(Component::uuid())
|
||||
: column(detail::stride<Component>(), Component::uuid())
|
||||
{}
|
||||
|
||||
template <typename Component, bool Empty>
|
||||
|
|
@ -177,7 +184,7 @@ namespace psemek::ecs::detail
|
|||
|
||||
template <typename Component>
|
||||
column_impl<Component, true>::column_impl()
|
||||
: column(Component::uuid())
|
||||
: column(detail::stride<Component>(), Component::uuid())
|
||||
{
|
||||
data_ = reinterpret_cast<std::uint8_t *>(new Component[1]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,11 @@ namespace psemek::ecs::detail
|
|||
return entity_handles_;
|
||||
}
|
||||
|
||||
util::span<std::unique_ptr<detail::column> const> columns() const
|
||||
{
|
||||
return columns_;
|
||||
}
|
||||
|
||||
std::size_t column_count() const
|
||||
{
|
||||
return component_uuid_to_column_.size();
|
||||
|
|
@ -44,6 +49,7 @@ namespace psemek::ecs::detail
|
|||
}
|
||||
|
||||
std::size_t push_row(entity_handle handle);
|
||||
std::size_t move_row(entity_handle handle, table * from, std::size_t from_row);
|
||||
void swap_rows(std::size_t row1, std::size_t row2);
|
||||
void pop_row();
|
||||
void clear();
|
||||
|
|
@ -68,7 +74,8 @@ namespace psemek::ecs::detail
|
|||
|
||||
protected:
|
||||
std::size_t hash_;
|
||||
util::hash_map<util::uuid, std::unique_ptr<detail::column>> component_uuid_to_column_;
|
||||
std::vector<std::unique_ptr<detail::column>> columns_;
|
||||
util::hash_map<util::uuid, detail::column *> component_uuid_to_column_;
|
||||
|
||||
std::vector<entity_handle> entity_handles_;
|
||||
|
||||
|
|
|
|||
|
|
@ -43,8 +43,8 @@ namespace psemek::ecs::detail
|
|||
// TODO: store tables in a bitmask trie balanced by subtree size
|
||||
struct table_container
|
||||
{
|
||||
template <typename ... Components>
|
||||
std::pair<table *, bool> insert(util::span<util::uuid const> component_uuids);
|
||||
table * get(util::span<util::uuid const> component_uuids);
|
||||
table * insert(std::unique_ptr<table> table);
|
||||
|
||||
template <typename Function>
|
||||
void apply(Function && function, util::span<util::uuid const> component_uuids);
|
||||
|
|
@ -53,20 +53,19 @@ namespace psemek::ecs::detail
|
|||
util::hash_set<std::unique_ptr<table>, table_hashset_hash, table_hashset_equal> tables_;
|
||||
};
|
||||
|
||||
template <typename ... Components>
|
||||
std::pair<table *, bool> table_container::insert(util::span<util::uuid const> component_uuids)
|
||||
inline table * table_container::get(util::span<util::uuid const> component_uuids)
|
||||
{
|
||||
auto it = tables_.find(component_uuids);
|
||||
if (it != tables_.end())
|
||||
return {it->get(), false};
|
||||
return it->get();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<column>> columns;
|
||||
(columns.push_back(std::make_unique<column_impl<Components>>()), ...);
|
||||
|
||||
auto table = std::make_unique<detail::table>(std::move(columns));
|
||||
inline table * table_container::insert(std::unique_ptr<table> table)
|
||||
{
|
||||
auto result = table.get();
|
||||
tables_.insert(std::move(table));
|
||||
return {result, true};
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename Function>
|
||||
|
|
|
|||
|
|
@ -122,23 +122,29 @@ namespace psemek::ecs
|
|||
entity_accessor get(entity_handle const & entity);
|
||||
|
||||
/** Attach new components to an existing entity, or update existing
|
||||
* components with new values.
|
||||
* components with new values. Other components of this entity
|
||||
* are left untouched.
|
||||
* Attaching components invalidates all previously created entity accessors.
|
||||
* For the purposes of `apply()` semantics, attaching behaves as if the
|
||||
* entity was destroyed and then recreated with the same handle.
|
||||
* If any two of the passed component types are equal, the call fails with
|
||||
* a compilation error.
|
||||
* Attaching components invalidates all previously created entity accessors.
|
||||
* If the handle wasn't previously obtained by a `create()` call, or
|
||||
* the refered entity was already destroyed, the behavior is undefined.
|
||||
*/
|
||||
// TODO: implement
|
||||
template <typename ... Components>
|
||||
void attach(entity_handle const & entity, Components && ... components);
|
||||
|
||||
/** Detach (remove) components from an existing entity.
|
||||
/** Detach (remove) components from an existing entity. Other components of this entity
|
||||
* are left untouched. Detaching a component that doesn't exist does nothing.
|
||||
* Detaching components invalidates all previously created entity accessors.
|
||||
* For the purposes of `apply()` semantics, detaching behaves as if the
|
||||
* entity was destroyed and then recreated with the same handle.
|
||||
* If any two of the passed component types are equal, the call fails with
|
||||
* a compilation error.
|
||||
* If the handle wasn't previously obtained by a `create()` call, or
|
||||
* the refered entity was already destroyed, the behavior is undefined.
|
||||
*/
|
||||
// TODO: implement
|
||||
template <typename ... Components>
|
||||
void detach(entity_handle const & entity);
|
||||
|
||||
|
|
@ -155,10 +161,11 @@ namespace psemek::ecs
|
|||
* void(entity_handle, components...)
|
||||
* void(entity_container, components...)
|
||||
* void(entity_container, entity_handle, components...)
|
||||
* The function can freely create or destroy entities. It is unspecified
|
||||
* whether the function will or will not visit newly created entities
|
||||
* during this `apply()` call. The function is guaranteed not to visit
|
||||
* destroyed entities (unless it did so before the entity was destroyed).
|
||||
* The function can freely create or destroy entities, and or attach/detach
|
||||
* components to existing entities. It is unspecified whether the function
|
||||
* will or will not visit newly created entities during this `apply()` call.
|
||||
* The function is guaranteed not to visit destroyed entities (unless it did
|
||||
* so before the entity was destroyed).
|
||||
* An optional query cache can be supplied to speed up iteration.
|
||||
* If the query cache wasn't created with the exact sequence of component
|
||||
* types, the behavior is undefined.
|
||||
|
|
@ -193,7 +200,10 @@ namespace psemek::ecs
|
|||
detail::query_cache_container query_cache_container_;
|
||||
|
||||
std::vector<util::uuid> uuid_helper_;
|
||||
util::hash_set<util::uuid> uuid_set_helper_;
|
||||
|
||||
detail::table * insert_table(std::vector<std::unique_ptr<detail::column>> columns);
|
||||
void do_destroy(entity_handle const & entity);
|
||||
void remove_row(detail::table & table, std::uint32_t row, util::span<detail::entity_data> entities);
|
||||
};
|
||||
|
||||
|
|
@ -204,15 +214,14 @@ namespace psemek::ecs
|
|||
|
||||
detail::component_uuid_helper<Components...> uuids;
|
||||
|
||||
auto insert_result = table_container_.insert<Components...>(uuids.get());
|
||||
auto table = insert_result.first;
|
||||
bool created = insert_result.second;
|
||||
auto table = table_container_.get(uuids.get());
|
||||
|
||||
if (created)
|
||||
if (!table)
|
||||
{
|
||||
query_cache_container_.apply([table](detail::query_cache & cache){
|
||||
cache.add(table);
|
||||
}, [table](util::uuid const & uuid){ return table->column(uuid) != nullptr; });
|
||||
std::vector<std::unique_ptr<detail::column>> columns;
|
||||
(columns.push_back(std::make_unique<detail::column_impl<Components>>()), ...);
|
||||
|
||||
table = insert_table(std::move(columns));
|
||||
}
|
||||
|
||||
if (table->get_iteration_data())
|
||||
|
|
@ -228,6 +237,88 @@ namespace psemek::ecs
|
|||
return handle;
|
||||
}
|
||||
|
||||
template <typename ... Components>
|
||||
void entity_container::attach(entity_handle const & entity, Components && ... components)
|
||||
{
|
||||
static_assert(detail::all_different_types_v<Components...>, "all component types must be different");
|
||||
|
||||
auto & data = entity_list_.get_entities()[entity.id];
|
||||
for (auto const & column : data.table->columns())
|
||||
uuid_helper_.push_back(column->uuid());
|
||||
|
||||
((data.table->column(Components::uuid()) ? 0 : (uuid_helper_.push_back(Components::uuid()), 0)), ...);
|
||||
|
||||
auto table = table_container_.get(uuid_helper_);
|
||||
|
||||
if (!table)
|
||||
{
|
||||
std::vector<std::unique_ptr<detail::column>> columns;
|
||||
for (auto const & column : data.table->columns())
|
||||
columns.push_back(column->clone());
|
||||
|
||||
((data.table->column(Components::uuid()) ? 0 : (columns.push_back(std::make_unique<detail::column_impl<Components>>()), 0)), ...);
|
||||
|
||||
table = insert_table(std::move(columns));
|
||||
}
|
||||
|
||||
if (table != data.table)
|
||||
{
|
||||
if (table->get_iteration_data())
|
||||
table = table->get_delayed_table();
|
||||
|
||||
auto new_row = table->move_row(entity, data.table, data.row);
|
||||
do_destroy(entity);
|
||||
|
||||
data.table = table;
|
||||
data.row = new_row;
|
||||
}
|
||||
|
||||
auto accessor = get(entity);
|
||||
((accessor.get<Components>() = std::move(components)), ...);
|
||||
|
||||
uuid_helper_.clear();
|
||||
}
|
||||
|
||||
template <typename ... Components>
|
||||
void entity_container::detach(entity_handle const & entity)
|
||||
{
|
||||
static_assert(detail::all_different_types_v<Components...>, "all component types must be different");
|
||||
|
||||
(uuid_set_helper_.insert(Components::uuid()), ...);
|
||||
|
||||
auto & data = entity_list_.get_entities()[entity.id];
|
||||
for (auto const & column : data.table->columns())
|
||||
if (!uuid_set_helper_.contains(column->uuid()))
|
||||
uuid_helper_.push_back(column->uuid());
|
||||
|
||||
auto table = table_container_.get(uuid_helper_);
|
||||
|
||||
if (!table)
|
||||
{
|
||||
std::vector<std::unique_ptr<detail::column>> columns;
|
||||
for (auto const & column : data.table->columns())
|
||||
if (!uuid_set_helper_.contains(column->uuid()))
|
||||
columns.push_back(column->clone());
|
||||
|
||||
table = insert_table(std::move(columns));
|
||||
}
|
||||
|
||||
if (table != data.table)
|
||||
{
|
||||
if (table->get_iteration_data())
|
||||
table = table->get_delayed_table();
|
||||
|
||||
auto new_row = table->move_row(entity, data.table, data.row);
|
||||
do_destroy(entity);
|
||||
|
||||
data.table = table;
|
||||
data.row = new_row;
|
||||
}
|
||||
|
||||
uuid_set_helper_.clear();
|
||||
uuid_helper_.clear();
|
||||
}
|
||||
|
||||
template <typename ... Components>
|
||||
query_cache entity_container::cache()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -11,44 +11,59 @@ namespace psemek::ecs::detail
|
|||
{
|
||||
auto uuid = column->uuid();
|
||||
hasher(uuid);
|
||||
component_uuid_to_column_.insert({uuid, std::move(column)});
|
||||
component_uuid_to_column_.insert({uuid, column.get()});
|
||||
}
|
||||
hash_ = hasher.result;
|
||||
|
||||
columns_ = std::move(columns);
|
||||
}
|
||||
|
||||
detail::column * table::column(util::uuid const & uuid) const
|
||||
{
|
||||
if (auto it = component_uuid_to_column_.find(uuid); it != component_uuid_to_column_.end())
|
||||
return it->second.get();
|
||||
return it->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::size_t table::push_row(entity_handle handle)
|
||||
{
|
||||
for (auto & pair : component_uuid_to_column_)
|
||||
pair.second->push_row();
|
||||
for (auto & column : columns_)
|
||||
column->push_row();
|
||||
entity_handles_.push_back(handle);
|
||||
return entity_handles_.size() - 1;
|
||||
}
|
||||
|
||||
std::size_t table::move_row(entity_handle handle, table * from, std::size_t from_row)
|
||||
{
|
||||
for (auto & column : columns_)
|
||||
{
|
||||
if (auto other_column = from->column(column->uuid()))
|
||||
column->emplace_rows(other_column->data() + other_column->stride() * from_row, 1);
|
||||
else
|
||||
column->push_row();
|
||||
}
|
||||
entity_handles_.push_back(handle);
|
||||
return entity_handles_.size() - 1;
|
||||
}
|
||||
|
||||
void table::swap_rows(std::size_t row1, std::size_t row2)
|
||||
{
|
||||
for (auto & pair : component_uuid_to_column_)
|
||||
pair.second->swap_rows(row1, row2);
|
||||
for (auto & column : columns_)
|
||||
column->swap_rows(row1, row2);
|
||||
std::swap(entity_handles_[row1], entity_handles_[row2]);
|
||||
}
|
||||
|
||||
void table::pop_row()
|
||||
{
|
||||
for (auto & pair : component_uuid_to_column_)
|
||||
pair.second->pop_row();
|
||||
for (auto & column : columns_)
|
||||
column->pop_row();
|
||||
entity_handles_.pop_back();
|
||||
}
|
||||
|
||||
void table::clear()
|
||||
{
|
||||
for (auto & pair : component_uuid_to_column_)
|
||||
pair.second->clear();
|
||||
for (auto & column : columns_)
|
||||
column->clear();
|
||||
entity_handles_.clear();
|
||||
}
|
||||
|
||||
|
|
@ -65,8 +80,8 @@ namespace psemek::ecs::detail
|
|||
std::unique_ptr<table> table::clone() const
|
||||
{
|
||||
std::vector<std::unique_ptr<detail::column>> columns;
|
||||
for (auto const & pair : component_uuid_to_column_)
|
||||
columns.push_back(pair.second->clone());
|
||||
for (auto const & column : columns_)
|
||||
columns.push_back(column->clone());
|
||||
return std::make_unique<table>(std::move(columns));
|
||||
}
|
||||
|
||||
|
|
@ -83,10 +98,10 @@ namespace psemek::ecs::detail
|
|||
return {};
|
||||
|
||||
std::size_t count = delayed_table_->row_count();
|
||||
for (auto & pair : component_uuid_to_column_)
|
||||
for (std::size_t i = 0; i < columns_.size(); ++i)
|
||||
{
|
||||
auto * src_column = delayed_table_->column(pair.second->uuid());
|
||||
pair.second->emplace_rows(src_column->data(), count);
|
||||
auto * src_column = delayed_table_->columns_[i].get();
|
||||
columns_[i]->emplace_rows(src_column->data(), count);
|
||||
}
|
||||
entity_handles_.insert(entity_handles_.end(), delayed_table_->entity_handles_.begin(), delayed_table_->entity_handles_.end());
|
||||
|
||||
|
|
|
|||
|
|
@ -10,20 +10,8 @@ namespace psemek::ecs
|
|||
|
||||
void entity_container::destroy(entity_handle const & entity)
|
||||
{
|
||||
auto entities = entity_list_.get_entities();
|
||||
auto & data = entities[entity.id];
|
||||
auto & iteration_data = data.table->get_iteration_data();
|
||||
|
||||
if (!iteration_data || iteration_data->current_row < data.row)
|
||||
{
|
||||
remove_row(*data.table, data.row, entities);
|
||||
entity_list_.destroy(entity.id);
|
||||
}
|
||||
else
|
||||
{
|
||||
data.table->push_remove(data.row);
|
||||
entity_list_.destroy(entity.id);
|
||||
}
|
||||
do_destroy(entity);
|
||||
entity_list_.destroy(entity.id);
|
||||
}
|
||||
|
||||
entity_accessor entity_container::get(entity_handle const & entity)
|
||||
|
|
@ -32,6 +20,29 @@ namespace psemek::ecs
|
|||
return {data.table, data.row};
|
||||
}
|
||||
|
||||
detail::table * entity_container::insert_table(std::vector<std::unique_ptr<detail::column>> columns)
|
||||
{
|
||||
auto table = table_container_.insert(std::make_unique<detail::table>(std::move(columns)));
|
||||
|
||||
query_cache_container_.apply([table](detail::query_cache & cache){
|
||||
cache.add(table);
|
||||
}, [table](util::uuid const & uuid){ return table->column(uuid) != nullptr; });
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
void entity_container::do_destroy(entity_handle const & entity)
|
||||
{
|
||||
auto entities = entity_list_.get_entities();
|
||||
auto & data = entities[entity.id];
|
||||
auto & iteration_data = data.table->get_iteration_data();
|
||||
|
||||
if (!iteration_data || iteration_data->current_row < data.row)
|
||||
remove_row(*data.table, data.row, entities);
|
||||
else
|
||||
data.table->push_remove(data.row);
|
||||
}
|
||||
|
||||
void entity_container::remove_row(detail::table & table, std::uint32_t row, util::span<detail::entity_data> entities)
|
||||
{
|
||||
// Swap with the last row in that table
|
||||
|
|
|
|||
242
libs/ecs/tests/attach.cpp
Normal file
242
libs/ecs/tests/attach.cpp
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
#include <psemek/test/test.hpp>
|
||||
|
||||
#include <psemek/ecs/entity_container.hpp>
|
||||
#include <psemek/ecs/declare_uuid.hpp>
|
||||
#include <psemek/random/generator.hpp>
|
||||
#include <psemek/random/uniform.hpp>
|
||||
|
||||
using namespace psemek;
|
||||
using namespace psemek::ecs;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
struct component_1
|
||||
{
|
||||
int value;
|
||||
|
||||
psemek_declare_uuid("component_1")
|
||||
};
|
||||
|
||||
struct component_2
|
||||
{
|
||||
int value;
|
||||
|
||||
psemek_declare_uuid("component_2")
|
||||
};
|
||||
|
||||
void check_impl(entity_container & container, int count0, int count1, int count2, int count12)
|
||||
{
|
||||
int call_count = 0;
|
||||
container.apply<>([&]{ ++call_count; });
|
||||
expect_equal(call_count, count0 + count1 + count2 + count12);
|
||||
|
||||
call_count = 0;
|
||||
container.apply<component_1>([&](component_1 const &){ ++call_count; });
|
||||
expect_equal(call_count, count1 + count12);
|
||||
|
||||
call_count = 0;
|
||||
container.apply<component_2>([&](component_2 const &){ ++call_count; });
|
||||
expect_equal(call_count, count2 + count12);
|
||||
|
||||
call_count = 0;
|
||||
container.apply<component_1, component_2>([&](component_1 const &, component_2 const &){ ++call_count; });
|
||||
expect_equal(call_count, count12);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
test_case(ecs_attach_empty)
|
||||
{
|
||||
entity_container container;
|
||||
|
||||
auto h = container.create();
|
||||
|
||||
expect(container.get(h).get_if<component_1>() == nullptr);
|
||||
expect(container.get(h).get_if<component_2>() == nullptr);
|
||||
|
||||
check_impl(container, 1, 0, 0, 0);
|
||||
}
|
||||
|
||||
test_case(ecs_attach_one)
|
||||
{
|
||||
entity_container container;
|
||||
|
||||
auto h = container.create();
|
||||
|
||||
container.attach(h, component_1{10});
|
||||
|
||||
expect_equal(container.get(h).get<component_1>().value, 10);
|
||||
expect(container.get(h).get_if<component_2>() == nullptr);
|
||||
|
||||
check_impl(container, 0, 1, 0, 0);
|
||||
}
|
||||
|
||||
test_case(ecs_attach_twice)
|
||||
{
|
||||
entity_container container;
|
||||
|
||||
auto h = container.create();
|
||||
|
||||
container.attach(h, component_1{10});
|
||||
container.attach(h, component_1{11});
|
||||
|
||||
expect_equal(container.get(h).get<component_1>().value, 11);
|
||||
expect(container.get(h).get_if<component_2>() == nullptr);
|
||||
|
||||
check_impl(container, 0, 1, 0, 0);
|
||||
}
|
||||
|
||||
test_case(ecs_attach_two)
|
||||
{
|
||||
entity_container container;
|
||||
|
||||
auto h = container.create();
|
||||
|
||||
container.attach(h, component_1{10});
|
||||
container.attach(h, component_2{20});
|
||||
|
||||
expect_equal(container.get(h).get<component_1>().value, 10);
|
||||
expect_equal(container.get(h).get<component_2>().value, 20);
|
||||
|
||||
check_impl(container, 0, 0, 0, 1);
|
||||
}
|
||||
|
||||
test_case(ecs_detach_empty)
|
||||
{
|
||||
entity_container container;
|
||||
|
||||
auto h = container.create();
|
||||
|
||||
container.detach<component_1>(h);
|
||||
|
||||
expect(container.get(h).get_if<component_1>() == nullptr);
|
||||
expect(container.get(h).get_if<component_2>() == nullptr);
|
||||
|
||||
check_impl(container, 1, 0, 0, 0);
|
||||
}
|
||||
|
||||
test_case(ecs_detach_nonexistent)
|
||||
{
|
||||
entity_container container;
|
||||
|
||||
auto h = container.create(component_1{10});
|
||||
|
||||
container.detach<component_2>(h);
|
||||
|
||||
expect_equal(container.get(h).get<component_1>().value, 10);
|
||||
expect(container.get(h).get_if<component_2>() == nullptr);
|
||||
|
||||
check_impl(container, 0, 1, 0, 0);
|
||||
}
|
||||
|
||||
test_case(ecs_detach_one)
|
||||
{
|
||||
entity_container container;
|
||||
|
||||
auto h = container.create(component_1{10}, component_2{20});
|
||||
|
||||
container.detach<component_2>(h);
|
||||
|
||||
expect_equal(container.get(h).get<component_1>().value, 10);
|
||||
expect(container.get(h).get_if<component_2>() == nullptr);
|
||||
|
||||
check_impl(container, 0, 1, 0, 0);
|
||||
}
|
||||
|
||||
test_case(ecs_detach_two)
|
||||
{
|
||||
entity_container container;
|
||||
|
||||
auto h = container.create(component_1{10}, component_2{20});
|
||||
|
||||
container.detach<component_1>(h);
|
||||
container.detach<component_2>(h);
|
||||
|
||||
expect(container.get(h).get_if<component_1>() == nullptr);
|
||||
expect(container.get(h).get_if<component_2>() == nullptr);
|
||||
|
||||
check_impl(container, 1, 0, 0, 0);
|
||||
}
|
||||
|
||||
test_case(ecs_attach_random)
|
||||
{
|
||||
random::generator rng;
|
||||
entity_container container;
|
||||
|
||||
int count0 = 0;
|
||||
int count1 = 0;
|
||||
int count2 = 0;
|
||||
int count12 = 0;
|
||||
|
||||
for (int i = 0; i < 1024 * 1024; ++i)
|
||||
{
|
||||
auto h = container.create();
|
||||
|
||||
bool has1 = random::uniform<bool>(rng);
|
||||
bool has2 = random::uniform<bool>(rng);
|
||||
|
||||
if (has1 && has2)
|
||||
{
|
||||
container.attach(h, component_1{i}, component_2{2 * i});
|
||||
++count12;
|
||||
}
|
||||
else if (has1 && !has2)
|
||||
{
|
||||
container.attach(h, component_1{i});
|
||||
++count1;
|
||||
}
|
||||
else if (!has1 && has2)
|
||||
{
|
||||
container.attach(h, component_2{2 * i});
|
||||
++count2;
|
||||
}
|
||||
else
|
||||
{
|
||||
++count0;
|
||||
}
|
||||
}
|
||||
|
||||
check_impl(container, count0, count1, count2, count12);
|
||||
}
|
||||
|
||||
test_case(ecs_detach_random)
|
||||
{
|
||||
random::generator rng;
|
||||
entity_container container;
|
||||
|
||||
int count0 = 0;
|
||||
int count1 = 0;
|
||||
int count2 = 0;
|
||||
int count12 = 0;
|
||||
|
||||
for (int i = 0; i < 1024 * 1024; ++i)
|
||||
{
|
||||
auto h = container.create(component_1{i}, component_2{2 * i});
|
||||
|
||||
bool has1 = random::uniform<bool>(rng);
|
||||
bool has2 = random::uniform<bool>(rng);
|
||||
|
||||
if (has1 && has2)
|
||||
{
|
||||
++count12;
|
||||
}
|
||||
else if (has1 && !has2)
|
||||
{
|
||||
container.detach<component_2>(h);
|
||||
++count1;
|
||||
}
|
||||
else if (!has1 && has2)
|
||||
{
|
||||
container.detach<component_1>(h);
|
||||
++count2;
|
||||
}
|
||||
else
|
||||
{
|
||||
container.detach<component_1, component_2>(h);
|
||||
++count0;
|
||||
}
|
||||
}
|
||||
|
||||
check_impl(container, count0, count1, count2, count12);
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue