Skip to content

Commit

Permalink
refactor(ecs): move add/remove to component views
Browse files Browse the repository at this point in the history
  • Loading branch information
RiscadoA committed Nov 1, 2023
1 parent ff92b96 commit b2acdc9
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 122 deletions.
159 changes: 50 additions & 109 deletions core/include/cubos/core/ecs/world.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,41 +67,22 @@ namespace cubos::core::ecs
template <typename T>
WriteResource<T> write() const;

/// @brief Creates a new entity with the given components.
/// @tparam ComponentTypes Types of the components.
/// @param components Initial values for the components.
template <typename... ComponentTypes>
Entity create(ComponentTypes... components);
/// @brief Creates a new entity.
/// @return Entity.
Entity create();

/// @brief Destroys an entity and its components.
/// @todo Whats the behavior when we pass an entity that has already been destroyed?
/// @brief Destroys an entity.
///
/// If an entity has already been destroyed, this function does nothing.
///
/// @param entity Entity identifier.
void destroy(Entity entity);

/// @brief Checks if an entity is still alive.
/// @param entity Entity identifier.
/// @param entity Entity.
/// @return Whether the entity is alive.
bool isAlive(Entity entity) const;

/// @brief Adds components to an entity.
///
/// If the entity already has one of the components, it is overwritten.
///
/// @tparam ComponentTypes Types of the components.
/// @param entity Entity identifier.
/// @param components Initial values for the components.
template <typename... ComponentTypes>
void add(Entity entity, ComponentTypes&&... components);

/// @brief Removes components from an entity.
///
/// If the entity doesn't have one of the components, nothing happens.
///
/// @tparam ComponentTypes Types of the components.
/// @param entity Entity identifier.
template <typename... ComponentTypes>
void remove(Entity entity);

/// @brief Creates a components view for the given entity.
///
/// The given @p entity must be @ref isAlive "alive".
Expand Down Expand Up @@ -203,6 +184,48 @@ namespace cubos::core::ecs
/// @return Iterator.
Iterator end();

/// @brief Adds a component to the entity.
///
/// If the entity already has the component, its value is overwritten.
///
/// @param type Component type.
/// @param value Component value to move.
/// @return Reference to this.
Components& add(const reflection::Type& type, void* value);

/// @brief Adds a component to the entity.
///
/// If the entity already has the component, its value is overwritten.
///
/// @tparam T Component type.
/// @param value Component value to move.
/// @return Reference to this.
template <reflection::Reflectable T>
Components& add(T&& value)
{
return this->add(reflection::reflect<T>(), &value);
}

/// @brief Removes a component from the entity.
///
/// If the entity doesn't have the component, nothing happens.
///
/// @param type Component type.
/// @return Reference to this.
Components& remove(const reflection::Type& type);

/// @brief Removes a component from the entity.
///
/// If the entity doesn't have the component, nothing happens.
///
/// @tparam T Component type.
/// @return Reference to this.
template <reflection::Reflectable T>
Components& remove()
{
return this->remove(reflection::reflect<T>());
}

private:
World& mWorld;
Entity mEntity;
Expand Down Expand Up @@ -384,86 +407,4 @@ namespace cubos::core::ecs
{
return mResourceManager.write<T>();
}

template <typename... ComponentTypes>
Entity World::create(ComponentTypes... components)
{
std::size_t ids[] = {
0, (mComponentManager
.getID<std::remove_const_t<std::remove_reference_t<std::remove_pointer_t<ComponentTypes>>>>())...};

Entity::Mask mask{};
for (auto id : ids)
{
mask.set(id);
}

auto entity = mEntityManager.create(mask);
([&](auto component) { mComponentManager.add(entity.index, std::move(component)); }(std::move(components)),
...);

#if CUBOS_LOG_LEVEL <= CUBOS_LOG_LEVEL_DEBUG
// Get the number of components being added.
constexpr std::size_t ComponentCount = sizeof...(ComponentTypes);

std::string componentNames[] = {"", "'" + std::string{getComponentName<ComponentTypes>().value()} + "'" ...};
CUBOS_DEBUG("Created entity {} with components {}", entity.index,
fmt::join(componentNames + 1, componentNames + ComponentCount + 1, ", "));
#endif
return entity;
}

