diff --git a/core/include/cubos/core/ecs/world.hpp b/core/include/cubos/core/ecs/world.hpp index 4ae1c3826..558752c0f 100644 --- a/core/include/cubos/core/ecs/world.hpp +++ b/core/include/cubos/core/ecs/world.hpp @@ -67,41 +67,22 @@ namespace cubos::core::ecs template WriteResource 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 - 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 - 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 - void remove(Entity entity); - /// @brief Creates a components view for the given entity. /// /// The given @p entity must be @ref isAlive "alive". @@ -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 + Components& add(T&& value) + { + return this->add(reflection::reflect(), &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 + Components& remove() + { + return this->remove(reflection::reflect()); + } + private: World& mWorld; Entity mEntity; @@ -384,86 +407,4 @@ namespace cubos::core::ecs { return mResourceManager.write(); } - - template - Entity World::create(ComponentTypes... components) - { - std::size_t ids[] = { - 0, (mComponentManager - .getID>>>())...}; - - 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().value()} + "'" ...}; - CUBOS_DEBUG("Created entity {} with components {}", entity.index, - fmt::join(componentNames + 1, componentNames + ComponentCount + 1, ", ")); -#endif - return entity; - } - - template - 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(); - 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().value()} + "'" ...}; - CUBOS_DEBUG("Added components {} to entity {}", fmt::join(componentNames, ", "), entity.index); -#endif - } - - template - 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(); - mask.set(componentId, false); - mComponentManager.remove(entity.index); - }(), - ...); - - mEntityManager.setMask(entity, mask); - -#if CUBOS_LOG_LEVEL <= CUBOS_LOG_LEVEL_DEBUG - std::string componentNames[] = {"'" + std::string{getComponentName().value()} + "'" ...}; - CUBOS_DEBUG("Removed components {} from entity {}", fmt::join(componentNames, ", "), entity.index); -#endif - } } // namespace cubos::core::ecs diff --git a/core/src/cubos/core/ecs/world.cpp b/core/src/cubos/core/ecs/world.cpp index ca14f69a5..9a1702459 100644 --- a/core/src/cubos/core/ecs/world.cpp +++ b/core/src/cubos/core/ecs/world.cpp @@ -1,6 +1,7 @@ #include #include #include +#include using namespace cubos::core; using namespace cubos::core::ecs; @@ -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 @@ -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} diff --git a/core/tests/ecs/query.cpp b/core/tests/ecs/query.cpp index 0c54d5688..5f35b1c55 100644 --- a/core/tests/ecs/query.cpp +++ b/core/tests/ecs/query.cpp @@ -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()); diff --git a/core/tests/ecs/world.cpp b/core/tests/ecs/world.cpp index 52bcedaaa..f92162053 100644 --- a/core/tests/ecs/world.cpp +++ b/core/tests/ecs/world.cpp @@ -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()); CHECK_FALSE(world.components(foo).has()); CHECK_FALSE(destroyed); @@ -69,13 +70,13 @@ TEST_CASE("ecs::World") CHECK(constComponents.begin()->type->is()); // Add a parent component. - world.add(foo, ParentComponent{}); + world.components(foo).add(ParentComponent{}); CHECK(world.components(foo).has()); CHECK(world.components(foo).has()); CHECK_FALSE(destroyed); // Remove the detect destructor component. - world.remove(foo); + world.components(foo).remove(); CHECK_FALSE(world.components(foo).has()); CHECK(constWorld.components(foo).has()); CHECK(destroyed); @@ -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()); CHECK(world.components(foo).has()); @@ -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. @@ -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); }