From d7a6689cf50341c08064464c805e6a43163673ad Mon Sep 17 00:00:00 2001 From: bog Date: Tue, 14 Nov 2023 13:51:17 +0100 Subject: [PATCH] :sparkles: Entity Component System. --- meson.build | 6 ++ src/BaseComponent.cpp | 12 ++++ src/BaseComponent.hpp | 18 ++++++ src/BaseSystem.cpp | 13 ++++ src/BaseSystem.hpp | 31 ++++++++++ src/ECS.cpp | 101 +++++++++++++++++++++++++++++++ src/ECS.hpp | 132 +++++++++++++++++++++++++++++++++++++++++ src/arena/Arena.cpp | 29 ++++----- src/arena/Arena.hpp | 3 +- src/comps/BodyC.hpp | 22 +++++++ src/conf.in.hpp | 1 + src/gfx/Canvas.cpp | 7 +++ src/gfx/Canvas.hpp | 4 ++ src/sys/BodySystem.cpp | 33 +++++++++++ src/sys/BodySystem.hpp | 22 +++++++ 15 files changed, 419 insertions(+), 15 deletions(-) create mode 100644 src/BaseComponent.cpp create mode 100644 src/BaseComponent.hpp create mode 100644 src/BaseSystem.cpp create mode 100644 src/BaseSystem.hpp create mode 100644 src/ECS.cpp create mode 100644 src/ECS.hpp create mode 100644 src/comps/BodyC.hpp create mode 100644 src/sys/BodySystem.cpp create mode 100644 src/sys/BodySystem.hpp diff --git a/meson.build b/meson.build index dd78f2d..9c3f06e 100644 --- a/meson.build +++ b/meson.build @@ -28,6 +28,12 @@ executable( # game 'src/Game.cpp', 'src/BaseScene.cpp', + 'src/ECS.cpp', + 'src/BaseComponent.cpp', + 'src/BaseSystem.cpp', + + # systems + 'src/sys/BodySystem.cpp', # arena 'src/arena/Arena.cpp', diff --git a/src/BaseComponent.cpp b/src/BaseComponent.cpp new file mode 100644 index 0000000..dfcedce --- /dev/null +++ b/src/BaseComponent.cpp @@ -0,0 +1,12 @@ +#include "BaseComponent.hpp" + +namespace rid +{ + /*explicit*/ BaseComponent::BaseComponent() + { + } + + /*virtual*/ BaseComponent::~BaseComponent() + { + } +} diff --git a/src/BaseComponent.hpp b/src/BaseComponent.hpp new file mode 100644 index 0000000..6e22728 --- /dev/null +++ b/src/BaseComponent.hpp @@ -0,0 +1,18 @@ +#ifndef rid_BASECOMPONENT_HPP +#define rid_BASECOMPONENT_HPP + +#include "conf.hpp" + +namespace rid +{ + class BaseComponent + { + public: + explicit BaseComponent(); + virtual ~BaseComponent(); + + private: + }; +} + +#endif diff --git a/src/BaseSystem.cpp b/src/BaseSystem.cpp new file mode 100644 index 0000000..007be1d --- /dev/null +++ b/src/BaseSystem.cpp @@ -0,0 +1,13 @@ +#include "BaseSystem.hpp" + +namespace rid +{ + /*explicit*/ BaseSystem::BaseSystem(size_t signature) + : m_signature { signature } + { + } + + /*virtual*/ BaseSystem::~BaseSystem() + { + } +} diff --git a/src/BaseSystem.hpp b/src/BaseSystem.hpp new file mode 100644 index 0000000..b4aba4e --- /dev/null +++ b/src/BaseSystem.hpp @@ -0,0 +1,31 @@ +#ifndef rid_BASESYSTEM_HPP +#define rid_BASESYSTEM_HPP + +#include "conf.hpp" + +namespace rid +{ + class ECS; + + class BaseSystem + { + public: + explicit BaseSystem(size_t signature=0); + virtual ~BaseSystem(); + + // Signature + // --------- + void set_signature(size_t sig) { m_signature = sig; } + size_t signature() const { return m_signature; } + + // Process + // ------- + virtual void update(ECS& ecs, std::vector const& entities) = 0; + virtual void draw(ECS& ecs, std::vector const& entities) = 0; + + private: + size_t m_signature; + }; +} + +#endif diff --git a/src/ECS.cpp b/src/ECS.cpp new file mode 100644 index 0000000..2bf1127 --- /dev/null +++ b/src/ECS.cpp @@ -0,0 +1,101 @@ +#include "ECS.hpp" + +namespace rid +{ + /*explicit*/ ECS::ECS() + { + } + + /*virtual*/ ECS::~ECS() + { + } + + size_t ECS::create_entity() + { + size_t entity; + + if (m_entities_pool.empty()) + { + entity = m_entities_count++; + } + else + { + entity = m_entities_pool.back(); + m_entities_pool.pop_back(); + } + + if (entity >= MAX_ENTITIES) + { + throw ecs_error {"Cannot create entity: entity overflow."}; + } + + return entity; + } + + void ECS::delete_entity(size_t entity) + { + if (entity >= MAX_ENTITIES) + { + throw ecs_error {"Cannot delete entity: entity overflow."}; + } + + for (size_t i=0; i ECS::get_entities_by_signature(size_t signature) const + { + std::vector entities; + + for (size_t i=0; iupdate(*this, get_entities_by_signature(sys->signature())); + } + } + + void ECS::draw(Window const& win) + { + m_window = &win; + + for (auto& sys: m_systems) + { + sys->draw(*this, get_entities_by_signature(sys->signature())); + } + } +} diff --git a/src/ECS.hpp b/src/ECS.hpp new file mode 100644 index 0000000..80934a9 --- /dev/null +++ b/src/ECS.hpp @@ -0,0 +1,132 @@ +#ifndef rid_ECS_HPP +#define rid_ECS_HPP + +#include "conf.hpp" +#include "BaseComponent.hpp" +#include "BaseSystem.hpp" +#include "gfx/Window.hpp" +#include + +namespace rid +{ + RID_ERROR(ecs_error); + + constexpr size_t MAX_ENTITIES = 2048; + constexpr size_t MAX_COMPONENTS = 32; + + class ECS + { + public: + explicit ECS(); + virtual ~ECS(); + + // Context + // ------- + float dt() const { return m_dt; } + Window const& window() const { return *m_window; } + + // Entities + // -------- + size_t create_entity(); + + void delete_entity(size_t entity); + + size_t get_entity_signature(size_t entity) const; + + std::vector get_entities_by_signature(size_t signature) const; + + template + T const& getc(size_t entity) const; + + template + T& getc(size_t entity); + + // Components + // ---------- + template + size_t get_component_index(); + + template + void attach_component(size_t entity, Args... args); + + template + void detach_component(size_t entity); + + // Systems + // ------- + template + size_t get_signature(); + + template + void add_system(std::unique_ptr system); + + void update(float dt); + void draw(Window const& win); + + private: + std::array, MAX_COMPONENTS>, MAX_ENTITIES> m_entities; + std::vector> m_systems; + std::vector m_entities_pool; + size_t m_entities_count = 0; + size_t m_components_count = 0; + + Window const* m_window = nullptr; + float m_dt = 0.0f; + }; + + template + size_t ECS::get_component_index() + { + static size_t count = m_components_count++; + return count; + } + + template + void ECS::attach_component(size_t entity, Args... args) + { + m_entities[entity][get_component_index()] = std::make_unique(args...); + } + + template + void ECS::detach_component(size_t entity) + { + m_entities[entity][get_component_index()].reset(); + } + + template + size_t ECS::get_signature() + { + size_t signature = (1 << get_component_index()); + + if constexpr (sizeof...(Args) == 0) + { + return signature; + } + else + { + return signature | get_signature(); + } + } + + template + void ECS::add_system(std::unique_ptr system) + { + system->set_signature(get_signature()); + m_systems.push_back(std::move(system)); + } + + template + T const& ECS::getc(size_t entity) const + { + return static_cast(*m_entities[entity][get_component_index()]); + } + + template + T& ECS::getc(size_t entity) + { + return static_cast(*m_entities[entity][get_component_index()]); + } + +} + +#endif diff --git a/src/arena/Arena.cpp b/src/arena/Arena.cpp index 37a2763..a3cad69 100644 --- a/src/arena/Arena.cpp +++ b/src/arena/Arena.cpp @@ -1,36 +1,37 @@ #include "Arena.hpp" #include "conf.hpp" +// Components +// ---------- +#include "../comps/BodyC.hpp" + +// Systems +// ------- + +#include "../sys/BodySystem.hpp" + namespace rid { /*explicit*/ Arena::Arena(Game& game) : BaseScene(game) - , m_canvas { std::make_unique() } { - auto tex = std::make_unique(); - tex->load(RID_DATADIR / "assets" / "images" / "walk_0.png"); - m_canvas->set_texture(std::move(tex)); - - m_canvas->draw_tex(glm::vec2 {0.0f, 0.0f}, - glm::vec2 {96.0f, 96.0f}, - glm::vec4 {1.0f, 1.0f, 1.0f, 1.0f}, - glm::vec2 {0.f, 0.0f}, - glm::vec2 {32.0f, 32.0f}); - - m_canvas->move(glm::vec2 {512.f, 512.f}); + m_ecs.add_system(std::make_unique()); + m_ecs.attach_component(m_ecs.create_entity(), glm::vec2 {64.f, 64.f}, glm::vec2 {64.f, 64.f}); + m_ecs.attach_component(m_ecs.create_entity(), glm::vec2 {640.f, 640.f}, glm::vec2 {128.f, 300.f}); } /*virtual*/ Arena::~Arena() { } - void Arena::update(float) /*override*/ + void Arena::update(float dt) /*override*/ { + m_ecs.update(dt); } void Arena::draw(Window const& win) /*override*/ { - win.draw(*m_canvas); + m_ecs.draw(win); } } diff --git a/src/arena/Arena.hpp b/src/arena/Arena.hpp index 35d6831..ad6bee2 100644 --- a/src/arena/Arena.hpp +++ b/src/arena/Arena.hpp @@ -4,6 +4,7 @@ #include "conf.hpp" #include "../BaseScene.hpp" #include "../gfx/Canvas.hpp" +#include "../ECS.hpp" namespace rid { @@ -17,7 +18,7 @@ namespace rid void draw(Window const& win) override; private: - std::unique_ptr m_canvas; + ECS m_ecs; }; } diff --git a/src/comps/BodyC.hpp b/src/comps/BodyC.hpp new file mode 100644 index 0000000..4742910 --- /dev/null +++ b/src/comps/BodyC.hpp @@ -0,0 +1,22 @@ +#ifndef rid_BODYC_HPP +#define rid_BODYC_HPP + +#include "conf.hpp" +#include "../BaseComponent.hpp" + +namespace rid +{ + struct BodyC: public BaseComponent + { + glm::vec2 pos; + glm::vec2 size; + + explicit BodyC(glm::vec2 _pos, glm::vec2 _size) + : pos { _pos } + , size { _size } + { + } + }; +} + +#endif diff --git a/src/conf.in.hpp b/src/conf.in.hpp index c58a15f..7c50a25 100644 --- a/src/conf.in.hpp +++ b/src/conf.in.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include diff --git a/src/gfx/Canvas.cpp b/src/gfx/Canvas.cpp index fb3e0e5..a88cd96 100644 --- a/src/gfx/Canvas.cpp +++ b/src/gfx/Canvas.cpp @@ -96,6 +96,13 @@ namespace rid update(); } + void Canvas::draw_rect(glm::vec2 pos, + glm::vec2 size, + glm::vec4 color) + { + draw_tex(pos, size, color, glm::vec2 {0.0f}, glm::vec2 {0.0f}); + } + void Canvas::draw(glm::mat4 transform) const { use(); diff --git a/src/gfx/Canvas.hpp b/src/gfx/Canvas.hpp index d4e87f4..47cb04f 100644 --- a/src/gfx/Canvas.hpp +++ b/src/gfx/Canvas.hpp @@ -33,6 +33,10 @@ namespace rid glm::vec2 tex_pos, glm::vec2 tex_size); + void draw_rect(glm::vec2 pos, + glm::vec2 size, + glm::vec4 color); + void draw(glm::mat4 transform) const; void clear() { m_vertices.clear(); update(); } diff --git a/src/sys/BodySystem.cpp b/src/sys/BodySystem.cpp new file mode 100644 index 0000000..936cc85 --- /dev/null +++ b/src/sys/BodySystem.cpp @@ -0,0 +1,33 @@ +#include "BodySystem.hpp" +#include "../ECS.hpp" +#include "../comps/BodyC.hpp" + +namespace rid +{ + /*explicit*/ BodySystem::BodySystem() + : BaseSystem() + { + } + + /*virtual*/ BodySystem::~BodySystem() + { + } + + void BodySystem::update(ECS&, std::vector const&) /*override*/ + { + } + + void BodySystem::draw(ECS& ecs, std::vector const& entities) /*override*/ + { + Canvas canvas; + + for (auto entity: entities) + { + auto const& body = ecs.getc(entity); + + canvas.draw_rect(body.pos, body.size, glm::vec4 {1, 1, 1, 1}); + } + + ecs.window().draw(canvas); + } +} diff --git a/src/sys/BodySystem.hpp b/src/sys/BodySystem.hpp new file mode 100644 index 0000000..eeb584d --- /dev/null +++ b/src/sys/BodySystem.hpp @@ -0,0 +1,22 @@ +#ifndef rid_BODYSYSTEM_HPP +#define rid_BODYSYSTEM_HPP + +#include "conf.hpp" +#include "../BaseSystem.hpp" + +namespace rid +{ + class BodySystem: public BaseSystem + { + public: + explicit BodySystem(); + virtual ~BodySystem(); + + void update(ECS& ecs, std::vector const& entities) override; + void draw(ECS& ecs, std::vector const& entities) override; + + private: + }; +} + +#endif