Entity Component System.

main
bog 2023-11-14 13:51:17 +01:00
parent a8a2bd893c
commit d7a6689cf5
15 changed files with 419 additions and 15 deletions

View File

@ -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',

12
src/BaseComponent.cpp Normal file
View File

@ -0,0 +1,12 @@
#include "BaseComponent.hpp"
namespace rid
{
/*explicit*/ BaseComponent::BaseComponent()
{
}
/*virtual*/ BaseComponent::~BaseComponent()
{
}
}

18
src/BaseComponent.hpp Normal file
View File

@ -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

13
src/BaseSystem.cpp Normal file
View File

@ -0,0 +1,13 @@
#include "BaseSystem.hpp"
namespace rid
{
/*explicit*/ BaseSystem::BaseSystem(size_t signature)
: m_signature { signature }
{
}
/*virtual*/ BaseSystem::~BaseSystem()
{
}
}

31
src/BaseSystem.hpp Normal file
View File

@ -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<size_t> const& entities) = 0;
virtual void draw(ECS& ecs, std::vector<size_t> const& entities) = 0;
private:
size_t m_signature;
};
}
#endif

101
src/ECS.cpp Normal file
View File

@ -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<MAX_ENTITIES; i++)
{
m_entities[entity][i].reset();
}
m_entities_pool.push_back(entity);
}
size_t ECS::get_entity_signature(size_t entity) const
{
size_t signature = 0;
for (size_t i=0; i<MAX_COMPONENTS; i++)
{
if (m_entities[entity][i] != nullptr)
{
signature |= (1 << i);
}
}
return signature;
}
std::vector<size_t> ECS::get_entities_by_signature(size_t signature) const
{
std::vector<size_t> entities;
for (size_t i=0; i<m_entities_count; i++)
{
size_t entity_sig = get_entity_signature(i);
if ( (signature & entity_sig) == signature )
{
entities.push_back(i);
}
}
return entities;
}
void ECS::update(float dt)
{
m_dt = dt;
for (auto& sys: m_systems)
{
sys->update(*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()));
}
}
}

132
src/ECS.hpp Normal file
View File

@ -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 <tuple>
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<size_t> get_entities_by_signature(size_t signature) const;
template <typename T>
T const& getc(size_t entity) const;
template <typename T>
T& getc(size_t entity);
// Components
// ----------
template <typename T>
size_t get_component_index();
template <typename T, typename... Args>
void attach_component(size_t entity, Args... args);
template <typename T>
void detach_component(size_t entity);
// Systems
// -------
template <typename T, typename... Args>
size_t get_signature();
template <typename... ComponentsArgs>
void add_system(std::unique_ptr<BaseSystem> system);
void update(float dt);
void draw(Window const& win);
private:
std::array<std::array<std::unique_ptr<BaseComponent>, MAX_COMPONENTS>, MAX_ENTITIES> m_entities;
std::vector<std::unique_ptr<BaseSystem>> m_systems;
std::vector<size_t> 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 <typename T>
size_t ECS::get_component_index()
{
static size_t count = m_components_count++;
return count;
}
template <typename T, typename... Args>
void ECS::attach_component(size_t entity, Args... args)
{
m_entities[entity][get_component_index<T>()] = std::make_unique<T>(args...);
}
template <typename T>
void ECS::detach_component(size_t entity)
{
m_entities[entity][get_component_index<T>()].reset();
}
template <typename T, typename... Args>
size_t ECS::get_signature()
{
size_t signature = (1 << get_component_index<T>());
if constexpr (sizeof...(Args) == 0)
{
return signature;
}
else
{
return signature | get_signature<Args...>();
}
}
template <typename... ComponentsArgs>
void ECS::add_system(std::unique_ptr<BaseSystem> system)
{
system->set_signature(get_signature<ComponentsArgs...>());
m_systems.push_back(std::move(system));
}
template <typename T>
T const& ECS::getc(size_t entity) const
{
return static_cast<T const&>(*m_entities[entity][get_component_index<T>()]);
}
template <typename T>
T& ECS::getc(size_t entity)
{
return static_cast<T&>(*m_entities[entity][get_component_index<T>()]);
}
}
#endif

View File

@ -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<Canvas>() }
{
auto tex = std::make_unique<Texture>();
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<BodyC>(std::make_unique<BodySystem>());
m_ecs.attach_component<BodyC>(m_ecs.create_entity(), glm::vec2 {64.f, 64.f}, glm::vec2 {64.f, 64.f});
m_ecs.attach_component<BodyC>(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);
}
}

View File

@ -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<Canvas> m_canvas;
ECS m_ecs;
};
}

22
src/comps/BodyC.hpp Normal file
View File

@ -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

View File

@ -9,6 +9,7 @@
#include <stdexcept>
#include <string>
#include <vector>
#include <array>
#include <iostream>
#include <sstream>
#include <fstream>

View File

@ -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();

View File

@ -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(); }

33
src/sys/BodySystem.cpp Normal file
View File

@ -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<size_t> const&) /*override*/
{
}
void BodySystem::draw(ECS& ecs, std::vector<size_t> const& entities) /*override*/
{
Canvas canvas;
for (auto entity: entities)
{
auto const& body = ecs.getc<BodyC>(entity);
canvas.draw_rect(body.pos, body.size, glm::vec4 {1, 1, 1, 1});
}
ecs.window().draw(canvas);
}
}

22
src/sys/BodySystem.hpp Normal file
View File

@ -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<size_t> const& entities) override;
void draw(ECS& ecs, std::vector<size_t> const& entities) override;
private:
};
}
#endif