16
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-07-21 14:49:19 +02:00

ECS System WIP

This commit is contained in:
2026-04-07 23:40:03 +02:00
parent f6db9978d2
commit 8e9fd9225f
30 changed files with 1133 additions and 242 deletions

69
entitysystem/ECScene.cpp Normal file
View File

@@ -0,0 +1,69 @@
//
// Created by Daniu
//
#include "ECScene.h"
#include "utilities/Logs.h"
ECScene::ECScene()
: m_loaded(false)
{
}
ECScene::~ECScene()
{
Unload();
}
void ECScene::Load()
{
if (m_loaded)
return;
m_systems.Create(m_world);
OnLoad();
m_loaded = true;
}
void ECScene::Unload()
{
if (!m_loaded)
return;
OnUnload();
m_world.Clear();
m_loaded = false;
}
void ECScene::Update(float dt)
{
if (!m_loaded){
return;
}
m_systems.Update(m_world, dt);
OnUpdate(dt);
}
ECWorld& ECScene::World()
{
return m_world;
}
const ECWorld& ECScene::World() const
{
return m_world;
}
void ECScene::OnLoad()
{
}
void ECScene::OnUnload()
{;
}
void ECScene::OnUpdate(float dt)
{
(void)dt;
}

42
entitysystem/ECScene.h Normal file
View File

@@ -0,0 +1,42 @@
//
// Created by Daniu
//
#ifndef EU07_ECSCENE_H
#define EU07_ECSCENE_H
#pragma once
#include "ECWorld.h"
#include "systems/SystemManager.h"
class ECScene
{
public:
ECScene();
virtual ~ECScene();
void Load();
void Unload();
void Update(float dt);
ECWorld& World();
const ECWorld& World() const;
protected:
virtual void OnLoad();
virtual void OnUnload();
virtual void OnUpdate(float dt);
protected:
ECWorld m_world;
SystemManager m_systems;
private:
bool m_loaded;
};
#endif //EU07_ECSCENE_H

54
entitysystem/ECWorld.cpp Normal file
View File

@@ -0,0 +1,54 @@
//
// Created by Daniu
//
#include "ECWorld.h"
#include "entitysystem/components/BasicComponents.h"
entt::entity ECWorld::CreateEntity()
{
return m_registry.create();
}
void ECWorld::DestroyEntity(entt::entity entity)
{
if (m_registry.valid(entity))
m_registry.destroy(entity);
}
void ECWorld::Clear()
{
m_registry.clear();
}
bool ECWorld::IsAlive(entt::entity entity) const
{
return m_registry.valid(entity);
}
entt::entity ECWorld::FindEntityByName(const std::string& name) const
{
auto view = m_registry.view<ECSComponent::Identification>();
for (auto entity : view)
{
const auto& id = view.get<ECSComponent::Identification>(entity);
if (id.Name.ToString() == name)
{
return entity;
}
}
return entt::null;
}
entt::registry& ECWorld::Registry()
{
return m_registry;
}
const entt::registry& ECWorld::Registry() const
{
return m_registry;
}

135
entitysystem/ECWorld.h Normal file
View File

@@ -0,0 +1,135 @@
//
// Created by Daniu
//
#ifndef EU07_ECWORLD_H
#define EU07_ECWORLD_H
#pragma once
#include <entt/entt.hpp>
#include <utility>
#include <type_traits>
class ECWorld
{
public:
ECWorld() = default;
~ECWorld() = default;
entt::entity CreateEntity();
void DestroyEntity(entt::entity entity);
void Clear();
bool IsAlive(entt::entity entity) const;
entt::registry& Registry();
const entt::registry& Registry() const;
template<typename T, typename... Args>
T& AddComponent(entt::entity entity, Args&&... args);
template<typename T>
bool HasComponent(entt::entity entity) const;
template<typename T>
T* GetComponent(entt::entity entity);
template<typename T>
const T* GetComponent(entt::entity entity) const;
template<typename T>
void RemoveComponent(entt::entity entity);
entt::entity FindEntityByName(const std::string& name) const;
template<typename... Components>
auto View()
{
return m_registry.view<Components...>();
}
template<typename... Components, typename Func>
void Each(Func&& func)
{
auto view = m_registry.view<Components...>();
for (auto entity : view)
{
auto components = std::forward_as_tuple(view.get<Components>(entity)...);
std::apply([&](Components&... comps)
{
func(entity, comps...);
}, components);
}
}
std::vector<entt::entity> GetEntities() const
{
std::vector<entt::entity> entities;
auto *storage = m_registry.storage<entt::entity>();
for (auto entity : *storage)
{
entities.push_back(entity);
}
return entities;
}
std::size_t GetEntityCount() const
{
return m_registry.storage<entt::entity>()->size();
}
private:
entt::registry m_registry;
};
template<typename T, typename... Args>
T& ECWorld::AddComponent(entt::entity entity, Args&&... args)
{
static_assert(std::is_constructible_v<T, Args...>,
"Component must be constructible with provided arguments");
if (m_registry.all_of<T>(entity))
{
return m_registry.replace<T>(entity, std::forward<Args>(args)...);
}
return m_registry.emplace<T>(entity, std::forward<Args>(args)...);
}
template<typename T>
bool ECWorld::HasComponent(entt::entity entity) const
{
return m_registry.all_of<T>(entity);
}
template<typename T>
T* ECWorld::GetComponent(entt::entity entity)
{
return m_registry.try_get<T>(entity);
}
template<typename T>
const T* ECWorld::GetComponent(entt::entity entity) const
{
return m_registry.try_get<T>(entity);
}
template<typename T>
void ECWorld::RemoveComponent(entt::entity entity)
{
if (m_registry.all_of<T>(entity))
m_registry.remove<T>(entity);
}
extern ECWorld& GetComponentSystem();
#define ECS GetComponentSystem()
#endif //EU07_ECWORLD_H

View File

@@ -0,0 +1,59 @@
//
// Created by Daniu
//
#include "SceneManager.h"
#include "ECScene.h"
SceneManager::SceneManager() = default;
SceneManager::~SceneManager()
{
if (m_currentScene)
m_currentScene->Unload();
}
void SceneManager::SetScene(std::unique_ptr<ECScene> scene)
{
m_pendingScene = std::move(scene);
if (m_currentScene)
m_currentScene->Load();
}
void SceneManager::Update(float dt)
{
if (m_pendingScene)
SwitchScene();
if (m_currentScene){
m_currentScene->Update(dt);
}
}
void SceneManager::SwitchScene()
{
if (m_currentScene)
m_currentScene->Unload();
m_currentScene = std::move(m_pendingScene);
if (m_currentScene)
m_currentScene->Load();
}
ECScene* SceneManager::CurrentScene()
{
return m_currentScene.get();
}
const ECScene* SceneManager::CurrentScene() const
{
return m_currentScene.get();
}
bool SceneManager::HasScene() const
{
return m_currentScene != nullptr;
}

View File

@@ -0,0 +1,40 @@
//
// Created by Daniu
//
#ifndef EU07_SCENEMANAGER_H
#define EU07_SCENEMANAGER_H
#pragma once
#include <memory>
#include <string>
class ECScene;
class SceneManager
{
public:
SceneManager();
~SceneManager();
void SetScene(std::unique_ptr<ECScene> scene);
void Update(float dt);
ECScene* CurrentScene();
const ECScene* CurrentScene() const;
bool HasScene() const;
private:
void SwitchScene();
private:
std::unique_ptr<ECScene> m_currentScene;
std::unique_ptr<ECScene> m_pendingScene;
};
#endif //EU07_SCENEMANAGER_H

View File

@@ -3,11 +3,14 @@
*/
#ifndef EU07_BASICCOMPONENTS_H
#define EU07_BASICCOMPONENTS_H
#include "audio/sound.h"
#include "entt/entity/entity.hpp"
#include "glm/vec3.hpp"
#include "glm/gtc/quaternion.hpp"
#include <string>
#include "registry/FName.h"
#include "utils/uuid.hpp"
#include <cstdint>
#include <string>
namespace ECSComponent
{
@@ -16,18 +19,23 @@ namespace ECSComponent
///</summary>
struct Transform
{
glm::vec3 Position{0.f}; // object position
glm::dvec3 Position{0.f}; // object position
glm::quat Rotation{1.f, 0.f, 0.f, 0.f}; // object rotation
glm::vec3 Scale{1.f}; // object scale
};
struct Velocity
{
glm::vec3 Value{0.f}; // object velocity
};
///< summary>
/// Basic component for naming entities
/// in future for scenery hierarchy
///</summary>
struct Identification
{
std::string Name; // object name - may contain slashes for editor hierarchy "directories"
FName Name; // object name - may contain slashes for editor hierarchy "directories"
std::uint64_t Id{0}; // id in scene
};
@@ -39,6 +47,16 @@ struct Parent
entt::entity value{entt::null};
};
struct SoundComponent
{
sound_source sound; // sound source
float volume = 1.0f; // 01
float pitch = 1.0f; // speed / pitch
float range = 1.0f; // range
bool loop = false;
bool playOnStart = false;
bool isPlaying = false;
};
///< summary>
/// Empty component for entities that are disabled and should not be processed by systems
///</summary>

