Rework ECS framework
- Add queue systems when adding/removing components and systems - Add GlobalData to ECSManager, used to store data that is not specific to entities - Add View class, used to for loop all entities that contains the given Components - Rework how signaling works
This commit is contained in:
@@ -430,7 +430,7 @@ namespace Copium
|
||||
int width, height;
|
||||
glfwGetFramebufferSize(window, &width, &height);
|
||||
|
||||
VkExtent2D extent{width, height};
|
||||
VkExtent2D extent{static_cast<uint32_t>(width), static_cast<uint32_t>(height)};
|
||||
extent.width = std::clamp(extent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width);
|
||||
extent.height = std::clamp(extent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height);
|
||||
return extent;
|
||||
|
||||
@@ -19,6 +19,16 @@ namespace Copium
|
||||
std::vector<Component> components;
|
||||
ComponentListener<Component>* listener = nullptr;
|
||||
|
||||
enum class QueueOperation
|
||||
{
|
||||
Add,
|
||||
Remove
|
||||
};
|
||||
|
||||
std::vector<QueueOperation> queueOperationOrder;
|
||||
std::vector<std::pair<EntityId, Component>> addQueue;
|
||||
std::vector<EntityId> removeQueue;
|
||||
|
||||
public:
|
||||
ComponentPool()
|
||||
{
|
||||
@@ -35,34 +45,61 @@ namespace Copium
|
||||
Emplace(entity, component);
|
||||
}
|
||||
|
||||
Component& Emplace(EntityId entity, const Component& component)
|
||||
void Emplace(EntityId entity, const Component& component)
|
||||
{
|
||||
components.push_back(component);
|
||||
entities.Emplace(entity);
|
||||
if (listener)
|
||||
listener->Added(entity, components.back());
|
||||
return components.back();
|
||||
addQueue.emplace_back(entity, component);
|
||||
queueOperationOrder.emplace_back(QueueOperation::Add);
|
||||
}
|
||||
|
||||
bool Erase(EntityId entity) override
|
||||
{
|
||||
size_t index = entities.Find(entity);
|
||||
if (!entities.Erase(entity))
|
||||
if (entities.Find(entity) == entities.Size())
|
||||
return false;
|
||||
if (listener)
|
||||
{
|
||||
auto it = components.begin() + index;
|
||||
Component component = *it;
|
||||
components.erase(it);
|
||||
listener->Removed(entity, component);
|
||||
}
|
||||
else
|
||||
{
|
||||
components.erase(components.begin() + index);
|
||||
}
|
||||
|
||||
removeQueue.emplace_back(entity);
|
||||
queueOperationOrder.emplace_back(QueueOperation::Remove);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CommitUpdates() override
|
||||
{
|
||||
if (queueOperationOrder.empty())
|
||||
return;
|
||||
|
||||
CP_ASSERT(queueOperationOrder.size() == addQueue.size() + removeQueue.size(),
|
||||
"queueOperationOrder size=%zu doesn't match the sum of the addQueue size=%zu and removeQueue size=%zu, "
|
||||
"which is %zu",
|
||||
queueOperationOrder.size(),
|
||||
addQueue.size(),
|
||||
removeQueue.size(),
|
||||
addQueue.size() + removeQueue.size());
|
||||
|
||||
int addQueueIndex = 0;
|
||||
int removeQueueIndex = 0;
|
||||
for (QueueOperation queueOperation : queueOperationOrder)
|
||||
{
|
||||
switch (queueOperation)
|
||||
{
|
||||
case QueueOperation::Add:
|
||||
{
|
||||
CommitAddComponent(addQueueIndex);
|
||||
addQueueIndex++;
|
||||
break;
|
||||
}
|
||||
case QueueOperation::Remove:
|
||||
{
|
||||
CommitRemoveComponent(removeQueueIndex);
|
||||
removeQueueIndex++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
removeQueue.clear();
|
||||
addQueue.clear();
|
||||
queueOperationOrder.clear();
|
||||
}
|
||||
|
||||
Component& At(size_t index)
|
||||
{
|
||||
return operator[](index);
|
||||
@@ -111,5 +148,56 @@ namespace Copium
|
||||
{
|
||||
return components.end();
|
||||
}
|
||||
|
||||
void CommitAddComponent(int queueIndex)
|
||||
{
|
||||
CP_ASSERT(
|
||||
queueIndex < addQueue.size(), "queueIndex=%d is greater than the addQueueSize=%d", queueIndex, addQueue.size());
|
||||
|
||||
const auto& [entity, component] = addQueue[queueIndex];
|
||||
// TODO: Debugging errors caused by this assert might be a bit difficult, since there wont be any stacktrace for
|
||||
// where the component was added. Might want to validate this in AddComponent somehow (like looping through
|
||||
// the queued changes and work out if the component already exists)
|
||||
CP_ASSERT(Find(entity) == Size(),
|
||||
"Component already exists in entity (entity=%u, Component=%s)",
|
||||
entity,
|
||||
typeid(Component).name());
|
||||
|
||||
components.push_back(component);
|
||||
entities.Emplace(entity);
|
||||
if (listener)
|
||||
listener->Added(entity, components.back());
|
||||
}
|
||||
|
||||
void CommitRemoveComponent(int queueIndex)
|
||||
{
|
||||
CP_ASSERT(queueIndex < removeQueue.size(),
|
||||
"queueIndex=%d is greater than the removeQueueSize=%d",
|
||||
queueIndex,
|
||||
removeQueue.size());
|
||||
|
||||
const auto& entity = removeQueue[queueIndex];
|
||||
size_t index = entities.Find(entity);
|
||||
if (!entities.Erase(entity))
|
||||
{
|
||||
// TODO: Debugging warnings caused by this might be a bit difficult, since there wont be any stacktrace for
|
||||
// where the component was added. Might want to validate this in AddComponent somehow (like looping
|
||||
// through the queued changes and work out if the component already exists)
|
||||
CP_WARN("Entity did not contain component (entity=%u, Component=%s)", entity, typeid(Component).name());
|
||||
return;
|
||||
}
|
||||
|
||||
if (listener)
|
||||
{
|
||||
auto it = components.begin() + index;
|
||||
Component component = *it;
|
||||
components.erase(it);
|
||||
listener->Removed(entity, component);
|
||||
}
|
||||
else
|
||||
{
|
||||
components.erase(components.begin() + index);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ namespace Copium
|
||||
|
||||
virtual size_t Size() = 0;
|
||||
virtual bool Erase(EntityId entity) = 0;
|
||||
virtual void CommitUpdates() = 0;
|
||||
std::vector<EntityId>& GetEntities();
|
||||
const std::vector<EntityId>& GetEntities() const;
|
||||
};
|
||||
|
||||
@@ -4,30 +4,37 @@
|
||||
|
||||
namespace Copium
|
||||
{
|
||||
std::vector<EntityId> ECSManager::emptyEntities = {};
|
||||
|
||||
ECSManager::ECSManager()
|
||||
: systemPool{std::make_unique<SystemPool>(this)}
|
||||
{
|
||||
}
|
||||
|
||||
ECSManager::~ECSManager()
|
||||
{
|
||||
for (auto&& components : componentPool)
|
||||
for (auto&& components : componentPools)
|
||||
{
|
||||
delete components.second;
|
||||
}
|
||||
componentPool.clear();
|
||||
componentPools.clear();
|
||||
}
|
||||
|
||||
void ECSManager::UpdateSystems()
|
||||
void ECSManager::CommitEntityUpdates()
|
||||
{
|
||||
systemPool->Update();
|
||||
for (auto& componentPool : componentPools)
|
||||
{
|
||||
componentPool.second->CommitUpdates();
|
||||
}
|
||||
}
|
||||
|
||||
void ECSManager::UpdateSystems(const Signal& signal)
|
||||
void ECSManager::UpdateSystems(const Uuid& systemPoolId)
|
||||
{
|
||||
// TODO: Maybe we want a different pool for Signal based Systems for performance reasons?
|
||||
// Maybe even a pool for each type of Signal?
|
||||
systemPool->Update(signal);
|
||||
auto it = systemPools.find(systemPoolId);
|
||||
CP_ASSERT(it != systemPools.end(), "SystemPool doesn't exist with Uuid=%s", systemPoolId.ToString().c_str());
|
||||
it->second->CommitSignals();
|
||||
CommitEntityUpdates();
|
||||
it->second->CommitUpdates();
|
||||
it->second->Update();
|
||||
}
|
||||
|
||||
size_t ECSManager::GetEntityCount() const
|
||||
@@ -37,6 +44,13 @@ namespace Copium
|
||||
|
||||
EntityId ECSManager::CreateEntity()
|
||||
{
|
||||
if (!destroyedEntityIds.empty())
|
||||
{
|
||||
EntityId newId = *destroyedEntityIds.begin();
|
||||
destroyedEntityIds.erase(destroyedEntityIds.begin());
|
||||
return newId;
|
||||
}
|
||||
|
||||
CP_ASSERT(currentEntityId != MAX_NUM_ENTITIES, "No more entities available");
|
||||
entities.emplace(currentEntityId);
|
||||
currentEntityId++;
|
||||
@@ -47,8 +61,21 @@ namespace Copium
|
||||
{
|
||||
auto it = entities.find(entity);
|
||||
CP_ASSERT(it != entities.end(), "Entity does not exist in ECSManager (entity=%u)", entity);
|
||||
if (entity == currentEntityId - 1)
|
||||
{
|
||||
currentEntityId--;
|
||||
while (!destroyedEntityIds.empty() && *destroyedEntityIds.rbegin() == currentEntityId - 1)
|
||||
{
|
||||
destroyedEntityIds.erase(std::prev(destroyedEntityIds.end()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
destroyedEntityIds.emplace(entity);
|
||||
}
|
||||
|
||||
entities.erase(it);
|
||||
for (auto&& pool : componentPool)
|
||||
for (auto&& pool : componentPools)
|
||||
{
|
||||
pool.second->Erase(entity);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <typeindex>
|
||||
#include <unordered_set>
|
||||
|
||||
@@ -10,36 +11,68 @@
|
||||
#include "copium/ecs/Signal.h"
|
||||
#include "copium/ecs/SystemPool.h"
|
||||
#include "copium/util/Common.h"
|
||||
#include "copium/util/GenericType.h"
|
||||
#include "copium/util/Uuid.h"
|
||||
|
||||
namespace Copium
|
||||
{
|
||||
class ECSManager final
|
||||
{
|
||||
CP_DELETE_COPY_AND_MOVE_CTOR(ECSManager);
|
||||
|
||||
private:
|
||||
std::unordered_set<EntityId> entities;
|
||||
std::map<std::type_index, ComponentPoolBase*> componentPool;
|
||||
std::map<std::type_index, ComponentPoolBase*> componentPools;
|
||||
|
||||
std::unique_ptr<SystemPool> systemPool;
|
||||
int currentEntityId = 1;
|
||||
std::map<Uuid, std::unique_ptr<SystemPool>> systemPools;
|
||||
EntityId currentEntityId = 1;
|
||||
std::set<EntityId> destroyedEntityIds;
|
||||
|
||||
std::map<std::type_index, GenericType> globalDatas;
|
||||
|
||||
public:
|
||||
static std::vector<EntityId> emptyEntities;
|
||||
|
||||
ECSManager();
|
||||
~ECSManager();
|
||||
|
||||
template <typename SystemClass, typename... Args>
|
||||
SystemOrderer AddSystem(const Args&... args)
|
||||
SystemOrderer& AddSystem(const Uuid& systemPoolId)
|
||||
{
|
||||
return systemPool->AddSystem(typeid(SystemClass), new SystemClass{args...});
|
||||
auto it = systemPools.find(systemPoolId);
|
||||
if (it == systemPools.end())
|
||||
it = systemPools.emplace(systemPoolId, std::make_unique<SystemPool>(this)).first;
|
||||
|
||||
return it->second->AddSystem(typeid(SystemClass), new SystemClass{});
|
||||
}
|
||||
|
||||
template <typename SystemClass>
|
||||
void RemoveSystem()
|
||||
void RemoveSystem(const Uuid& systemPoolId)
|
||||
{
|
||||
systemPool->RemoveSystem(typeid(SystemClass));
|
||||
auto it = systemPools.find(systemPoolId);
|
||||
CP_ASSERT(it != systemPools.end(), "SystemPool doesn't exist with Uuid=%s", systemPoolId.ToString().c_str());
|
||||
|
||||
it->second->RemoveSystem(typeid(SystemClass));
|
||||
|
||||
if (it->second->IsEmpty())
|
||||
systemPools.erase(it);
|
||||
}
|
||||
|
||||
void UpdateSystems();
|
||||
void UpdateSystems(const Signal& signal);
|
||||
void CommitEntityUpdates();
|
||||
|
||||
void UpdateSystems(const Uuid& systemPoolId);
|
||||
|
||||
template <typename S, typename... Args>
|
||||
void SendSignal(const Args&... args)
|
||||
{
|
||||
Signal::ValidateSignal<S>();
|
||||
for (auto& systemPool : systemPools)
|
||||
{
|
||||
S* newSignal = new S{args...};
|
||||
newSignal->uuid = S::UUID;
|
||||
systemPool.second->SendSignal(newSignal);
|
||||
}
|
||||
}
|
||||
|
||||
EntityId CreateEntity();
|
||||
void DestroyEntity(EntityId entity);
|
||||
@@ -61,41 +94,36 @@ namespace Copium
|
||||
else
|
||||
{
|
||||
ComponentPool<Component>* pool{new ComponentPool<Component>{}};
|
||||
componentPool.emplace(GetComponentId<Component>(), pool);
|
||||
componentPools.emplace(GetComponentId<Component>(), pool);
|
||||
pool->SetComponentListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... Components>
|
||||
std::tuple<Components&...> AddComponents(EntityId entity, Components&&... components)
|
||||
void AddComponents(EntityId entity, Components&&... components)
|
||||
{
|
||||
return std::forward_as_tuple(AddComponent(entity, Components(components))...);
|
||||
(AddComponent(entity, components), ...);
|
||||
}
|
||||
|
||||
template <typename Component, typename... Args>
|
||||
Component& AddComponent(EntityId entity, Args&&... args)
|
||||
void AddComponent(EntityId entity, Args&&... args)
|
||||
{
|
||||
return AddComponent(entity, Component{args...});
|
||||
AddComponent(entity, Component{args...});
|
||||
}
|
||||
|
||||
template <typename Component>
|
||||
Component& AddComponent(EntityId entity, const Component& component)
|
||||
void AddComponent(EntityId entity, const Component& component)
|
||||
{
|
||||
auto pool = GetComponentPool<Component>();
|
||||
|
||||
if (pool)
|
||||
{
|
||||
CP_ASSERT(!HasComponent<Component>(entity),
|
||||
"Component already exists in entity (entity=%u, Component=%s)",
|
||||
entity,
|
||||
typeid(Component).name());
|
||||
return pool->Emplace(entity, component);
|
||||
pool->Emplace(entity, component);
|
||||
}
|
||||
else
|
||||
{
|
||||
ComponentPool<Component>* pool{new ComponentPool{entity, component}};
|
||||
auto ret = componentPool.emplace(GetComponentId<Component>(), pool);
|
||||
return pool->At(0);
|
||||
auto ret = componentPools.emplace(GetComponentId<Component>(), pool);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,10 +131,7 @@ namespace Copium
|
||||
void RemoveComponent(EntityId entity)
|
||||
{
|
||||
auto pool = GetComponentPoolAssure<Component>();
|
||||
CP_ASSERT(pool->Erase(entity),
|
||||
"Entity did not contain component (entity=%u, Component=%s)",
|
||||
entity,
|
||||
typeid(Component).name());
|
||||
pool->Erase(entity);
|
||||
}
|
||||
|
||||
template <typename... Components>
|
||||
@@ -229,22 +254,54 @@ namespace Copium
|
||||
return std::type_index(typeid(T));
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename Component>
|
||||
ComponentPool<Component>* GetComponentPool()
|
||||
template <typename T, typename... Args>
|
||||
void AddGlobalData(const Args&... args)
|
||||
{
|
||||
auto it = componentPool.find(GetComponentId<Component>());
|
||||
return it == componentPool.end() ? nullptr : static_cast<ComponentPool<Component>*>(it->second);
|
||||
auto it = globalDatas.find(typeid(T));
|
||||
CP_ASSERT(!HasGlobalData<T>(), "Global with typeid=%s already exists. Do nothing", typeid(T).name());
|
||||
|
||||
globalDatas.emplace(typeid(T), GenericType::Create<T>(args...));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T& GetGlobalData()
|
||||
{
|
||||
auto it = globalDatas.find(typeid(T));
|
||||
CP_ASSERT(it != globalDatas.end(), "Global with typeid=%s doesn't exist");
|
||||
|
||||
return it->second.Get<T>();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool HasGlobalData()
|
||||
{
|
||||
return globalDatas.find(typeid(T)) != globalDatas.end();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void RemoveGlobalData()
|
||||
{
|
||||
auto it = globalDatas.find(typeid(T));
|
||||
CP_ASSERT("Global with typeid=%s doesn't exist. Do nothing", typeid(T).name());
|
||||
globalDatas.erase(it);
|
||||
}
|
||||
|
||||
template <typename Component>
|
||||
ComponentPool<Component>* GetComponentPoolAssure()
|
||||
ComponentPool<std::remove_const_t<Component>>* GetComponentPool()
|
||||
{
|
||||
auto it = componentPool.find(GetComponentId<Component>());
|
||||
CP_ASSERT(it != componentPool.end(),
|
||||
auto it = componentPools.find(GetComponentId<Component>());
|
||||
return it == componentPools.end() ? nullptr
|
||||
: static_cast<ComponentPool<std::remove_const_t<Component>>*>(it->second);
|
||||
}
|
||||
|
||||
template <typename Component>
|
||||
ComponentPool<std::remove_const_t<Component>>* GetComponentPoolAssure()
|
||||
{
|
||||
auto it = componentPools.find(GetComponentId<Component>());
|
||||
CP_ASSERT(it != componentPools.end(),
|
||||
"Component has not been added to an entity (Component=%s)",
|
||||
typeid(Component).name());
|
||||
return static_cast<ComponentPool<Component>*>(it->second);
|
||||
return static_cast<ComponentPool<std::remove_const_t<Component>>*>(it->second);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace Copium
|
||||
return id != entity.id;
|
||||
}
|
||||
|
||||
Entity::operator bool() const
|
||||
bool Entity::IsValid() const
|
||||
{
|
||||
if (id == INVALID_ENTITY)
|
||||
return false;
|
||||
@@ -49,6 +49,11 @@ namespace Copium
|
||||
return false;
|
||||
}
|
||||
|
||||
Entity::operator bool() const
|
||||
{
|
||||
return IsValid();
|
||||
}
|
||||
|
||||
void Entity::Invalidate()
|
||||
{
|
||||
id = INVALID_ENTITY;
|
||||
@@ -79,4 +84,4 @@ namespace Copium
|
||||
{
|
||||
return {manager, manager->CreateEntity()};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ namespace Copium
|
||||
bool operator!=(const Entity& entity);
|
||||
operator bool() const;
|
||||
|
||||
bool IsValid() const;
|
||||
void Invalidate();
|
||||
void Destroy();
|
||||
void SetId(EntityId entityId);
|
||||
@@ -33,15 +34,15 @@ namespace Copium
|
||||
static Entity Create(ECSManager* manager);
|
||||
|
||||
template <typename Component, typename... Args>
|
||||
inline Component& AddComponent(Args... args)
|
||||
inline void AddComponent(Args... args)
|
||||
{
|
||||
return manager->AddComponent<Component>(id, args...);
|
||||
manager->AddComponent<Component>(id, args...);
|
||||
}
|
||||
|
||||
template <typename... Components>
|
||||
std::tuple<Components&...> AddComponents(Components&&... components)
|
||||
void AddComponents(Components&&... components)
|
||||
{
|
||||
return manager->AddComponents(id, components...);
|
||||
manager->AddComponents(id, components...);
|
||||
}
|
||||
|
||||
template <typename Component>
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
#include "copium/ecs/Signal.h"
|
||||
|
||||
namespace Copium
|
||||
{
|
||||
int Signal::GetAllocatedId()
|
||||
{
|
||||
allocatedIds++;
|
||||
return allocatedIds;
|
||||
}
|
||||
}
|
||||
@@ -1,48 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#define CP_SIGNAL_DECLERATION_DEFINITION() \
|
||||
static int GetIdStatic() \
|
||||
{ \
|
||||
static int id = GetAllocatedId(); \
|
||||
return id; \
|
||||
} \
|
||||
\
|
||||
int GetId() const \
|
||||
{ \
|
||||
return GetIdStatic(); \
|
||||
}
|
||||
#include "copium/util/Uuid.h"
|
||||
|
||||
#define CP_SIGNAL_DECLERATION(SignalClass) \
|
||||
static int GetIdStatic(); \
|
||||
int GetId() const override
|
||||
|
||||
#define CP_SIGNAL_DEFINITION(SignalClass) \
|
||||
int SignalClass::GetIdStatic() \
|
||||
{ \
|
||||
static int id = GetAllocatedId(); \
|
||||
return id; \
|
||||
} \
|
||||
\
|
||||
int SignalClass::GetId() const \
|
||||
{ \
|
||||
return GetIdStatic(); \
|
||||
}
|
||||
#define CP_REGISTER_SIGNAL static inline Copium::Uuid UUID = Copium::Uuid()
|
||||
|
||||
namespace Copium
|
||||
{
|
||||
class Signal
|
||||
{
|
||||
private:
|
||||
static inline int allocatedIds = 0;
|
||||
|
||||
public:
|
||||
Signal() = default;
|
||||
virtual ~Signal() = default;
|
||||
|
||||
virtual int GetId() const = 0;
|
||||
const Uuid& GetUuid() const
|
||||
{
|
||||
return uuid;
|
||||
}
|
||||
|
||||
protected:
|
||||
static int GetAllocatedId();
|
||||
template <typename T>
|
||||
static void ValidateSignal()
|
||||
{
|
||||
static_assert(std::is_base_of_v<Signal, T>, "ValidateSignal : Given T is does not have Signal as base class");
|
||||
static_assert(
|
||||
has_static_uuid<T>(),
|
||||
"ValidateSignal : Given T has not defined a UUID. Did you add CP_REGISTER_SIGNAL to your Signal class?");
|
||||
}
|
||||
|
||||
private:
|
||||
friend class ECSManager;
|
||||
Uuid uuid;
|
||||
|
||||
template <typename, typename = void>
|
||||
struct has_static_uuid : std::false_type
|
||||
{
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct has_static_uuid<T, std::void_t<decltype(T::UUID)>> : std::true_type
|
||||
{
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -3,44 +3,54 @@
|
||||
#include <set>
|
||||
|
||||
#include "copium/ecs/ECSManager.h"
|
||||
#include "copium/ecs/Entity.h"
|
||||
#include "copium/ecs/SystemBase.h"
|
||||
#include "copium/ecs/Signal.h"
|
||||
|
||||
namespace Copium
|
||||
{
|
||||
template <typename... Components>
|
||||
class System : public SystemBase
|
||||
class System
|
||||
{
|
||||
public:
|
||||
void Run() override
|
||||
virtual ~System() = default;
|
||||
virtual void Run() = 0;
|
||||
|
||||
virtual void HandleSignal(const Signal& signal)
|
||||
{
|
||||
manager->Each<Components...>([&](EntityId entityId, Components&... components)
|
||||
{ RunEntity(Entity{manager, entityId}, components...); });
|
||||
}
|
||||
|
||||
void Run(const Signal& signal) override
|
||||
const bool IsSignalSubscribed(const Uuid& uuid) const
|
||||
{
|
||||
manager->Each<Components...>([&](EntityId entityId, Components&... components)
|
||||
{ RunEntity(signal, Entity{manager, entityId}, components...); });
|
||||
return subscribedSignals.count(uuid) != 0;
|
||||
}
|
||||
|
||||
virtual void RunEntity(Entity entity, Components&... components) {};
|
||||
virtual void RunEntity(const Signal& signal, Entity entity, Components&... components) {};
|
||||
|
||||
// TODO: Not sure if this is the way entities should be validated
|
||||
std::set<EntityId> loggedEntities;
|
||||
|
||||
bool ValidateEntity(Entity entity)
|
||||
template <typename S>
|
||||
void SubscribeToSignal()
|
||||
{
|
||||
if (entity && entity.HasComponents<Components...>())
|
||||
{
|
||||
loggedEntities.erase(entity);
|
||||
return true;
|
||||
}
|
||||
if (loggedEntities.find(entity) == loggedEntities.end())
|
||||
CP_WARN("Invalid Entity");
|
||||
loggedEntities.emplace(entity);
|
||||
return false;
|
||||
Signal::ValidateSignal<S>();
|
||||
subscribedSignals.emplace(S::UUID);
|
||||
}
|
||||
|
||||
template <typename S>
|
||||
void UnsubscribeToSignal()
|
||||
{
|
||||
Signal::ValidateSignal<S>();
|
||||
subscribedSignals.erase(S::UUID);
|
||||
}
|
||||
|
||||
void SetECSManager(ECSManager* manager)
|
||||
{
|
||||
this->manager = manager;
|
||||
}
|
||||
|
||||
template <typename S, typename... Args>
|
||||
void SendSignal(const Args&... args)
|
||||
{
|
||||
manager->SendSignal<S>(args...);
|
||||
}
|
||||
|
||||
protected:
|
||||
ECSManager* manager;
|
||||
|
||||
private:
|
||||
std::set<Uuid> subscribedSignals{};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "copium/ecs/Signal.h"
|
||||
|
||||
namespace Copium
|
||||
{
|
||||
class ECSManager;
|
||||
|
||||
class SystemBase
|
||||
{
|
||||
friend class SystemPool;
|
||||
|
||||
protected:
|
||||
ECSManager* manager;
|
||||
|
||||
public:
|
||||
virtual ~SystemBase() = default;
|
||||
|
||||
virtual void Run() = 0;
|
||||
virtual void Run(const Signal& signal) = 0;
|
||||
};
|
||||
}
|
||||
@@ -10,13 +10,34 @@ namespace Copium
|
||||
{
|
||||
}
|
||||
|
||||
void SystemOrderer::Before(const std::type_index& otherSystemId)
|
||||
void SystemOrderer::CommitOrdering()
|
||||
{
|
||||
for (const auto& [orderOperation, systemId] : orderQueue)
|
||||
{
|
||||
switch (orderOperation)
|
||||
{
|
||||
case OrderOperation::Before:
|
||||
{
|
||||
CommitBefore(systemId);
|
||||
break;
|
||||
}
|
||||
case OrderOperation::After:
|
||||
{
|
||||
CommitAfter(systemId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
orderQueue.clear();
|
||||
}
|
||||
|
||||
void SystemOrderer::CommitBefore(const std::type_index& otherSystemId)
|
||||
{
|
||||
systemPool->MoveSystemBefore(systemId, otherSystemId);
|
||||
}
|
||||
|
||||
void SystemOrderer::After(const std::type_index& otherSystemId)
|
||||
void SystemOrderer::CommitAfter(const std::type_index& otherSystemId)
|
||||
{
|
||||
systemPool->MoveSystemAfter(systemId, otherSystemId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <typeindex>
|
||||
#include <vector>
|
||||
|
||||
namespace Copium
|
||||
{
|
||||
@@ -8,27 +9,37 @@ namespace Copium
|
||||
|
||||
class SystemOrderer
|
||||
{
|
||||
private:
|
||||
std::type_index systemId;
|
||||
SystemPool* systemPool = nullptr;
|
||||
|
||||
public:
|
||||
SystemOrderer(std::type_index systemId, SystemPool* systemPool);
|
||||
|
||||
template <typename Other>
|
||||
template <typename System>
|
||||
void Before()
|
||||
{
|
||||
Before(typeid(Other));
|
||||
orderQueue.emplace_back(OrderOperation::Before, typeid(System));
|
||||
}
|
||||
|
||||
template <typename Other>
|
||||
template <typename System>
|
||||
void After()
|
||||
{
|
||||
After(typeid(Other));
|
||||
orderQueue.emplace_back(OrderOperation::After, typeid(System));
|
||||
}
|
||||
|
||||
private:
|
||||
void Before(const std::type_index& otherSystemId);
|
||||
void After(const std::type_index& otherSystemId);
|
||||
enum class OrderOperation
|
||||
{
|
||||
Before,
|
||||
After
|
||||
};
|
||||
|
||||
std::type_index systemId;
|
||||
SystemPool* systemPool = nullptr;
|
||||
std::vector<std::pair<OrderOperation, std::type_index>> orderQueue;
|
||||
|
||||
private:
|
||||
friend class SystemPool;
|
||||
|
||||
void CommitOrdering();
|
||||
void CommitBefore(const std::type_index& otherSystemId);
|
||||
void CommitAfter(const std::type_index& otherSystemId);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "copium/ecs/System.h"
|
||||
|
||||
namespace Copium
|
||||
{
|
||||
SystemPool::SystemPool(ECSManager* manager)
|
||||
@@ -19,28 +21,56 @@ namespace Copium
|
||||
systems.clear();
|
||||
}
|
||||
|
||||
SystemOrderer SystemPool::AddSystem(const std::type_index& systemId, SystemBase* system)
|
||||
SystemOrderer& SystemPool::AddSystem(const std::type_index& systemId, System* system)
|
||||
{
|
||||
system->manager = manager;
|
||||
CP_ASSERT(systems.find(systemId) == systems.end(), "System already exist in Ecs");
|
||||
systems.emplace(systemId, system);
|
||||
systemOrder.emplace_back(system);
|
||||
return SystemOrderer{systemId, this};
|
||||
system->SetECSManager(manager);
|
||||
addQueue.emplace_back(systemId, system, SystemOrderer{systemId, this});
|
||||
queueOperationOrder.emplace_back(QueueOperation::Add);
|
||||
return std::get<2>(addQueue.back());
|
||||
}
|
||||
|
||||
void SystemPool::RemoveSystem(const std::type_index& systemId)
|
||||
{
|
||||
auto it = systems.find(systemId);
|
||||
if (it == systems.end())
|
||||
{
|
||||
CP_WARN("System does not exist in Ecs");
|
||||
return;
|
||||
}
|
||||
removeQueue.emplace_back(systemId);
|
||||
queueOperationOrder.emplace_back(QueueOperation::Remove);
|
||||
}
|
||||
|
||||
auto itOrder = std::find(systemOrder.begin(), systemOrder.end(), it->second);
|
||||
delete it->second;
|
||||
systems.erase(it);
|
||||
systemOrder.erase(itOrder);
|
||||
void SystemPool::CommitUpdates()
|
||||
{
|
||||
if (queueOperationOrder.empty())
|
||||
return;
|
||||
|
||||
CP_ASSERT(queueOperationOrder.size() == addQueue.size() + removeQueue.size(),
|
||||
"queueOperationOrder size=%zu doesn't match the sum of the addQueue size=%zu and removeQueue size=%zu, "
|
||||
"which is %zu",
|
||||
queueOperationOrder.size(),
|
||||
addQueue.size(),
|
||||
removeQueue.size(),
|
||||
addQueue.size() + removeQueue.size());
|
||||
|
||||
int addQueueIndex = 0;
|
||||
int removeQueueIndex = 0;
|
||||
for (QueueOperation queueOperation : queueOperationOrder)
|
||||
{
|
||||
switch (queueOperation)
|
||||
{
|
||||
case QueueOperation::Add:
|
||||
{
|
||||
CommitAddSystem(addQueueIndex);
|
||||
addQueueIndex++;
|
||||
break;
|
||||
}
|
||||
case QueueOperation::Remove:
|
||||
{
|
||||
CommitRemoveSystem(removeQueueIndex);
|
||||
removeQueueIndex++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
removeQueue.clear();
|
||||
addQueue.clear();
|
||||
queueOperationOrder.clear();
|
||||
}
|
||||
|
||||
void SystemPool::Update()
|
||||
@@ -51,20 +81,44 @@ namespace Copium
|
||||
}
|
||||
}
|
||||
|
||||
void SystemPool::Update(const Signal& signal)
|
||||
void SystemPool::SendSignal(Signal* signal)
|
||||
{
|
||||
for (auto& system : systemOrder)
|
||||
signalQueue.emplace(signal);
|
||||
}
|
||||
|
||||
void SystemPool::CommitSignals()
|
||||
{
|
||||
while (!signalQueue.empty())
|
||||
{
|
||||
system->Run(signal);
|
||||
auto& signal = signalQueue.front();
|
||||
for (auto& system : systemOrder)
|
||||
{
|
||||
if (system->IsSignalSubscribed(signal->GetUuid()))
|
||||
{
|
||||
system->HandleSignal(*signal);
|
||||
}
|
||||
}
|
||||
delete signal;
|
||||
signalQueue.pop();
|
||||
}
|
||||
}
|
||||
|
||||
size_t SystemPool::Size() const
|
||||
{
|
||||
return systemOrder.size();
|
||||
}
|
||||
|
||||
bool SystemPool::IsEmpty() const
|
||||
{
|
||||
return systemOrder.empty();
|
||||
}
|
||||
|
||||
void SystemPool::MoveSystemAfter(const std::type_index& systemId, const std::type_index& afterSystemId)
|
||||
{
|
||||
auto it1 = systems.find(systemId);
|
||||
CP_ASSERT(it1 != systems.end(), "System does not exist in SystemPool");
|
||||
CP_ASSERT(it1 != systems.end(), "System with typeid=%s does not exist in SystemPool", systemId.name());
|
||||
auto it2 = systems.find(afterSystemId);
|
||||
CP_ASSERT(it2 != systems.end(), "System does not exist in SystemPool");
|
||||
CP_ASSERT(it2 != systems.end(), "System with typeid=%s does not exist in SystemPool", afterSystemId.name());
|
||||
|
||||
auto itSystemId = std::find(systemOrder.rbegin(), systemOrder.rend(), it1->second);
|
||||
auto itAfterSystemId = std::find(systemOrder.rbegin(), systemOrder.rend(), it2->second);
|
||||
@@ -74,13 +128,45 @@ namespace Copium
|
||||
void SystemPool::MoveSystemBefore(const std::type_index& systemId, const std::type_index& beforeSystemId)
|
||||
{
|
||||
auto it1 = systems.find(systemId);
|
||||
CP_ASSERT(it1 != systems.end(), "System does not exist in SystemPool");
|
||||
CP_ASSERT(it1 != systems.end(), "System with typeid=%s does not exist in SystemPool", systemId.name());
|
||||
auto it2 = systems.find(beforeSystemId);
|
||||
CP_ASSERT(it2 != systems.end(), "System does not exist in SystemPool");
|
||||
CP_ASSERT(it2 != systems.end(), "System with typeid=%s does not exist in SystemPool", beforeSystemId.name());
|
||||
|
||||
auto itSystemId = std::find(systemOrder.rbegin(), systemOrder.rend(), it1->second);
|
||||
auto itBeforeSystemId = std::find(systemOrder.rbegin(), systemOrder.rend(), it2->second);
|
||||
|
||||
std::rotate(itSystemId, itSystemId + 1, itBeforeSystemId + 1);
|
||||
}
|
||||
|
||||
void SystemPool::CommitAddSystem(int queueIndex)
|
||||
{
|
||||
CP_ASSERT(
|
||||
queueIndex < addQueue.size(), "queueIndex=%d is greater than the addQueueSize=%d", queueIndex, addQueue.size());
|
||||
|
||||
auto& [systemId, system, ordering] = addQueue[queueIndex];
|
||||
CP_ASSERT(systems.find(systemId) == systems.end(), "System with typeid=%s already exists in SystemPool");
|
||||
|
||||
systems.emplace(systemId, system);
|
||||
systemOrder.emplace_back(system);
|
||||
ordering.CommitOrdering();
|
||||
}
|
||||
|
||||
void SystemPool::CommitRemoveSystem(int queueIndex)
|
||||
{
|
||||
CP_ASSERT(queueIndex < removeQueue.size(),
|
||||
"queueIndex=%d is greater than the removeQueueSize=%d",
|
||||
queueIndex,
|
||||
removeQueue.size());
|
||||
|
||||
const auto& systemId = removeQueue[queueIndex];
|
||||
auto it = systems.find(systemId);
|
||||
CP_ASSERT(it != systems.end(), "System with typeid=%s does not exist in SystemPool", systemId.name());
|
||||
|
||||
auto itOrder = std::find(systemOrder.begin(), systemOrder.end(), it->second);
|
||||
CP_ASSERT(itOrder != systemOrder.end(), "System with typeid=%s does not exist in systemOrder", systemId.name());
|
||||
|
||||
delete it->second;
|
||||
systems.erase(it);
|
||||
systemOrder.erase(itOrder);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <queue>
|
||||
#include <typeindex>
|
||||
#include <vector>
|
||||
|
||||
#include "copium/ecs/Signal.h"
|
||||
#include "copium/ecs/SystemBase.h"
|
||||
#include "copium/ecs/SystemOrderer.h"
|
||||
#include "copium/util/Common.h"
|
||||
|
||||
namespace Copium
|
||||
{
|
||||
class ECSManager;
|
||||
class System;
|
||||
|
||||
class SystemPool final
|
||||
{
|
||||
CP_DELETE_COPY_AND_MOVE_CTOR(SystemPool);
|
||||
|
||||
private:
|
||||
ECSManager* manager;
|
||||
std::map<std::type_index, SystemBase*> systems;
|
||||
std::vector<SystemBase*> systemOrder;
|
||||
|
||||
public:
|
||||
SystemPool(ECSManager* manager);
|
||||
~SystemPool();
|
||||
|
||||
SystemOrderer AddSystem(const std::type_index& systemId, SystemBase* system);
|
||||
SystemOrderer& AddSystem(const std::type_index& systemId, System* system);
|
||||
void RemoveSystem(const std::type_index& systemId);
|
||||
void Update();
|
||||
void Update(const Signal& signal);
|
||||
void SendSignal(Signal* signal);
|
||||
void CommitSignals();
|
||||
void CommitUpdates();
|
||||
|
||||
size_t Size() const;
|
||||
bool IsEmpty() const;
|
||||
|
||||
void MoveSystemAfter(const std::type_index& systemId, const std::type_index& afterSystemId);
|
||||
void MoveSystemBefore(const std::type_index& systemId, const std::type_index& beforeSystemId);
|
||||
|
||||
private:
|
||||
ECSManager* manager;
|
||||
std::map<std::type_index, System*> systems;
|
||||
std::vector<System*> systemOrder;
|
||||
|
||||
enum class QueueOperation
|
||||
{
|
||||
Add,
|
||||
Remove
|
||||
};
|
||||
|
||||
std::vector<QueueOperation> queueOperationOrder;
|
||||
std::vector<std::tuple<std::type_index, System*, SystemOrderer>> addQueue;
|
||||
std::vector<std::type_index> removeQueue;
|
||||
std::queue<Signal*> signalQueue;
|
||||
|
||||
void CommitAddSystem(int queueIndex);
|
||||
void CommitRemoveSystem(int queueIndex);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
#pragma once
|
||||
|
||||
#include "copium/ecs/ECSManager.h"
|
||||
#include "copium/ecs/Entity.h"
|
||||
|
||||
namespace Copium
|
||||
{
|
||||
template <typename Component, typename... Components>
|
||||
struct View
|
||||
{
|
||||
class Iterator
|
||||
{
|
||||
public:
|
||||
std::tuple<Entity, Component&, Components&...> operator*()
|
||||
{
|
||||
EntityId entityId{entities[index]};
|
||||
CP_ASSERT((manager->HasComponents<Component, Components...>(entityId)),
|
||||
"entity doesn't contains all components");
|
||||
return std::forward_as_tuple<Entity, Component&, Components&...>(
|
||||
Entity{manager, entityId}, firstPool->At(index), manager->GetComponent<Components>(entityId)...);
|
||||
}
|
||||
|
||||
Iterator& operator++()
|
||||
{
|
||||
++index;
|
||||
FindNextEntity();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Iterator operator++(int)
|
||||
{
|
||||
Iterator it = *this;
|
||||
++*this;
|
||||
return it;
|
||||
}
|
||||
|
||||
Iterator operator+(size_t advance)
|
||||
{
|
||||
Iterator it = *this;
|
||||
for (int i = 0; i < advance; i++)
|
||||
++it;
|
||||
return it;
|
||||
}
|
||||
|
||||
bool operator==(const Iterator& other) const
|
||||
{
|
||||
return index == other.index;
|
||||
}
|
||||
|
||||
bool operator!=(const Iterator& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class View;
|
||||
|
||||
ECSManager* manager;
|
||||
ComponentPool<std::remove_const_t<Component>>* firstPool;
|
||||
const std::vector<EntityId>& entities;
|
||||
int index;
|
||||
|
||||
Iterator(ECSManager* manager, ComponentPool<std::remove_const_t<Component>>* firstPool)
|
||||
: manager{manager},
|
||||
firstPool{firstPool},
|
||||
entities{firstPool ? firstPool->GetEntities() : ECSManager::emptyEntities},
|
||||
index{0}
|
||||
{
|
||||
}
|
||||
|
||||
static Iterator Begin(ECSManager* manager)
|
||||
{
|
||||
Iterator iterator{manager, manager->GetComponentPool<Component>()};
|
||||
iterator.FindNextEntity();
|
||||
return iterator;
|
||||
}
|
||||
|
||||
static Iterator End(ECSManager* manager)
|
||||
{
|
||||
Iterator iterator{manager, manager->GetComponentPool<Component>()};
|
||||
if (iterator.firstPool)
|
||||
iterator.index = iterator.firstPool->GetEntities().size();
|
||||
return iterator;
|
||||
}
|
||||
|
||||
void FindNextEntity()
|
||||
{
|
||||
while (index < entities.size() && !manager->HasComponents<Components...>(entities[index]))
|
||||
{
|
||||
++index;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
View(ECSManager* manager)
|
||||
: manager{manager}
|
||||
{
|
||||
}
|
||||
|
||||
Iterator begin()
|
||||
{
|
||||
return Iterator::Begin(manager);
|
||||
}
|
||||
|
||||
Iterator end()
|
||||
{
|
||||
return Iterator::End(manager);
|
||||
}
|
||||
|
||||
private:
|
||||
ECSManager* manager;
|
||||
};
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
#include "copium/event/EventSignal.h"
|
||||
|
||||
namespace Copium
|
||||
{
|
||||
EventSignal::EventSignal(const Event& event)
|
||||
: event{event}
|
||||
{
|
||||
}
|
||||
|
||||
const Event& EventSignal::GetEvent() const
|
||||
{
|
||||
return event;
|
||||
}
|
||||
|
||||
CP_SIGNAL_DEFINITION(EventSignal);
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "copium/ecs/Signal.h"
|
||||
#include "copium/event/Event.h"
|
||||
|
||||
namespace Copium
|
||||
{
|
||||
|
||||
class EventSignal : public Signal
|
||||
{
|
||||
private:
|
||||
const Event& event;
|
||||
|
||||
public:
|
||||
EventSignal(const Event& event);
|
||||
|
||||
const Event& GetEvent() const;
|
||||
|
||||
CP_SIGNAL_DECLERATION(EventSignal);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -102,6 +102,58 @@ namespace Copium
|
||||
return offset;
|
||||
}
|
||||
|
||||
glm::vec2 Renderer::TextUi(
|
||||
const std::string& str, const glm::vec2& position, const Font& font, float size, const glm::vec3& color)
|
||||
{
|
||||
glm::vec2 offset = position;
|
||||
for (char c : str)
|
||||
{
|
||||
if (c == ' ')
|
||||
{
|
||||
const Glyph& glyph = font.GetGlyph(c);
|
||||
offset.x += glyph.advance * size;
|
||||
continue;
|
||||
}
|
||||
else if (c == '\t')
|
||||
{
|
||||
const Glyph& glyph = font.GetGlyph(' ');
|
||||
offset.x += glyph.advance * size * 4;
|
||||
continue;
|
||||
}
|
||||
else if (c == '\n')
|
||||
{
|
||||
offset.y += font.GetLineHeight() * size;
|
||||
offset.x = position.x;
|
||||
continue;
|
||||
}
|
||||
const Glyph& glyph = font.GetGlyph(c);
|
||||
AllocateQuad();
|
||||
int texIndex = AllocateSampler(font);
|
||||
AddVertex(offset + glm::vec2{glyph.boundingBox.l * size, -glyph.boundingBox.t * size},
|
||||
color,
|
||||
texIndex,
|
||||
glm::vec2{glyph.texCoordBoundingBox.l, glyph.texCoordBoundingBox.t},
|
||||
RendererVertex::TYPE_TEXT);
|
||||
AddVertex(offset + glm::vec2{glyph.boundingBox.l * size, -glyph.boundingBox.b * size},
|
||||
color,
|
||||
texIndex,
|
||||
glm::vec2{glyph.texCoordBoundingBox.l, glyph.texCoordBoundingBox.b},
|
||||
RendererVertex::TYPE_TEXT);
|
||||
AddVertex(offset + glm::vec2{glyph.boundingBox.r * size, -glyph.boundingBox.b * size},
|
||||
color,
|
||||
texIndex,
|
||||
glm::vec2{glyph.texCoordBoundingBox.r, glyph.texCoordBoundingBox.b},
|
||||
RendererVertex::TYPE_TEXT);
|
||||
AddVertex(offset + glm::vec2{glyph.boundingBox.r * size, -glyph.boundingBox.t * size},
|
||||
color,
|
||||
texIndex,
|
||||
glm::vec2{glyph.texCoordBoundingBox.r, glyph.texCoordBoundingBox.t},
|
||||
RendererVertex::TYPE_TEXT);
|
||||
offset.x += glyph.advance * size;
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
void Renderer::AddVertex(
|
||||
const glm::vec2& position, const glm::vec3& color, int texindex, const glm::vec2& texCoord, int type)
|
||||
{
|
||||
|
||||
@@ -46,6 +46,12 @@ namespace Copium
|
||||
const Font& font,
|
||||
float size,
|
||||
const glm::vec3& color = glm::vec3(1, 1, 1));
|
||||
// Returns the position where the text rendering ended
|
||||
glm::vec2 TextUi(const std::string& str,
|
||||
const glm::vec2& position,
|
||||
const Font& font,
|
||||
float size,
|
||||
const glm::vec3& color = glm::vec3(1, 1, 1));
|
||||
|
||||
void Begin(CommandBuffer& commandBuffer);
|
||||
void End();
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
class GenericType
|
||||
{
|
||||
public:
|
||||
template <typename T, typename... Args>
|
||||
static GenericType Create(const Args&... args)
|
||||
{
|
||||
GenericType type;
|
||||
type.data = new T{args...};
|
||||
type.deleter = [](void* data) { delete (T*)data; };
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
GenericType(GenericType&& type)
|
||||
: data{type.data},
|
||||
deleter{type.deleter}
|
||||
{
|
||||
type.data = nullptr;
|
||||
}
|
||||
|
||||
GenericType& operator=(GenericType&& type)
|
||||
{
|
||||
this->data = type.data;
|
||||
this->deleter = type.deleter;
|
||||
type.data = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
~GenericType()
|
||||
{
|
||||
if (data)
|
||||
{
|
||||
deleter(data);
|
||||
}
|
||||
data = nullptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T& Get()
|
||||
{
|
||||
return *(T*)data;
|
||||
}
|
||||
|
||||
private:
|
||||
GenericType() = default;
|
||||
|
||||
GenericType(const GenericType& type) = delete;
|
||||
GenericType& operator=(const GenericType& type) = delete;
|
||||
|
||||
void* data;
|
||||
std::function<void(void*)> deleter;
|
||||
};
|
||||
@@ -26,4 +26,9 @@ namespace Copium
|
||||
|
||||
return elapsedTime;
|
||||
}
|
||||
|
||||
void Timer::OffsetSeconds(int time)
|
||||
{
|
||||
startTime += std::chrono::seconds(time);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,5 +15,6 @@ namespace Copium
|
||||
void Start();
|
||||
double Elapsed();
|
||||
double ElapsedRestart();
|
||||
void OffsetSeconds(int time);
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user