template <typename... ComponentTypes>
void World::add(Entity entity, ComponentTypes&&... components)
{
if (!mEntityManager.isValid(entity))
{
CUBOS_ERROR("Entity {} doesn't exist!", entity.index);
return;
}

auto mask = mEntityManager.getMask(entity);

(
[&]() {
std::size_t componentId = mComponentManager.getID<ComponentTypes>();
mask.set(componentId);
mComponentManager.add(entity.index, std::move(components));
}(),
...);

mEntityManager.setMask(entity, mask);

#if CUBOS_LOG_LEVEL <= CUBOS_LOG_LEVEL_DEBUG
std::string componentNames[] = {"'" + std::string{getComponentName<ComponentTypes>().value()} + "'" ...};
CUBOS_DEBUG("Added components {} to entity {}", fmt::join(componentNames, ", "), entity.index);
#endif
}

template <typename... ComponentTypes>
void World::remove(Entity entity)
{
if (!mEntityManager.isValid(entity))
{
CUBOS_ERROR("Entity {} doesn't exist!", entity.index);
return;
}

auto mask = mEntityManager.getMask(entity);

(
[&]() {
std::size_t componentId = mComponentManager.getID<ComponentTypes>();
mask.set(componentId, false);
mComponentManager.remove<ComponentTypes>(entity.index);
}(),
...);

mEntityManager.setMask(entity, mask);

#if CUBOS_LOG_LEVEL <= CUBOS_LOG_LEVEL_DEBUG
std::string componentNames[] = {"'" + std::string{getComponentName<ComponentTypes>().value()} + "'" ...};
CUBOS_DEBUG("Removed components {} from entity {}", fmt::join(componentNames, ", "), entity.index);
#endif
}
} // namespace cubos::core::ecs
36 changes: 35 additions & 1 deletion core/src/cubos/core/ecs/world.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include <cubos/core/ecs/component/registry.hpp>
#include <cubos/core/ecs/world.hpp>
#include <cubos/core/log.hpp>
#include <cubos/core/reflection/type.hpp>

using namespace cubos::core;
using namespace cubos::core::ecs;
Expand All @@ -12,11 +13,20 @@ World::World(std::size_t initialCapacity)
// Edu: BAH!
}

Entity World::create()
{
Entity::Mask mask{};
mask.set(0);
auto entity = mEntityManager.create(mask);
CUBOS_DEBUG("Created entity {}#{}", entity.index, entity.generation);
return entity;
}

void World::destroy(Entity entity)
{
mEntityManager.destroy(entity);
mComponentManager.removeAll(entity.index);
CUBOS_DEBUG("Destroyed entity {}", entity.index);
CUBOS_DEBUG("Destroyed entity {}#{}", entity.index, entity.generation);
}

bool World::isAlive(Entity entity) const
Expand Down Expand Up @@ -133,6 +143,30 @@ auto World::Components::end() -> Iterator
return Iterator{*this, true};
}

auto World::Components::add(const reflection::Type& type, void* value) -> Components&
{
std::size_t componentId = mWorld.mComponentManager.getID(type);
auto mask = mWorld.mEntityManager.getMask(mEntity);
mask.set(componentId);
mWorld.mEntityManager.setMask(mEntity, mask);
mWorld.mComponentManager.add(mEntity.index, type, value);

CUBOS_DEBUG("Added component {} to entity {}#{}", type.name(), mEntity.index, mEntity.generation);
return *this;
}

