diff --git a/CMakeLists.txt b/CMakeLists.txt index f1f860c8..142d675a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,6 +77,7 @@ add_executable(unit_tests EXCLUDE_FROM_ALL tests/CallbackTest.cpp tests/CollisionTest.cpp tests/MoveTests.cpp + tests/ViewTest.cpp ) target_include_directories(unit_tests PUBLIC sources) target_link_libraries(unit_tests PUBLIC wal ray) diff --git a/lib/wal/CMakeLists.txt b/lib/wal/CMakeLists.txt index ba2a60a0..e8cd8655 100644 --- a/lib/wal/CMakeLists.txt +++ b/lib/wal/CMakeLists.txt @@ -17,6 +17,6 @@ add_library(wal sources/Component/Component.cpp sources/System/System.cpp sources/Models/Callback.hpp - sources/View/View.hpp) +) target_include_directories(wal PUBLIC sources) \ No newline at end of file diff --git a/lib/wal/sources/Entity/Entity.cpp b/lib/wal/sources/Entity/Entity.cpp index 1dd1a1b3..90e9a41a 100644 --- a/lib/wal/sources/Entity/Entity.cpp +++ b/lib/wal/sources/Entity/Entity.cpp @@ -10,13 +10,15 @@ namespace WAL { unsigned Entity::nextID = 0; - Entity::Entity(std::string name) + Entity::Entity(Scene &scene, std::string name) : _uid(Entity::nextID++), + _scene(scene), _name(std::move(name)) { } Entity::Entity(const Entity &other) : _uid(Entity::nextID++), + _scene(other._scene), _name(other._name), _disabled(other._disabled) { @@ -50,6 +52,7 @@ namespace WAL if (this->hasComponent(type)) throw DuplicateError("A component of the type \"" + std::string(type.name()) + "\" already exists."); this->_components.emplace(type, component.clone(*this)); + this->_scene._componentAdded(*this, type); return *this; } diff --git a/lib/wal/sources/Entity/Entity.hpp b/lib/wal/sources/Entity/Entity.hpp index e9fa672e..60bd0090 100644 --- a/lib/wal/sources/Entity/Entity.hpp +++ b/lib/wal/sources/Entity/Entity.hpp @@ -10,9 +10,23 @@ #include #include "Component/Component.hpp" #include "Exception/WalError.hpp" +#include "Wal.hpp" namespace WAL { + + class Scene { + public: + //! @brief Notify this scene that a component has been added to the given entity. + //! @param entity The entity with the new component + //! @param type The type of the component added. + void _componentAdded(const Entity &entity, std::type_index type); + //! @brief Notify this scene that a component has been removed to the given entity. + //! @param entity The entity with the removed component + //! @param type The type of the component removed. namespace WAL + void _componentRemoved(const Entity &entity, std::type_index type); + }; + //! @brief An entity of the WAL's ECS. class Entity { @@ -28,6 +42,9 @@ namespace WAL //! @brief This ID will be the one of the next entity created. static unsigned nextID; + protected: + //! @brief A reference to the ECS. + Scene &_scene; public: //! @brief Get the ID of the entity. unsigned getUid() const; @@ -79,6 +96,7 @@ namespace WAL if (this->hasComponent(type)) throw DuplicateError("A component of the type \"" + std::string(type.name()) + "\" already exists."); this->_components[type] = std::make_unique(*this, std::forward(params)...); + this->_scene._componentAdded(*this, type); return *this; } @@ -97,11 +115,12 @@ namespace WAL if (existing == this->_components.end()) throw NotFoundError("No component could be found with the type \"" + std::string(type.name()) + "\"."); this->_components.erase(existing); + this->_scene._componentRemoved(*this, type); return *this; } //! @brief A default constructor - explicit Entity(std::string name); + explicit Entity(Scene &wal, std::string name); //! @brief An entity is copyable Entity(const Entity &); //! @brief An entity is movable. diff --git a/lib/wal/sources/Scene/Scene.cpp b/lib/wal/sources/Scene/Scene.cpp index 3c07f3c8..930382f7 100644 --- a/lib/wal/sources/Scene/Scene.cpp +++ b/lib/wal/sources/Scene/Scene.cpp @@ -10,8 +10,24 @@ namespace WAL { return this->_entities; } + Scene &Scene::operator=(const Scene &) { return *this; } + + Entity &Scene::addEntity(const std::string &name) + { + return this->_entities.emplace_back(*this, name); + } + + void Scene::_componentAdded(const Entity &entity, std::type_index type) + { + + } + + void Scene::_componentRemoved(const Entity &entity, std::type_index type) + { + + } } // namespace WAL \ No newline at end of file diff --git a/lib/wal/sources/Scene/Scene.hpp b/lib/wal/sources/Scene/Scene.hpp index 840d2ec5..4ccf9316 100644 --- a/lib/wal/sources/Scene/Scene.hpp +++ b/lib/wal/sources/Scene/Scene.hpp @@ -3,11 +3,11 @@ // -#pragma once +#ifndef WAL_SCENE +#define WAL_SCENE #include #include -#include "View/View.hpp" #include "Entity/Entity.hpp" namespace WAL @@ -18,20 +18,44 @@ namespace WAL private: //! @brief The list of registered entities std::vector _entities = {}; - //! @brief A list of cached views. -// std::vector _views = {}; + + //! @brief Notify this scene that a component has been added to the given entity. + //! @param entity The entity with the new component + //! @param type The type of the component added. + void _componentAdded(const Entity &entity, std::type_index type); + //! @brief Notify this scene that a component has been removed to the given entity. + //! @param entity The entity with the removed component + //! @param type The type of the component removed. + void _componentRemoved(const Entity &entity, std::type_index type); public: //! @brief Get the list of entities. std::vector &getEntities(); - //! @brief Add a new entity to the scene, you can use this method with the same arguments as the entity's constructor. - //! @return The current scene is returned to allow you to chain call. - template - Entity &addEntity(Params &&...params) + //! @brief Add a new entity to the scene. + //! @param name The name of the created entity. + //! @return The created entity is returned. + Entity &addEntity(const std::string &name); + + template + std::vector> &view() { - return this->_entities.emplace_back(std::forward(params)...); + return this->view(typeid(Components)...); } +#pragma clang diagnostic push +#pragma ide diagnostic ignored "NotImplementedFunctions" + template + std::vector> &view(const Components &...index) requires(std::is_same_v) + { + static std::vector> view; + + std::copy_if(this->_entities.begin(), this->_entities.end(), std::back_inserter(view), [&index...](Entity &entity) { + return (entity.hasComponent(index) && ...); + }); + return view; + } +#pragma clang diagnostic pop + //! @brief A default constructor Scene() = default; //! @brief A scene is copy constructable @@ -41,5 +65,11 @@ namespace WAL //! @brief A scene is assignable Scene &operator=(const Scene &); Scene(Scene &&) = default; + + friend Entity; }; -} // namespace WAL \ No newline at end of file +} // namespace WAL + +#else + +#endif \ No newline at end of file diff --git a/lib/wal/sources/System/System.hpp b/lib/wal/sources/System/System.hpp index 59c82c4a..c3e5ee83 100644 --- a/lib/wal/sources/System/System.hpp +++ b/lib/wal/sources/System/System.hpp @@ -6,10 +6,13 @@ #include #include -#include "Entity/Entity.hpp" +#include +#include namespace WAL { + class Entity; + //! @brief A base system of WAL class System { diff --git a/lib/wal/sources/View/View.hpp b/lib/wal/sources/View/View.hpp deleted file mode 100644 index 9f004188..00000000 --- a/lib/wal/sources/View/View.hpp +++ /dev/null @@ -1,35 +0,0 @@ -// -// Created by Zoe Roux on 2021-06-02. -// - - -#pragma once - -#include -#include - -namespace WAL -{ - //! @brief A view caching entities containing requested components - template - class View - { - //! @brief A list of reference to entities that contains the - std::vector> entities; - - explicit View(std::vector &entities) - : entities() - { - std::copy_if(entities.begin(), entities.end(), std::back_inserter(this->entities), [](Entity &entity) { - return (entity.hasComponent() && ...); - }); - } - - //! @brief A default copy constructor. - View(const View &) = default; - //! @brief A default destructor. - ~View() = default; - //! @brief A View is assignable. - View &operator=(const View &) = default; - }; -} \ No newline at end of file diff --git a/lib/wal/sources/Wal.cpp b/lib/wal/sources/Wal.cpp index 875e3bd8..d4262b73 100644 --- a/lib/wal/sources/Wal.cpp +++ b/lib/wal/sources/Wal.cpp @@ -5,6 +5,7 @@ #include #include #include "Wal.hpp" +#include "Scene/Scene.hpp" namespace WAL { diff --git a/lib/wal/sources/Wal.hpp b/lib/wal/sources/Wal.hpp index 50a87188..a16e4f5c 100644 --- a/lib/wal/sources/Wal.hpp +++ b/lib/wal/sources/Wal.hpp @@ -10,13 +10,14 @@ #include #include #include "Exception/WalError.hpp" -#include "Scene/Scene.hpp" -#include "Entity/Entity.hpp" #include "System/System.hpp" #include "Models/Callback.hpp" namespace WAL { + class Entity; + class Scene; + //! @brief The main WAL class, it is used to setup and run the ECS. class Wal { diff --git a/sources/Runner/Runner.cpp b/sources/Runner/Runner.cpp index db3896c9..7e03bfef 100644 --- a/sources/Runner/Runner.cpp +++ b/sources/Runner/Runner.cpp @@ -64,9 +64,9 @@ namespace BBM wal.addSystem(window); } - std::shared_ptr loadGameScene() + std::shared_ptr loadGameScene(WAL::Wal &wal) { - auto scene = std::make_shared(); + auto scene = std::make_shared(wal); scene->addEntity("player") .addComponent() .addComponent>("assets/player/player.iqm", std::make_pair(MAP_DIFFUSE, "assets/player/blue.png")) @@ -90,7 +90,7 @@ namespace BBM scene->addEntity("camera") .addComponent(8, 20, 7) .addComponent(Vector3f(8, 0, 8)); - std::srand(std::time(NULL)); + std::srand(std::time(nullptr)); MapGenerator::loadMap(16, 16, MapGenerator::createMap(16, 16), scene); return scene; } @@ -100,7 +100,7 @@ namespace BBM WAL::Wal wal; addSystems(wal); enableRaylib(wal); - wal.scene = loadGameScene(); + wal.scene = loadGameScene(wal); try { wal.run(updateState); diff --git a/tests/CallbackTest.cpp b/tests/CallbackTest.cpp index 534ca9f4..44b3a103 100644 --- a/tests/CallbackTest.cpp +++ b/tests/CallbackTest.cpp @@ -4,6 +4,7 @@ #include #include +#include #include "Entity/Entity.hpp" #include "Models/Callback.hpp" @@ -36,6 +37,7 @@ TEST_CASE("Callback multiple arguments", "[Callback]") callback.addCallback([](const std::string& str, int a, unsigned *value, Entity &entity) { throw std::runtime_error(""); }); - Entity entity("name"); + Wal wal; + Entity entity(wal, "name"); REQUIRE_THROWS_AS(callback("1", 0, nullptr, entity), std::runtime_error); } diff --git a/tests/CollisionTest.cpp b/tests/CollisionTest.cpp index 69c75207..42b7d186 100644 --- a/tests/CollisionTest.cpp +++ b/tests/CollisionTest.cpp @@ -21,7 +21,7 @@ TEST_CASE("Collision test", "[Component][System]") { Wal wal; CollisionSystem collision(wal); - wal.scene = std::shared_ptr(new Scene); + wal.scene = std::make_shared(wal); wal.scene->addEntity("player") .addComponent() .addComponent([](Entity &actual, const Entity &) { @@ -65,7 +65,7 @@ TEST_CASE("Collsion test with movable", "[Component][System]") Wal wal; CollisionSystem collision(wal); MovableSystem movable; - wal.scene = std::shared_ptr(new Scene); + wal.scene = std::make_shared(wal); wal.scene->addEntity("player") .addComponent() .addComponent([](Entity &actual, const Entity &) {}, [](Entity &actual, const Entity &) {}, 5.0) diff --git a/tests/EntityTests.cpp b/tests/EntityTests.cpp index f58fd0c1..7a0a3490 100644 --- a/tests/EntityTests.cpp +++ b/tests/EntityTests.cpp @@ -5,13 +5,15 @@ #include "Entity/Entity.hpp" #include "Component/Position/PositionComponent.hpp" #include +#include using namespace WAL; using namespace BBM; TEST_CASE("Component", "[Entity]") { - Entity entity("Bob"); + Wal wal; + Entity entity(wal, "Bob"); entity.addComponent(2, 3, 4); SECTION("Check value") { @@ -31,13 +33,15 @@ TEST_CASE("Component", "[Entity]") TEST_CASE("ComponentNotFound", "[Entity]") { - Entity entity("Bob"); + Wal wal; + Entity entity(wal, "Bob"); REQUIRE_THROWS_AS(entity.getComponent(), NotFoundError); } TEST_CASE("Add component by reference", "[Entity]") { - Entity entity("Bob"); + Wal wal; + Entity entity(wal, "Bob"); PositionComponent component(entity, 4, 5, 6); REQUIRE(&entity.addComponent(component) == &entity); diff --git a/tests/MoveTests.cpp b/tests/MoveTests.cpp index cd37d22a..0a47bd10 100644 --- a/tests/MoveTests.cpp +++ b/tests/MoveTests.cpp @@ -19,7 +19,8 @@ using namespace BBM; TEST_CASE("Move test", "[Component][System]") { - Scene scene; + Wal wal; + Scene scene(wal); scene.addEntity("player") .addComponent() .addComponent() diff --git a/tests/ViewTest.cpp b/tests/ViewTest.cpp new file mode 100644 index 00000000..90328acd --- /dev/null +++ b/tests/ViewTest.cpp @@ -0,0 +1,33 @@ +// +// Created by Zoe Roux on 6/3/21. +// + +#include "Entity/Entity.hpp" +#include "Component/Position/PositionComponent.hpp" +#include "System/Movable/MovableSystem.hpp" +#include "System/Controllable/ControllableSystem.hpp" +#include +#include +#include + +#define private public +#include + +using namespace WAL; +using namespace BBM; + +TEST_CASE("View creation", "[View]") +{ + Wal wal; + Scene scene(wal); + scene.addEntity("player") + .addComponent() + .addComponent(); + scene.addEntity("Box") + .addComponent(); + REQUIRE(scene.view().size() == 2); + REQUIRE(scene.view().size() == 1); + Entity &entity = *scene.getEntities().begin(); + Entity &firstView = *scene.view().begin(); + REQUIRE(&entity == &firstView); +} \ No newline at end of file