✨ Entity Component System.
parent
a8a2bd893c
commit
d7a6689cf5
|
@ -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',
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
#include "BaseComponent.hpp"
|
||||
|
||||
namespace rid
|
||||
{
|
||||
/*explicit*/ BaseComponent::BaseComponent()
|
||||
{
|
||||
}
|
||||
|
||||
/*virtual*/ BaseComponent::~BaseComponent()
|
||||
{
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -0,0 +1,13 @@
|
|||
#include "BaseSystem.hpp"
|
||||
|
||||
namespace rid
|
||||
{
|
||||
/*explicit*/ BaseSystem::BaseSystem(size_t signature)
|
||||
: m_signature { signature }
|
||||
{
|
||||
}
|
||||
|
||||
/*virtual*/ BaseSystem::~BaseSystem()
|
||||
{
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
|
@ -9,6 +9,7 @@
|
|||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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(); }
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
Loading…
Reference in New Issue