View File

@@ -1,6 +1,9 @@
#ifndef EU07_RENDERCOMPONENTS_H
#define EU07_RENDERCOMPONENTS_H
#include "registry/FName.h"
#include "rendering/geometrybank.h"
namespace ECSComponent
{
/// <summary>
@@ -11,6 +14,8 @@ namespace ECSComponent
/// </remarks>
struct MeshRenderer
{
gfx::geometry_handle meshHandle;
bool visible = true;
};
/// <summary>
@@ -19,7 +24,53 @@ struct MeshRenderer
/// <remarks>Currently empty
/// TODO: Add component members
/// </remarks>
struct SpotLight{};
struct SpotLight
{
glm::vec3 Color{ 1.0f, 1.0f, 1.0f };
float Intensity = 1.0f;
float Range = 25.0f;
float InnerAngle = 15.0f;
float OuterAngle = 25.0f;
bool CastShadows = false;
bool Enabled = true;
};
// TODO: AreaLight like blender
// TODO: SunLight for scene
struct ParticleEmitter
{
FName particleEffectPath = ("");
bool active = true;
};
struct Decal
{
FName decalTexturePath = ("");
float size = 1.0f;
bool active = true;
};
struct Billboard
{
FName texturePath = ("");
float size = 1.0f;
bool active = true;
};
struct Line {
glm::vec3 start{ 0.0f };
glm::vec3 end{ 1.0f, 0.0f, 0.0f };
glm::vec3 color{ 1.0f, 1.0f, 1.0f };
float thickness = 1.0f;
bool active = true;
};
/// <summary>
/// Component for entities that can be rendered with LOD

View File

@@ -1,69 +0,0 @@
//
// Created by Hirek on 3/14/2026.
//
#include "ecs.h"
void ECS::ClearWorld()
{
world_.clear();
}
entt::entity ECS::CreateObject()
{
const auto e = world_.create();
world_.emplace<ECSComponent::Transform>(e);
// add UID
auto id = world_.emplace<ECSComponent::Identification>(e);
id.Id = nextId_++;
return e;
}
void ECS::DestroyObject(entt::entity Entity)
{
if (Entity == entt::null) // check if Entity is not null
return;
if (!world_.valid(Entity)) // check if exist
return;
world_.destroy(Entity);
}
bool ECS::ValidObject(entt::entity entity) const
{
return entity != entt::null && world_.valid(entity);
}
entt::entity ECS::FindById(std::uint64_t id)
{
auto view = world_.view<const ECSComponent::Identification>();
for (auto e : view)
{
const auto &ident = view.get<ECSComponent::Identification>(e);
if (ident.Id == id)
return e;
}
return entt::null;
}
std::vector<entt::entity> ECS::FindByName(std::string_view name)
{
std::vector<entt::entity> result;
auto view = world_.view<const ECSComponent::Identification>();
for (auto e : view)
{
const auto &ident = view.get<ECSComponent::Identification>(e);
if (ident.Name == name)
{
result.push_back(e);
}
}
return result;
}
ECS &GetComponentSystem()
{
static ECS _ecs;
return _ecs;
}

View File

@@ -1,153 +0,0 @@
//
// Created by Hirek on 3/14/2026.
//
#ifndef EU07_ECS_H
#define EU07_ECS_H
#include "components/BasicComponents.h"
#include "entt/entity/registry.hpp"
#include <cstdint>
#include <vector>
#include <string_view>
#include <utility>
class ECS final
{
private:
entt::registry world_; // scene registry
std::uint64_t nextId_ = 1; // 0 is invalid/none
public:
//
// World lifecycle
//
/// <summary>
/// Clears the world, removing all entities and components
/// </summary>
void ClearWorld();
//
// Objects
//
/// <summary>
/// Creates new object with basic components (Transform, Identification) and returns its entity handle.
/// </summary>
/// <returns>Entity handle of created object</returns>
entt::entity CreateObject();
/// <summary>
/// Destroys object with it's all components
/// </summary>
/// <param name="Entity">Entity to be removed</param>
void DestroyObject(entt::entity Entity);
/// <summary>
/// Checks if object with given entity handle exists in the registry
/// </summary>
/// <param name="entity">Entity handle to check</param>
/// <returns>true if object exists, false otherwise</returns>
bool ValidObject(entt::entity entity) const;
//
// Identification lookups
//
/// <summary>
/// Finds object with given UID. Returns null entity if not found.
/// </summary>
/// <param name="id">UID of object</param>
entt::entity FindById(std::uint64_t id);
/// <summary>
/// Finds objects with given name. Returns empty vector if not found.
/// </summary>
/// <param name="name">Name of object</param>
/// <remarks>May return more than 1 as many objects can have the same name</remarks>
std::vector<entt::entity> FindByName(std::string_view name);
//
// Components
//
/// <summary>
/// Adds component of type T to entity, forwarding provided arguments to component constructor. If component already exists, it will be replaced.
/// </summary>
/// <typeparam name="T">Component type</typeparam>
/// <param name="entity">Entity to which component will be added</param>
/// <param name="args">Arguments forwarded to component constructor</param>
/// <returns>Reference to added component</returns>
template <class T, class... Args> T &AddComponent(entt::entity entity, Args &&...args)
{
return world_.emplace<T>(entity, std::forward<Args>(args)...);
}
/// <summary>
/// Returns entity's component
/// </summary>
/// <typeparam name="T"> type</typeparam>
/// <param name="entity">Entity to which component will be added</param>
/// <returns>Reference to added component</returns>
template <class T> T &GetComponent(entt::entity entity)
{
return world_.get<T>(entity);
}
/// <summary>
/// Returns entity's component
/// </summary>
/// <typeparam name="T"> type</typeparam>
/// <param name="entity">Entity to which component will be added</param>
/// <returns>Reference to added component</returns>
template <class T> const T &GetComponent(entt::entity entity) const
{
return world_.get<T>(entity);
}
/// <summary>
/// Tries to get component of type T from entity. Returns nullptr if component does not exist.
/// </summary>
/// <typeparam name="T">Component type</typeparam>
/// <param name="entity">Entity from which component will be retrieved</param>
/// <returns>Pointer to component if exists, nullptr otherwise</returns>
template <class T> T *TryGetComponent(entt::entity entity)
{
return world_.try_get<T>(entity);
}
/// <summary>
/// Tries to get component of type T from entity. Returns nullptr if component does not exist.
/// </summary>
/// <typeparam name="T">Component type</typeparam>
/// <param name="entity">Entity from which component will be retrieved</param>
/// <returns>Pointer to component if exists, nullptr otherwise</returns>
template <class T> const T *TryGetComponent(entt::entity entity) const
{
return world_.try_get<T>(entity);
}
/// <summary>
/// Checks if entity has component of type T.
/// </summary>
/// <typeparam name="T">Component type</typeparam>
/// <param name="entity">Entity to check</param>
/// <returns>true if entity has component, false otherwise</returns>
template <class... T> bool HasComponent(entt::entity entity) const
{
return world_.all_of<T...>(entity);
}
/// <summary>
/// Removes component of type T from entity. Does nothing if component does not exist.
/// </summary> <typeparam name="T">Component type</typeparam>
/// <param name="entity">Entity from which component will be removed</param>
template <class... T> void RemoveComponent(entt::entity entity)
{
world_.remove<T...>(entity);
}
};
extern ECS& GetComponentSystem();
#define CS GetComponentSystem()
#endif // EU07_ECS_H

View File

@@ -0,0 +1,35 @@
//
// Created by Daniu
//
#include "GameScene.h"
#include "entitysystem/components/BasicComponents.h"
#include "entitysystem/components/RenderComponents.h"
#include "entitysystem/systems/MovementSystem.h"
#include "utilities/Logs.h"
GameScene::GameScene() = default;
GameScene::~GameScene() = default ;
void GameScene::OnLoad()
{
m_systems.AddSystem<MovementSystem>();
}
void GameScene::OnUpdate(float dt)
{
//auto& world = World();
//auto view = world.View<ECSComponent::Transform>();
}
void GameScene::OnUnload()
{
World().Clear();
}

View File

@@ -0,0 +1,30 @@
//
// Created by Daniu
//
#ifndef EU07_GAMESCENE_H
#define EU07_GAMESCENE_H
#pragma once
#include "entitysystem/ECScene.h"
class GameScene final : public ECScene
{
public:
GameScene();
~GameScene() override;
private:
void OnLoad() override;
void OnUnload() override;
void OnUpdate(float dt) override;
private:
void CreateTestEntities();
};
#endif //EU07_GAMESCENE_H

View File

@@ -0,0 +1,5 @@
//
// Created by Daniu
//
#include "BaseSystem.h"

View File

@@ -0,0 +1,25 @@
//
// Created by Daniu
//
#ifndef EU07_BASICSYSTEM_H
#define EU07_BASICSYSTEM_H
#pragma once
class ECWorld;
class BaseSystem
{
public:
virtual ~BaseSystem() = default;
virtual void OnCreate(ECWorld& world) {}
virtual void OnDestroy(ECWorld& world) {}
virtual void Update(ECWorld& world, float dt) {}
};
#endif //EU07_BASICSYSTEM_H

View File

@@ -0,0 +1,29 @@
//
// Created by Daniu
//
#include "MovementSystem.h"
#include "entitysystem/ECWorld.h"
#include "entitysystem/components/BasicComponents.h"
#include "utilities/Logs.h"
void MovementSystem::Update(ECWorld& world, float dt)
{
constexpr double damping = 0.90; // 0.0 = instant stop, 1.0 = without damping
world.Each<ECSComponent::Transform, ECSComponent::Velocity>(
[dt](auto entity, auto& transform, auto& velocity)
{
transform.Position += velocity.Value * dt;
velocity.Value *= damping;
if (glm::length(velocity.Value) < 0.001)
{
velocity.Value = glm::dvec3(0.0);
}
}
);
}

View File

@@ -0,0 +1,25 @@
//
// Created by Daniu
//
#ifndef EU07_MOVEMENTSYSTEM_H
#define EU07_MOVEMENTSYSTEM_H
#pragma once
#include "BaseSystem.h"
namespace ECSComponent
{
struct Transform;
struct Velocity;
}
class MovementSystem : public BaseSystem
{
public:
void Update(ECWorld& world, float dt) override;
};
#endif //EU07_MOVEMENTSYSTEM_H

View File

@@ -0,0 +1,33 @@
//
// Created by Daniu
//
#include "SystemManager.h"
#include "entitysystem/systems/BaseSystem.h"
#include "entitysystem/ECWorld.h"
#include "utilities/Logs.h"
SystemManager::~SystemManager() = default;
void SystemManager::Create(ECWorld& world)
{
WriteLog("[SystemManager] Creating systems");
for (auto& system : m_systems){
system->OnCreate(world);
}
}
void SystemManager::Destroy(ECWorld& world)
{
WriteLog("[SystemManager] Destroying systems");
for (auto& system : m_systems)
system->OnDestroy(world);
}
void SystemManager::Update(ECWorld& world, float dt)
{
for (auto& system : m_systems)
system->Update(world, dt);
}

View File

@@ -0,0 +1,46 @@
//
// Created by Daniu
//
#ifndef EU07_SYSTEMMANAGER_H
#define EU07_SYSTEMMANAGER_H
#pragma once
#include "BaseSystem.h"
#include <vector>
#include <memory>
class ECWorld;
class SystemManager
{
public:
SystemManager() = default;
~SystemManager();
template<typename T, typename... Args>
T& AddSystem(Args&&... args)
{
static_assert(std::is_base_of_v<BaseSystem, T>);
auto system = std::make_unique<T>(std::forward<Args>(args)...);
T& ref = *system;
m_systems.emplace_back(std::move(system));
return ref;
}
void Create(ECWorld& world);
void Destroy(ECWorld& world);
void Update(ECWorld& world, float dt);
private:
std::vector<std::unique_ptr<BaseSystem>> m_systems;
};
#endif //EU07_SYSTEMMANAGER_H