switch to ECS architecture
This commit is contained in:
@@ -5,6 +5,7 @@ SET(HDRS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Rotation.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/BuildingType.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/EntityAdmin.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/EcsComponents.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Blueprint.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ItemType.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Item.h
|
||||
|
||||
107
src/lib/core/EcsComponents.h
Normal file
107
src/lib/core/EcsComponents.h
Normal file
@@ -0,0 +1,107 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <QPoint>
|
||||
#include <QSize>
|
||||
#include <QVector2D>
|
||||
|
||||
#include "EntityId.h"
|
||||
#include "Tick.h"
|
||||
|
||||
#include "entt/entity/entity.hpp"
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Shared components (used by ships, stations, scrap, HQ proxy)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
struct Position
|
||||
{
|
||||
QVector2D value;
|
||||
};
|
||||
|
||||
struct Health
|
||||
{
|
||||
float hp;
|
||||
float maxHp;
|
||||
};
|
||||
|
||||
struct Faction
|
||||
{
|
||||
bool isEnemy;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Ship components (always present on every ship)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
struct Velocity
|
||||
{
|
||||
QVector2D value;
|
||||
};
|
||||
|
||||
struct Facing
|
||||
{
|
||||
float radians;
|
||||
float rotationSpeed;
|
||||
};
|
||||
|
||||
struct ShipDynamics
|
||||
{
|
||||
float maxSpeedPerTick;
|
||||
float mainAccelerationPerTick;
|
||||
float maneuveringAccelerationPerTick;
|
||||
float angularAccelerationPerTick;
|
||||
float maxRotationSpeedPerTick;
|
||||
};
|
||||
|
||||
struct SensorRange
|
||||
{
|
||||
float value;
|
||||
};
|
||||
|
||||
struct ShipIdentity
|
||||
{
|
||||
int level;
|
||||
std::string schematicId;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Ship optional components (hardware + behavior, in Ship.h)
|
||||
// ---------------------------------------------------------------------------
|
||||
// Weapon, SalvageCargo, RepairTool, ThreatResponse, ScrapCollector,
|
||||
// RepairBehavior, HomeReturn, RallyBehavior remain defined in Ship.h.
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Station components
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
struct StationBody
|
||||
{
|
||||
QPoint anchor;
|
||||
QSize footprint;
|
||||
std::vector<QPoint> bodyCells;
|
||||
};
|
||||
|
||||
// StationWeapon remains defined in Building.h.
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Scrap components
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
struct ScrapData
|
||||
{
|
||||
int amount;
|
||||
};
|
||||
|
||||
struct DespawnAt
|
||||
{
|
||||
Tick tick;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// HQ proxy (empty tag)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
struct HqProxy { char unused = 0; };
|
||||
@@ -1,11 +1,14 @@
|
||||
#include "EntityAdmin.h"
|
||||
|
||||
#include "EcsComponents.h"
|
||||
#include "MovementIntent.h"
|
||||
|
||||
entt::entity EntityAdmin::createEntity()
|
||||
{
|
||||
return m_registry.create();
|
||||
}
|
||||
|
||||
bool EntityAdmin::isValid(entt::entity entity)
|
||||
bool EntityAdmin::isValid(entt::entity entity) const
|
||||
{
|
||||
return m_registry.valid(entity);
|
||||
}
|
||||
@@ -19,3 +22,57 @@ void EntityAdmin::clear()
|
||||
{
|
||||
m_registry.clear();
|
||||
}
|
||||
|
||||
entt::entity EntityAdmin::spawnShip(QVector2D position, float hp, float maxHp,
|
||||
float maxSpeedPerTick, float mainAccelPerTick,
|
||||
float maneuveringAccelPerTick, float angularAccelPerTick,
|
||||
float maxRotationSpeedPerTick, float sensorRange,
|
||||
int level, const std::string& schematicId, bool isEnemy)
|
||||
{
|
||||
entt::entity entity = createEntity();
|
||||
add<Position>(entity, Position{position});
|
||||
add<Health>(entity, Health{hp, maxHp});
|
||||
add<Faction>(entity, Faction{isEnemy});
|
||||
add<Velocity>(entity, Velocity{QVector2D(0.0f, 0.0f)});
|
||||
add<Facing>(entity, Facing{0.0f, 0.0f});
|
||||
add<ShipDynamics>(entity, ShipDynamics{
|
||||
maxSpeedPerTick, mainAccelPerTick, maneuveringAccelPerTick,
|
||||
angularAccelPerTick, maxRotationSpeedPerTick});
|
||||
add<SensorRange>(entity, SensorRange{sensorRange});
|
||||
add<ShipIdentity>(entity, ShipIdentity{level, schematicId});
|
||||
add<MovementIntent>(entity, MovementIntent{0, QVector2D(0.0f, 0.0f)});
|
||||
return entity;
|
||||
}
|
||||
|
||||
entt::entity EntityAdmin::spawnStation(QPoint anchor, QSize footprint,
|
||||
const std::vector<QPoint>& bodyCells,
|
||||
float hp, float maxHp, bool isEnemy)
|
||||
{
|
||||
entt::entity entity = createEntity();
|
||||
QVector2D center(anchor.x() + footprint.width() / 2.0f,
|
||||
anchor.y() + footprint.height() / 2.0f);
|
||||
add<Position>(entity, Position{center});
|
||||
add<Health>(entity, Health{hp, maxHp});
|
||||
add<Faction>(entity, Faction{isEnemy});
|
||||
add<StationBody>(entity, StationBody{anchor, footprint, bodyCells});
|
||||
return entity;
|
||||
}
|
||||
|
||||
entt::entity EntityAdmin::spawnScrap(QVector2D position, int amount, Tick despawnAt)
|
||||
{
|
||||
entt::entity entity = createEntity();
|
||||
add<Position>(entity, Position{position});
|
||||
add<ScrapData>(entity, ScrapData{amount});
|
||||
add<DespawnAt>(entity, DespawnAt{despawnAt});
|
||||
return entity;
|
||||
}
|
||||
|
||||
entt::entity EntityAdmin::spawnHqProxy(QVector2D position, float hp, float maxHp)
|
||||
{
|
||||
entt::entity entity = createEntity();
|
||||
add<Position>(entity, Position{position});
|
||||
add<Health>(entity, Health{hp, maxHp});
|
||||
add<Faction>(entity, Faction{false});
|
||||
add<HqProxy>(entity);
|
||||
return entity;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
#ifndef ENTITY_ADMIN_H
|
||||
#define ENTITY_ADMIN_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <QPoint>
|
||||
#include <QSize>
|
||||
#include <QVector2D>
|
||||
|
||||
#include "Tick.h"
|
||||
|
||||
#include "entt/entity/registry.hpp"
|
||||
|
||||
class EntityAdmin
|
||||
@@ -10,36 +19,84 @@ public:
|
||||
EntityAdmin(const EntityAdmin&) = delete;
|
||||
EntityAdmin& operator=(const EntityAdmin&) = delete;
|
||||
|
||||
// -- Queries / iteration ------------------------------------------------
|
||||
|
||||
template <typename... Ts, typename Func>
|
||||
void forEach(Func&& f);
|
||||
|
||||
template <typename... Ts, typename Func>
|
||||
void forEach(Func&& f) const;
|
||||
|
||||
template <typename... Ts>
|
||||
bool hasAll(entt::entity entity);
|
||||
|
||||
template <typename T>
|
||||
T& get(entt::entity entity);
|
||||
|
||||
bool isValid(entt::entity entity);
|
||||
template <typename T>
|
||||
const T& get(entt::entity entity) const;
|
||||
|
||||
bool isValid(entt::entity entity) const;
|
||||
void destroy(entt::entity entity);
|
||||
void clear();
|
||||
|
||||
/*
|
||||
factory methods (like spawnShip, spawnScrap, etc shall go here)
|
||||
*/
|
||||
// -- Public component attachment ----------------------------------------
|
||||
// Used by systems (e.g. ShipSystem) to attach optional components after
|
||||
// a factory method has created the base entity.
|
||||
|
||||
template <typename T, typename... Args>
|
||||
void addComponent(entt::entity entity, Args&&... args);
|
||||
|
||||
template <typename T>
|
||||
void removeComponent(entt::entity entity);
|
||||
|
||||
// -- Factory methods ----------------------------------------------------
|
||||
|
||||
entt::entity spawnShip(QVector2D position, float hp, float maxHp,
|
||||
float maxSpeedPerTick, float mainAccelPerTick,
|
||||
float maneuveringAccelPerTick, float angularAccelPerTick,
|
||||
float maxRotationSpeedPerTick, float sensorRange,
|
||||
int level, const std::string& schematicId, bool isEnemy);
|
||||
|
||||
entt::entity spawnStation(QPoint anchor, QSize footprint,
|
||||
const std::vector<QPoint>& bodyCells,
|
||||
float hp, float maxHp, bool isEnemy);
|
||||
|
||||
entt::entity spawnScrap(QVector2D position, int amount, Tick despawnAt);
|
||||
|
||||
entt::entity spawnHqProxy(QVector2D position, float hp, float maxHp);
|
||||
|
||||
private:
|
||||
entt::entity createEntity();
|
||||
|
||||
template <typename T, typename... Args>
|
||||
T& add(entt::entity entity, Args&&... args);
|
||||
void add(entt::entity entity, Args&&... args);
|
||||
|
||||
entt::registry m_registry;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Template implementations
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
template <typename... Ts, typename Func>
|
||||
void EntityAdmin::forEach(Func&& f)
|
||||
{
|
||||
m_registry.view<Ts...>().each(std::forward<Func>(f));
|
||||
// Avoid view.each() — MSVC 2017 ICEs on the extended_storage_iterator it instantiates.
|
||||
for (entt::entity entity : m_registry.view<Ts...>())
|
||||
{
|
||||
f(entity, m_registry.get<Ts>(entity)...);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... Ts, typename Func>
|
||||
void EntityAdmin::forEach(Func&& f) const
|
||||
{
|
||||
entt::registry& reg = const_cast<entt::registry&>(m_registry);
|
||||
for (entt::entity entity : reg.view<Ts...>())
|
||||
{
|
||||
f(entity, reg.get<Ts>(entity)...);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... Ts>
|
||||
@@ -54,10 +111,28 @@ T& EntityAdmin::get(entt::entity entity)
|
||||
return m_registry.get<T>(entity);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
T& EntityAdmin::add(entt::entity entity, Args&&... args)
|
||||
template <typename T>
|
||||
const T& EntityAdmin::get(entt::entity entity) const
|
||||
{
|
||||
return m_registry.emplace<T>(entity, std::forward<Args>(args)...);
|
||||
return m_registry.get<T>(entity);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
void EntityAdmin::addComponent(entt::entity entity, Args&&... args)
|
||||
{
|
||||
m_registry.emplace<T>(entity, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void EntityAdmin::removeComponent(entt::entity entity)
|
||||
{
|
||||
m_registry.remove<T>(entity);
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
void EntityAdmin::add(entt::entity entity, Args&&... args)
|
||||
{
|
||||
m_registry.emplace<T>(entity, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
#endif // ENTITY_ADMIN_H
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "EntityId.h"
|
||||
#include "Tick.h"
|
||||
|
||||
#include "entt/entity/entity.hpp"
|
||||
|
||||
// Transient record emitted each time a weapon fires (REQ-SHP-FIRING,
|
||||
// REQ-SHP-FIRING-BEAM). Buffered in a sim-owned queue and drained by the
|
||||
// renderer each frame to draw the 0.3-second laser beam.
|
||||
struct FireEvent
|
||||
{
|
||||
EntityId shooter;
|
||||
EntityId target;
|
||||
entt::entity shooter;
|
||||
entt::entity target;
|
||||
Tick emittedAt;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user