auto World::Components::remove(const reflection::Type& type) -> Components&
{
std::size_t componentId = mWorld.mComponentManager.getID(type);
auto mask = mWorld.mEntityManager.getMask(mEntity);
mask.set(componentId, false);
mWorld.mEntityManager.setMask(mEntity, mask);
mWorld.mComponentManager.remove(mEntity.index, componentId);

CUBOS_DEBUG("Removed component {} from entity {}#{}", type.name(), mEntity.index, mEntity.generation);
return *this;
}

World::ConstComponents::ConstComponents(const World& world, Entity entity)
: mWorld{world}
, mEntity{entity}
Expand Down
16 changes: 11 additions & 5 deletions core/tests/ecs/query.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,17 @@ TEST_CASE("ecs::Query")

// Create a few entities.
auto empty = world.create();
auto int0 = world.create(IntegerComponent{0});
auto int1 = world.create(IntegerComponent{1}, ParentComponent{});
world.create(ParentComponent{});
world.create(IntegerComponent{2});
world.create(IntegerComponent{3}, ParentComponent{});
auto int0 = world.create();
auto int1 = world.create();
auto int2 = world.create();
auto int3 = world.create();
auto foo = world.create();

world.components(int0).add(IntegerComponent{0});
world.components(int1).add(IntegerComponent{1}).add(ParentComponent{});
world.components(int2).add(IntegerComponent{2});
world.components(int3).add(IntegerComponent{3}).add(ParentComponent{});
world.components(foo).add(ParentComponent{});

// Check if direct access to entities works.
CHECK(Query<>(world)[empty].has_value());
Expand Down
17 changes: 10 additions & 7 deletions core/tests/ecs/world.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,9 @@ TEST_CASE("ecs::World")
{
bool destroyed = false;

// Create an entity with an detect destructor component.
auto foo = world.create(DetectDestructorComponent{{&destroyed}});
// Create an entity with a detect destructor component.
auto foo = world.create();
world.components(foo).add(DetectDestructorComponent{{&destroyed}});
CHECK(world.components(foo).has<DetectDestructorComponent>());
CHECK_FALSE(world.components(foo).has<ParentComponent>());
CHECK_FALSE(destroyed);
Expand All @@ -69,13 +70,13 @@ TEST_CASE("ecs::World")
CHECK(constComponents.begin()->type->is<DetectDestructorComponent>());

// Add a parent component.
world.add(foo, ParentComponent{});
world.components(foo).add(ParentComponent{});
CHECK(world.components(foo).has<DetectDestructorComponent>());
CHECK(world.components(foo).has<ParentComponent>());
CHECK_FALSE(destroyed);

// Remove the detect destructor component.
world.remove<DetectDestructorComponent>(foo);
world.components(foo).remove<DetectDestructorComponent>();
CHECK_FALSE(world.components(foo).has<DetectDestructorComponent>());
CHECK(constWorld.components(foo).has<ParentComponent>());
CHECK(destroyed);
Expand Down Expand Up @@ -115,7 +116,8 @@ TEST_CASE("ecs::World")
{
// Create an entity which has an integer component and a parent component.
auto bar = world.create();
auto foo = world.create(IntegerComponent{0}, ParentComponent{bar});
auto foo = world.create();
world.components(foo).add(IntegerComponent{0}).add(ParentComponent{bar});
CHECK(world.components(foo).has<IntegerComponent>());
CHECK(world.components(foo).has<ParentComponent>());

Expand All @@ -139,7 +141,7 @@ TEST_CASE("ecs::World")

// Create an entity and add a detect destructor component.
auto foo = world.create();
world.add(foo, DetectDestructorComponent{{&destroyed}});
world.components(foo).add(DetectDestructorComponent{{&destroyed}});
CHECK_FALSE(destroyed);

// Destroy the entity.
Expand All @@ -155,7 +157,8 @@ TEST_CASE("ecs::World")
{
World world{};
setupWorld(world);
world.create(DetectDestructorComponent{{&destroyed}});
auto foo = world.create();
world.components(foo).add(DetectDestructorComponent{{&destroyed}});
CHECK_FALSE(destroyed);
}

Expand Down

0 comments on commit b2acdc9

Please sign in to comment.