From 095b529129a6768dbfbe66ef082215581806d2bc Mon Sep 17 00:00:00 2001 From: bog Date: Fri, 27 Oct 2023 19:33:20 +0200 Subject: [PATCH] :sparkles: simple entity component system. --- meson.build | 5 ++ src/commons.hpp | 3 + src/ecs/BaseComponent.cpp | 8 +++ src/ecs/BaseComponent.hpp | 18 ++++++ src/ecs/BaseSystem.cpp | 5 ++ src/ecs/BaseSystem.hpp | 42 ++++++++++++++ src/ecs/ECS.cpp | 27 +++++++++ src/ecs/ECS.hpp | 113 ++++++++++++++++++++++++++++++++++++++ 8 files changed, 221 insertions(+) create mode 100644 src/ecs/BaseComponent.cpp create mode 100644 src/ecs/BaseComponent.hpp create mode 100644 src/ecs/BaseSystem.cpp create mode 100644 src/ecs/BaseSystem.hpp create mode 100644 src/ecs/ECS.cpp create mode 100644 src/ecs/ECS.hpp diff --git a/meson.build b/meson.build index 486bf65..f91a92d 100644 --- a/meson.build +++ b/meson.build @@ -35,6 +35,11 @@ executable( 'src/scenes/Scene.cpp', 'src/scenes/World.cpp', + # Entity-Component-System (ECS) + 'src/ecs/ECS.cpp', + 'src/ecs/BaseComponent.cpp', + 'src/ecs/BaseSystem.cpp', + # Logs 'src/logs/Logs.cpp', 'src/logs/FileLogs.cpp', diff --git a/src/commons.hpp b/src/commons.hpp index 444c41b..e8d3a1f 100644 --- a/src/commons.hpp +++ b/src/commons.hpp @@ -34,4 +34,7 @@ {} \ } +using entity_t = size_t; +using component_t = size_t; + #endif diff --git a/src/ecs/BaseComponent.cpp b/src/ecs/BaseComponent.cpp new file mode 100644 index 0000000..23fe141 --- /dev/null +++ b/src/ecs/BaseComponent.cpp @@ -0,0 +1,8 @@ +#include "BaseComponent.hpp" + +namespace ml +{ + /*virtual*/ BaseComponent::~BaseComponent() + { + } +} diff --git a/src/ecs/BaseComponent.hpp b/src/ecs/BaseComponent.hpp new file mode 100644 index 0000000..2d9c432 --- /dev/null +++ b/src/ecs/BaseComponent.hpp @@ -0,0 +1,18 @@ +#ifndef ml_BASECOMPONENT_HPP +#define ml_BASECOMPONENT_HPP + +#include "../commons.hpp" + +namespace ml +{ + class BaseComponent + { + public: + BaseComponent() = default; + virtual ~BaseComponent(); + + private: + }; +} + +#endif diff --git a/src/ecs/BaseSystem.cpp b/src/ecs/BaseSystem.cpp new file mode 100644 index 0000000..ae1721c --- /dev/null +++ b/src/ecs/BaseSystem.cpp @@ -0,0 +1,5 @@ +#include "BaseSystem.hpp" + +namespace ml +{ +} diff --git a/src/ecs/BaseSystem.hpp b/src/ecs/BaseSystem.hpp new file mode 100644 index 0000000..c2df1bc --- /dev/null +++ b/src/ecs/BaseSystem.hpp @@ -0,0 +1,42 @@ +#ifndef ml_BASESYSTEM_HPP +#define ml_BASESYSTEM_HPP + +#include "ECS.hpp" +#include "../commons.hpp" + +namespace ml +{ + template + class BaseSystem + { + public: + explicit BaseSystem(ECS& ecs) + : m_ecs { ecs } + , m_sig { m_ecs.signature() } + { + } + + virtual ~BaseSystem() = default; + + virtual void update(std::vector entities) + { + for (auto entity: entities) + { + signature_t entity_sig = m_ecs.signature(entity); + + if ((entity_sig & m_sig) == m_sig) + { + process(entity); + } + } + } + + virtual void process(entity_t entity) = 0; + + protected: + ECS& m_ecs; + signature_t m_sig; + }; +} + +#endif diff --git a/src/ecs/ECS.cpp b/src/ecs/ECS.cpp new file mode 100644 index 0000000..e244af5 --- /dev/null +++ b/src/ecs/ECS.cpp @@ -0,0 +1,27 @@ +#include "ECS.hpp" + +namespace ml +{ + /*explicit*/ ECS::ECS() + { + } + + /*virtual*/ ECS::~ECS() + { + } + + signature_t ECS::signature(entity_t entity) const + { + int res = 0; + + for (component_t comp_id=0; comp_id < MAX_COMPONENTS; comp_id++) + { + if (m_entities[entity][comp_id] != nullptr) + { + res = res | (1 << comp_id); + } + } + + return res; + } +} diff --git a/src/ecs/ECS.hpp b/src/ecs/ECS.hpp new file mode 100644 index 0000000..6d2590d --- /dev/null +++ b/src/ecs/ECS.hpp @@ -0,0 +1,113 @@ +#ifndef ml_ECS_HPP +#define ml_ECS_HPP + +#include "../commons.hpp" +#include "BaseComponent.hpp" + +namespace ml +{ + using components_t = std::unordered_map>; + using signature_t = size_t; + constexpr size_t MAX_ENTITIES = 4096; + constexpr size_t MAX_COMPONENTS = 128; + + ML_ERROR(ecs_error); + + class ECS + { + public: + explicit ECS(); + virtual ~ECS(); + + template + signature_t signature(); + + signature_t signature(entity_t entity) const; + + template + size_t component_id(); + + template + entity_t spawn(T component); + + template + entity_t spawn(T component, Args... args); + + template + T& get(entity_t entity); + + private: + std::array, + MAX_COMPONENTS>, MAX_ENTITIES> + m_entities; + + size_t m_entities_count = 0; + size_t m_component_counter = 1; + }; + + template + signature_t ECS::signature() + { + component_t id = component_id(); + + size_t sig = 1 << id; + + if constexpr (sizeof...(Args) != 0) + { + sig |= signature(); + } + + return sig; + } + + template + size_t ECS::component_id() + { + static size_t comp_id = m_component_counter++; + return comp_id; + } + + template + entity_t ECS::spawn(T component) + { + size_t comp_id = component_id(); + + m_entities[m_entities_count][comp_id] = + std::make_unique(component); + + m_entities_count++; + + return m_entities_count - 1; + } + + template + entity_t ECS::spawn(T component, Args... args) + { + size_t comp_id = component_id(); + entity_t entity = spawn(args...); + + m_entities[entity][comp_id] = + std::make_unique(component); + + return entity; + } + + template + T& ECS::get(entity_t entity) + { + size_t comp_id = component_id(); + auto* comp = m_entities[entity][comp_id].get(); + + if (comp == nullptr) + { + throw ecs_error {"component not found in entity '" + + std::to_string(entity) + + "'"}; + } + + return static_cast(*comp); + } +} + +#endif