fix(ecs): sparse_set not properly removing elements
This commit is contained in:
parent
1b6d53f1c1
commit
04c2e59ada
3 changed files with 60 additions and 10 deletions
|
@ -76,8 +76,33 @@ Suite element_raii = [] {
|
|||
set.remove(idx);
|
||||
|
||||
expect_eq(set.get_size(), 10'000 - (idx + 1));
|
||||
expect_throw([&] { std::ignore = set.at(idx); });
|
||||
expect_false(set.contains(idx));
|
||||
expect_throw([&] { std::ignore = set.at(idx); });
|
||||
}
|
||||
};
|
||||
|
||||
Case { "removed elements won't be iterated again" } = [] {
|
||||
auto set = Set {};
|
||||
|
||||
for (auto idx : std::views::iota(0, 10'000))
|
||||
{
|
||||
set.insert(idx, idx);
|
||||
}
|
||||
|
||||
set.remove(0);
|
||||
set.remove(32);
|
||||
set.remove(69);
|
||||
set.remove(420);
|
||||
set.remove(9'999);
|
||||
|
||||
for (auto &[identifier, value] : set)
|
||||
{
|
||||
expect_eq(identifier, value);
|
||||
expect_ne(value, 0);
|
||||
expect_ne(value, 32);
|
||||
expect_ne(value, 69);
|
||||
expect_ne(value, 420);
|
||||
expect_ne(value, 9'999);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -8,9 +8,16 @@ using Entity = uint32_t;
|
|||
|
||||
/** A registry of components, the heart of an ECS architecture.
|
||||
*
|
||||
* @todo(Light): optimize multi-component views
|
||||
* @todo(Light): support more than 2-component views
|
||||
* @todo(Light): handle edge cases or specify the undefined behaviors
|
||||
* @todo(Light): Implement grouping
|
||||
* @todo(Light): Implement identifier recycling
|
||||
* @todo(Light): Optimize views/each
|
||||
* @todo(Light): Support >2 component views
|
||||
* @todo(Light): Handle more edge cases or specify the undefined behaviors
|
||||
*
|
||||
* @ref https://skypjack.github.io/
|
||||
* @ref https://github.com/alecthomas/entityx
|
||||
* @ref https://github.com/skypjack/entt
|
||||
* @ref https://github.com/SanderMertens/flecs
|
||||
*/
|
||||
class Registry
|
||||
{
|
||||
|
@ -93,7 +100,7 @@ public:
|
|||
}
|
||||
|
||||
template<typename Component_T>
|
||||
auto view() -> SparseSet<Component_T, Entity>&
|
||||
auto view() -> SparseSet<Component_T, Entity> &
|
||||
{
|
||||
return get_derived_set<Component_T>();
|
||||
};
|
||||
|
|
|
@ -25,10 +25,6 @@ public:
|
|||
virtual void remove(Identifier_T identifier) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @todo(Light): implement identifier recycling.
|
||||
*/
|
||||
template<typename Value_T, typename Identifier_T = uint32_t>
|
||||
class SparseSet: public TypeErasedSparseSet<Identifier_T>
|
||||
{
|
||||
|
@ -77,7 +73,29 @@ public:
|
|||
auto &idx = m_sparse[identifier];
|
||||
auto &[entity, component] = m_dense[idx];
|
||||
|
||||
idx = null_identifier;
|
||||
auto &[last_entity, last_component] = m_dense.back();
|
||||
auto &last_idx = m_sparse[last_entity];
|
||||
|
||||
// removed entity is in dense's back, just pop and invalidate sparse[identifier]
|
||||
if (entity == last_entity)
|
||||
{
|
||||
idx = null_identifier;
|
||||
m_dense.pop_back();
|
||||
}
|
||||
else
|
||||
{
|
||||
// swap dense's 'back' to 'removed'
|
||||
std::swap(component, last_component);
|
||||
entity = last_entity;
|
||||
|
||||
// make sparse to point to new idx
|
||||
last_idx = idx;
|
||||
|
||||
// pop dense and invalidate sparse[identifier]
|
||||
idx = null_identifier;
|
||||
m_dense.pop_back();
|
||||
}
|
||||
|
||||
++m_dead_count;
|
||||
--m_alive_count;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue