Merge pull request #51 from AnonymusRaccoon/ecs

Updating dependencies management, adding a callback
This commit is contained in:
Clément Le Bihan
2021-05-22 18:20:59 +02:00
committed by GitHub
21 changed files with 181 additions and 144 deletions
+3
View File
@@ -23,6 +23,9 @@ jobs:
if: matrix.name == 'Linux'
run: |
sudo apt install -y libasound2-dev mesa-common-dev libx11-dev libxrandr-dev libxi-dev xorg-dev libgl1-mesa-dev libglu1-mesa-dev
- name: Update G++
if: matrix.name == 'Linux'
run: sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-10 100
- name: Build
run: |
mkdir build && cd build
+2
View File
@@ -13,6 +13,8 @@ jobs:
- name: Install Xorg lib
run: |
sudo apt install -y libasound2-dev mesa-common-dev libx11-dev libxrandr-dev libxi-dev xorg-dev libgl1-mesa-dev libglu1-mesa-dev
- name: Update G++
run: sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-10 100
- name: Build
run: |
mkdir build && cd build
+2 -4
View File
@@ -9,12 +9,8 @@ add_library(wal
sources/System/System.hpp
sources/Wal.cpp
sources/Wal.hpp
sources/Scene/SceneManager.cpp
sources/Scene/SceneManager.hpp
sources/Scene/Scene.cpp
sources/Scene/Scene.hpp
sources/Events/EventManager.cpp
sources/Events/EventManager.hpp
sources/Exception/WalError.cpp
sources/Exception/WalError.hpp
sources/Entity/Entity.cpp
@@ -27,6 +23,7 @@ add_library(wal
sources/System/Movable/MovableSystem.cpp
sources/System/Movable/MovableSystem.hpp
sources/System/System.cpp
sources/Models/Callback.hpp
)
target_include_directories(wal PUBLIC sources)
@@ -35,6 +32,7 @@ add_executable(wal_tests EXCLUDE_FROM_ALL
tests/EntityTests.cpp
tests/MainTest.cpp
tests/EngineTests.cpp
tests/CallbackTest.cpp
)
target_link_libraries(wal_tests PRIVATE wal)
-5
View File
@@ -20,11 +20,6 @@ namespace WAL
this->_disabled = disabled;
}
const std::vector<std::type_index> &Component::getDependencies() const
{
return this->_dependencies;
}
void Component::onStart()
{
//TODO handle events here
-5
View File
@@ -22,8 +22,6 @@ namespace WAL
protected:
//! @brief The entity that own this component
Entity &_entity;
//! @brief The list of dependencies of this component.
std::vector<std::type_index> _dependencies;
//! @brief A component can't be instantiated, it should be derived.
explicit Component(Entity &entity);
@@ -44,9 +42,6 @@ namespace WAL
//! @brief Disable this component.
void setDisable(bool disabled);
//! @brief Get the dependencies of this component.
const std::vector<std::type_index> &getDependencies() const;
//! @brief The entity or this component has just been enabled.
virtual void onStart();
+8
View File
@@ -59,4 +59,12 @@ namespace WAL
});
return existing != this->_components.end();
}
bool Entity::hasComponent(const std::type_index &type) const
{
auto existing = std::find_if(this->_components.begin(), this->_components.end(), [&type] (const auto &cmp) {
return std::type_index(typeid(*cmp)) == type;
});
return existing != this->_components.end();
}
}
+4
View File
@@ -67,6 +67,10 @@ namespace WAL
//! @param type The type of the component
bool hasComponent(const std::type_info &type) const;
//! @brief Check if this entity has a component.
//! @param type The type of the component
bool hasComponent(const std::type_index &type) const;
//! @brief Add a component to this entity. The component is constructed in place.
//! @throw DuplicateError is thrown if a component with the same type already exist.
//! @return This entity is returned
-5
View File
@@ -1,5 +0,0 @@
//
// Created by Zoe Roux on 2021-05-14.
//
#include "EventManager.hpp"
-15
View File
@@ -1,15 +0,0 @@
//
// Created by Zoe Roux on 2021-05-14.
//
#pragma once
namespace WAL
{
//! @brief A class to handle events.
class EventManager
{
};
}
+54
View File
@@ -0,0 +1,54 @@
//
// Created by Zoe Roux on 5/21/21.
//
#pragma once
#include <functional>
namespace WAL
{
//! @brief A callback where you can subscribe to and emit it.
template<typename ...Types>
class Callback
{
private:
int _nextID = 0;
//! @brief The list of functions to call.
std::unordered_map<int, std::function<void (Types...)>> _functions = {};
public:
//! @brief Add a method to be called when this callback is invoked.
//! @param callback The list of arguments of the callback method
//! @return A unique ID for this callback. That can be used to remove the callback later.
int addCallback(std::function<void (Types...)> callback)
{
int id = this->_nextID++;
this->_functions[id] = std::move(callback);
return id;
}
//! @brief Remove a function from this callback.
//! @param id The ID of the function.
void removeCallback(int id)
{
this->_functions.erase(id);
}
void operator()(Types ...args) const
{
for (const auto &[_, callback] : this->_functions)
callback(args...);
}
//! @brief A default constructor
Callback() = default;
//! @brief A default copy constructor
Callback(const Callback &) = default;
//! @brief A default destructor
~Callback() = default;
//! @brief A default assignment operator
Callback &operator=(const Callback &) = default;
};
}
+2 -2
View File
@@ -23,12 +23,12 @@ namespace WAL
T z;
//! @brief Create a new nil vector3.
Vector3<T>()
Vector3()
: x(0), y(0), z(0)
{}
//! @brief Create a new vector3 representing a specific coordinate.
Vector3<T>(T x, T y, T z)
Vector3(T x, T y, T z)
: x(x), y(y), z(z)
{}
+20 -1
View File
@@ -6,6 +6,7 @@
#pragma once
#include <vector>
#include <functional>
#include "Entity/Entity.hpp"
namespace WAL
@@ -15,9 +16,27 @@ namespace WAL
{
private:
//! @brief The list of registered entities
std::vector<Entity> _entities;
std::vector<Entity> _entities = {};
public:
//! @brief Get the list of entities.
std::vector<Entity> &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 <class ...Params>
Scene &addEntity(Params ...params)
{
this->_entities.emplace_back(params...);
return *this;
}
//! @brief A default constructor
Scene() = default;
//! @brief A scene is copy constructable
Scene(const Scene &) = default;
//! @brief A default destructor
~Scene() = default;
//! @brief A scene is assignable
Scene &operator=(const Scene &) = default;
};
}
-33
View File
@@ -1,33 +0,0 @@
//
// Created by Zoe Roux on 2021-05-14.
//
#include "SceneManager.hpp"
namespace WAL
{
SceneManager &WAL::SceneManager::addScene(WAL::Scene &&scene)
{
this->_scenes.push_front(scene);
return *this;
}
SceneManager &SceneManager::addBackScene(Scene &&scene)
{
this->_scenes.insert(++this->_scenes.begin(), scene);
return *this;
}
Scene &SceneManager::getCurrent()
{
if (this->_scenes.empty())
throw NotFoundError("No scene exists.");
return this->_scenes.front();
}
SceneManager &SceneManager::closeCurrent()
{
this->_scenes.pop_front();
return *this;
}
}
-43
View File
@@ -1,43 +0,0 @@
//
// Created by Zoe Roux on 2021-05-14.
//
#pragma once
#include <queue>
#include "Scene/Scene.hpp"
namespace WAL
{
//! @brief A class to manage scenes
class SceneManager
{
private:
std::deque<Scene> _scenes = {};
public:
//! @brief Add a scene to the container and move to it.
//! @return The manager instance used to call this function is returned. This allow method chaining.
SceneManager &addScene(Scene &&scene);
//! @brief Add a scene before the current scene. This could be useful for lobbies or scene where the next scene can be constructed.
//! @return The manager instance used to call this function is returned. This allow method chaining.
SceneManager &addBackScene(Scene &&scene);
//! @breif Get the current scene
Scene &getCurrent();
//! @brief Remove the current scene and switch to the previous scene on the stack.
//! @return The manager instance used to call this function is returned. This allow method chaining.
SceneManager &closeCurrent();
//! @brief A default constructor
SceneManager() = default;
//! @brief A scene manager is copy constructable
SceneManager(const SceneManager &) = default;
//! @brief A default destructor.
~SceneManager() = default;
//! @brief A scene manager is assignable
SceneManager &operator=(const SceneManager &) = default;
};
}
@@ -9,10 +9,12 @@
namespace WAL
{
const std::type_info &MovableSystem::getComponent() const
{
return typeid(MovableComponent);
}
MovableSystem::MovableSystem()
: System({
typeid(MovableComponent),
typeid(PositionComponent)
})
{}
void MovableSystem::onFixedUpdate(Entity &entity)
{
@@ -13,13 +13,11 @@ namespace WAL
class MovableSystem : public System
{
public:
//! @inherit
const std::type_info &getComponent() const override;
//! @inherit
void onFixedUpdate(Entity &entity) override;
//! @brief A default constructor
MovableSystem() = default;
MovableSystem();
//! @brief A movable system is copy constructable
MovableSystem(const MovableSystem &) = default;
//! @brief A default destructor
+10
View File
@@ -4,8 +4,13 @@
#include "System.hpp"
#include <utility>
namespace WAL
{
System::System(std::vector<std::type_index> dependencies)
: _dependencies(std::move(dependencies))
{}
void System::onUpdate(Entity &entity, std::chrono::nanoseconds dtime)
{}
@@ -15,4 +20,9 @@ namespace WAL
void System::onSelfUpdate()
{}
const std::vector<std::type_index> &System::getDependencies() const
{
return this->_dependencies;
}
}
+6 -3
View File
@@ -12,6 +12,9 @@ namespace WAL
//! @brief A base system of WAL
class System
{
private:
//! @brief The list of dependencies of this system
std::vector<std::type_index> _dependencies = {};
public:
//! @brief A virtual, default, destructor
virtual ~System() = default;
@@ -19,8 +22,8 @@ namespace WAL
System(System &&) = default;
//! @brief Get the name of the component corresponding to this system.
virtual const std::type_info &getComponent() const = 0;
const std::vector<std::type_index> &getDependencies() const;
//! @brief Update the corresponding component of the given entity
//! @param entity The entity to update.
//! @param dtime The delta time.
@@ -35,7 +38,7 @@ namespace WAL
virtual void onSelfUpdate();
protected:
//! @brief A system can't be instantiated, it should be derived.
System() = default;
explicit System(std::vector<std::type_index> dependencies);
//! @brief A system can't be instantiated, it should be derived.
System(const System &) = default;
//! @brief A system can't be instantiated, it should be derived.
+14 -13
View File
@@ -3,6 +3,7 @@
//
#include <chrono>
#include <algorithm>
#include "Wal.hpp"
using namespace std::chrono_literals;
@@ -11,11 +12,6 @@ namespace WAL
{
std::chrono::nanoseconds Wal::timestep = 8ms;
SceneManager &Wal::getSceneManager()
{
return this->_sceneManager;
}
void Wal::run()
{
auto lastTick = std::chrono::steady_clock::now();
@@ -37,14 +33,12 @@ namespace WAL
void Wal::_update(std::chrono::nanoseconds dtime)
{
auto &entities = this->_sceneManager.getCurrent().getEntities();
auto &entities = this->_scene.getEntities();
for (auto &system : this->_systems) {
for (auto &entity : entities) {
const auto &cmp = system->getComponent();
if (!entity.hasComponent(cmp))
if (!Wal::_hasDependencies(entity, *system))
continue;
// TODO handle dependencies.
system->onUpdate(entity, dtime);
}
system->onSelfUpdate();
@@ -53,16 +47,23 @@ namespace WAL
void Wal::_fixedUpdate()
{
auto &entities = this->_sceneManager.getCurrent().getEntities();
auto &entities = this->_scene.getEntities();
for (auto &system : this->_systems) {
for (auto &entity : entities) {
auto &cmp = system->getComponent();
if (!entity.hasComponent(cmp))
if (!Wal::_hasDependencies(entity, *system))
continue;
// TODO handle dependencies.
system->onFixedUpdate(entity);
}
}
}
bool Wal::_hasDependencies(const Entity &entity, const System &system)
{
// TODO use an hashmap to cache results.
const auto &dependency = system.getDependencies();
return std::ranges::all_of(dependency.begin(), dependency.end(), [&entity](const auto &dependency) {
return entity.hasComponent(dependency);
});
}
}
+8 -8
View File
@@ -9,8 +9,7 @@
#include <memory>
#include <typeinfo>
#include <Exception/WalError.hpp>
#include "Events/EventManager.hpp"
#include "Scene/SceneManager.hpp"
#include "Scene/Scene.hpp"
#include "Entity/Entity.hpp"
#include "System/System.hpp"
@@ -21,9 +20,7 @@ namespace WAL
{
private:
//! @brief The scene manager that allow multiple scene to work together.
SceneManager _sceneManager;
//! @brief The event manager
EventManager _eventManager;
Scene _scene;
//! @brief The list of registered systems
std::vector<std::unique_ptr<System>> _systems = {};
//! @brief True if the engine should close after the end of the current tick.
@@ -34,6 +31,12 @@ namespace WAL
//! @brief Call the onFixedUpdate of every system with every component
void _fixedUpdate();
//! @brief Check if an entity met a system's dependencies.
//! @param entity The entity to check
//! @param system The system that will list dependencies
//! @return True if all dependencies are met, false otherwise.
static bool _hasDependencies(const Entity &entity, const System &system);
public:
//! @brief The time between each fixed update.
static std::chrono::nanoseconds timestep;
@@ -97,9 +100,6 @@ namespace WAL
return *this;
}
//! @brief Get the scene manager.
SceneManager &getSceneManager();
//! @brief Start the game loop
void run();
+41
View File
@@ -0,0 +1,41 @@
//
// Created by Zoe Roux on 5/21/21.
//
#include <catch2/catch.hpp>
#include <stdexcept>
#include <Entity/Entity.hpp>
#include "Models/Callback.hpp"
using namespace WAL;
TEST_CASE("Callback basic test", "[Callback]")
{
Callback<std::string> callback;
REQUIRE_NOTHROW(callback("1"));
SECTION("Check add") {
int id = callback.addCallback([](const std::string& i) {
if (i == "Super")
throw std::runtime_error(i);
});
REQUIRE_THROWS_AS(callback("Super"), std::runtime_error);
SECTION("Check remove") {
REQUIRE(id == 0);
callback.removeCallback(id);
REQUIRE_NOTHROW(callback("Super"));
}
}
}
TEST_CASE("Callback multiple arguments", "[Callback]")
{
Callback<std::string, int, unsigned *, Entity &> callback;
callback.addCallback([](const std::string& str, int a, unsigned *value, Entity &entity) {
throw std::runtime_error("");
});
Entity entity("name");
REQUIRE_THROWS_AS(callback("1", 0, nullptr, entity), std::runtime_error);
}