Create ECS system base

This commit is contained in:
2026-03-15 00:00:54 +01:00
parent b8f5282a39
commit 2bd4ef81b6
5 changed files with 290 additions and 0 deletions

View File

@@ -11,6 +11,8 @@ endif()
include(CheckCXXSymbolExists) include(CheckCXXSymbolExists)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_FIND_PACKAGE_PREFER_CONFIG TRUE) set(CMAKE_FIND_PACKAGE_PREFER_CONFIG TRUE)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/CMake_modules/") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/CMake_modules/")
@@ -233,6 +235,10 @@ set(SOURCES
"launcher/scenery_scanner.cpp" "launcher/scenery_scanner.cpp"
"vr/vr_interface.cpp" "vr/vr_interface.cpp"
entitysystem/components/BasicComponents.h
entitysystem/ecs.cpp
entitysystem/ecs.h
entitysystem/components/RenderComponents.h
) )
set(DEFINITIONS) set(DEFINITIONS)

View File

@@ -0,0 +1,50 @@
/*
* Basic components used by almost everything
*/
#ifndef EU07_BASICCOMPONENTS_H
#define EU07_BASICCOMPONENTS_H
#include "entt/entity/entity.hpp"
#include "glm/vec3.hpp"
#include "glm/gtc/quaternion.hpp"
#include <string>
#include <cstdint>
namespace ECSComponent
{
///< summary>
/// Basic component for storing transform of entities
///</summary>
struct Transform
{
glm::vec3 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
};
///< 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"
std::uint64_t Id{0}; // id in scene
};
///< summary>
/// Basic component for parent-child relationships between entities
///</summary>
struct Parent
{
entt::entity value{entt::null};
};
///< summary>
/// Empty component for entities that are disabled and should not be processed by systems
///</summary>
struct Disabled
{
};
} // namespace ECSComponent
#endif // EU07_BASICCOMPONENTS_H

View File

@@ -0,0 +1,25 @@
#ifndef EU07_RENDERCOMPONENTS_H
#define EU07_RENDERCOMPONENTS_H
namespace ECSComponent
{
/// <summary>
/// Component for entities that can be rendered.
/// </summary>
/// <remarks>Currently empty
/// TODO: Add component members
/// </remarks>
struct MeshRenderer
{
};
/// <summary>
/// Component for entities that can cast shadows.
/// </summary>
/// <remarks>Currently empty
/// TODO: Add component members
/// </remarks>
struct SpotLight{};
} // namespace ECSComponent
#endif // EU07_RENDERCOMPONENTS_H

61
entitysystem/ecs.cpp Normal file
View File

@@ -0,0 +1,61 @@
//
// 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);
world_.emplace<ECSComponent::Identification>(e);
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;
}

148
entitysystem/ecs.h Normal file
View File

@@ -0,0 +1,148 @@
//
// 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_;
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 &Add(entt::entity entity, Args &&...args)
{
return world_.emplace<T>(entity, std::forward<Args>(args)...);
}
/// <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>
/// <returns>Reference to added component</returns>
template <class T> T &Get(entt::entity entity)
{
return world_.get<T>(entity);
}
/// <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>
/// <returns>Reference to added component</returns>
template <class T> const T &Get(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 *TryGet(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 *TryGet(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 Has(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 Remove(entt::entity entity)
{
world_.remove<T...>(entity);
}
};
#endif // EU07_ECS_H