UPD: Introduce ECS System

This commit is contained in:
sebastian 2026-02-08 18:30:25 +01:00
parent fffc799447
commit fe376e9c0f
42 changed files with 557 additions and 216 deletions

View File

@ -58,7 +58,6 @@ add_executable(Dicewars_Siedler src/main.cpp
src/engine/layer/entities/Light.h src/engine/layer/entities/Light.h
src/engine/platform/glfw/MousePicker.cpp src/engine/platform/glfw/MousePicker.cpp
src/engine/platform/glfw/MousePicker.h src/engine/platform/glfw/MousePicker.h
src/game/hexWorld/HexTile.h
src/game/hexWorld/Map.cpp src/game/hexWorld/Map.cpp
src/game/hexWorld/Map.h src/game/hexWorld/Map.h
src/game/hexWorld/MapGenerator.cpp src/game/hexWorld/MapGenerator.cpp
@ -79,8 +78,25 @@ add_executable(Dicewars_Siedler src/main.cpp
src/game/hexWorld/tileGenerator/ForestTileGenerator.h src/game/hexWorld/tileGenerator/ForestTileGenerator.h
src/engine/toolbox/Random.cpp src/engine/toolbox/Random.cpp
src/engine/toolbox/Random.h src/engine/toolbox/Random.h
src/game/TileInteractionSystem.cpp src/engine/core/ECS/Component.cpp
src/game/TileInteractionSystem.h src/engine/core/ECS/Component.h
src/engine/core/ECS/TransformComponent.cpp
src/engine/core/ECS/TransformComponent.h
src/engine/core/ECS/ModelComponent.cpp
src/engine/core/ECS/ModelComponent.h
src/engine/core/ECS/EntityManager.cpp
src/engine/core/ECS/EntityManager.h
src/engine/core/ECS/RenderSystem.cpp
src/engine/core/ECS/RenderSystem.h
src/engine/core/ECS/TileRenderComponent.cpp
src/engine/core/ECS/TileRenderComponent.h
src/game/hexWorld/ecs/components/TileGameplayComponent.cpp
src/game/hexWorld/ecs/components/TileGameplayComponent.h
src/game/hexWorld/ecs/components/MapEntityComponent.h
src/engine/renderer/components/TerrainRenderingData.h
src/game/hexWorld/RessourceType.h
src/game/hexWorld/ecs/systems/TileHighlightSystem.cpp
src/game/hexWorld/ecs/systems/TileHighlightSystem.h
) )
target_include_directories(Dicewars_Siedler PRIVATE target_include_directories(Dicewars_Siedler PRIVATE

View File

@ -0,0 +1,5 @@
//
// Created by sebastian on 08.02.26.
//
#include "Component.h"

View File

@ -0,0 +1,16 @@
//
// Created by sebastian on 08.02.26.
//
#ifndef COMPONENT_H
#define COMPONENT_H
#include "glm/vec3.hpp"
class Component {
public:
virtual ~Component() = default;
};
#endif //COMPONENT_H

View File

@ -0,0 +1,17 @@
//
// Created by sebastian on 08.02.26.
//
#include "EntityManager.h"
#include <algorithm>
EntityID EntityManager::createEntity() {
const EntityID id = nextID++;
entities.push_back(id);
return id;
}
void EntityManager::destroyEntity(EntityID entity) {
entities.erase(std::remove(entities.begin(), entities.end(), entity), entities.end());
transforms.erase(entity);
models.erase(entity);
}

View File

@ -0,0 +1,80 @@
//
// Created by sebastian on 08.02.26.
//
#ifndef ENTITYMANAGER_H
#define ENTITYMANAGER_H
#include <cstdint>
#include <memory>
#include <unordered_map>
#include <vector>
// Forward Declarations (KEINE includes hier!)
class TransformComponent;
class ModelComponent;
class TileRenderComponent;
class TileGameplayComponent;
class MapEntityComponent;
using EntityID = std::uint32_t;
class EntityManager {
private:
EntityID nextID = 1;
std::vector<EntityID> entities;
std::unordered_map<EntityID, std::shared_ptr<TransformComponent>> transforms;
std::unordered_map<EntityID, std::shared_ptr<ModelComponent>> models;
std::unordered_map<EntityID, std::shared_ptr<TileRenderComponent>> tileRenderComponents;
std::unordered_map<EntityID, std::shared_ptr<TileGameplayComponent>> tileGameplayComponents;
std::unordered_map<EntityID, std::shared_ptr<MapEntityComponent>> mapEntityComponents;
public:
EntityID createEntity();
void destroyEntity(EntityID entity);
template<typename T>
void addComponent(EntityID entity, std::shared_ptr<T> component) {
if constexpr (std::is_same_v<T, TransformComponent>) {
transforms[entity] = component;
} else if constexpr (std::is_same_v<T, ModelComponent>) {
models[entity] = component;
} else if constexpr (std::is_same_v<T, TileRenderComponent>) {
tileRenderComponents[entity] = component;
} else if constexpr (std::is_same_v<T, TileGameplayComponent>) {
tileGameplayComponents[entity] = component;
} else if constexpr (std::is_same_v<T, MapEntityComponent>) {
mapEntityComponents[entity] = component;
} else {
static_assert(sizeof(T) == 0, "Component-Typ nicht unterstützt");
}
}
template<typename T>
std::shared_ptr<T> getComponent(EntityID entity) {
if constexpr (std::is_same_v<T, TransformComponent>) {
auto it = transforms.find(entity);
return (it != transforms.end()) ? it->second : nullptr;
} else if constexpr (std::is_same_v<T, ModelComponent>) {
auto it = models.find(entity);
return (it != models.end()) ? it->second : nullptr;
} else if constexpr (std::is_same_v<T, TileRenderComponent>) {
auto it = tileRenderComponents.find(entity);
return (it != tileRenderComponents.end()) ? it->second : nullptr;
} else if constexpr (std::is_same_v<T, TileGameplayComponent>) {
auto it = tileGameplayComponents.find(entity);
return (it != tileGameplayComponents.end()) ? it->second : nullptr;
} else if constexpr (std::is_same_v<T, MapEntityComponent>) {
auto it = mapEntityComponents.find(entity);
return (it != mapEntityComponents.end()) ? it->second : nullptr;
} else {
static_assert(sizeof(T) == 0, "Component-Typ nicht unterstützt");
}
}
const std::vector<EntityID>& getAllEntities() const { return entities; }
};
#endif //ENTITYMANAGER_H

View File

@ -0,0 +1,5 @@
//
// Created by sebastian on 08.02.26.
//
#include "ModelComponent.h"

View File

@ -0,0 +1,21 @@
//
// Created by sebastian on 08.02.26.
//
#ifndef MODELCOMPONENT_H
#define MODELCOMPONENT_H
#include <memory>
#include "Component.h"
#include "../../renderer/model/TexturedModel.h"
class ModelComponent: public Component {
public:
std::shared_ptr<TexturedModel> model;
ModelComponent(std::shared_ptr<TexturedModel> model) : model(std::move(model)) {};
};
#endif //MODELCOMPONENT_H

View File

@ -0,0 +1,24 @@
//
// Created by sebastian on 08.02.26.
//
#include "RenderSystem.h"
void RenderSystem::render(EntityManager &entityManager, MasterRenderer &renderer) {
for (auto id : entityManager.getAllEntities()) {
auto transform = entityManager.getComponent<TransformComponent>(id);
auto model = entityManager.getComponent<ModelComponent>(id);
auto tileRenderingComponent = entityManager.getComponent<TileRenderComponent>(id);
if (!transform || !model) continue;
if (tileRenderingComponent) {
renderer.submitTerrainTile(transform, model, tileRenderingComponent);
} else {
Entity entity = Entity(model->model, transform->position, transform->rotation.x, transform->rotation.y, transform->rotation.z, transform->scale);
renderer.submitEntity(std::make_unique<Entity>(entity));
}
}
}

View File

@ -0,0 +1,18 @@
//
// Created by sebastian on 08.02.26.
//
#ifndef RENDERSYSTEM_H
#define RENDERSYSTEM_H
#include "EntityManager.h"
#include "../../renderer/MasterRenderer.h"
class RenderSystem {
public:
void render(EntityManager &entityManager, MasterRenderer &renderer);
};
#endif //RENDERSYSTEM_H

View File

@ -0,0 +1,5 @@
//
// Created by sebastian on 08.02.26.
//
#include "TileRenderComponent.h"

View File

@ -0,0 +1,22 @@
//
// Created by sebastian on 08.02.26.
//
#ifndef TILERENDERCOMPONENT_H
#define TILERENDERCOMPONENT_H
#include <memory>
#include "Component.h"
#include "../../renderer/model/TexturedModel.h"
class TileRenderComponent : public Component{
public:
bool isHighlighted;
explicit TileRenderComponent(bool isHighlighted) : isHighlighted(isHighlighted) {};
};
#endif //TILERENDERCOMPONENT_H

View File

@ -0,0 +1,5 @@
//
// Created by sebastian on 08.02.26.
//
#include "TransformComponent.h"

View File

@ -0,0 +1,20 @@
//
// Created by sebastian on 08.02.26.
//
#ifndef TRANSFORMCOMPONENT_H
#define TRANSFORMCOMPONENT_H
#include "Component.h"
class TransformComponent : public Component {
public:
TransformComponent(glm::vec3 position, glm::vec3 rotation, float scale) : position(position), rotation(rotation), scale(scale) {};
glm::vec3 position;
glm::vec3 rotation;
float scale;
};
#endif //TRANSFORMCOMPONENT_H

View File

@ -9,7 +9,6 @@
#include "../../renderer/model/TexturedModel.h" #include "../../renderer/model/TexturedModel.h"
#include "glm/vec3.hpp" #include "glm/vec3.hpp"
class Entity { class Entity {
private: private:
std::shared_ptr<TexturedModel> model; std::shared_ptr<TexturedModel> model;

View File

@ -4,6 +4,8 @@
#include "MasterRenderer.h" #include "MasterRenderer.h"
#include <utility>
#include "../core/Application.h" #include "../core/Application.h"
#include "glm/ext/matrix_clip_space.hpp" #include "glm/ext/matrix_clip_space.hpp"
@ -24,13 +26,14 @@ void MasterRenderer::render(const Light &light, const Camera &camera) {
} }
void MasterRenderer::submitEntity(Entity *entity) { void MasterRenderer::submitEntity(std::unique_ptr<Entity> entity) {
TexturedModel* entityModel = entity->getModel().get(); TexturedModel* entityModel = entity->getModel().get();
entities[entityModel].push_back(entity); entities[entityModel].push_back(std::move(entity));
} }
void MasterRenderer::submitTerrainTile(HexRenderData *tile) { void MasterRenderer::submitTerrainTile(std::shared_ptr<TransformComponent> transform, std::shared_ptr<ModelComponent> model, std::shared_ptr<TileRenderComponent> terrainTileComponent) {
terrainTiles[tile->model.get()].push_back(tile); TerrainRenderingData terrain = TerrainRenderingData(std::move(transform), std::move(model), std::move(terrainTileComponent));
terrainTiles[terrain.modelComponent->model.get()].push_back(std::make_unique<TerrainRenderingData>(std::move(terrain)));
} }

View File

@ -9,7 +9,6 @@
#include "Renderer.h" #include "Renderer.h"
#include "TerrainRenderer.h" #include "TerrainRenderer.h"
#include "../../game/hexWorld/HexTile.h"
#include "../layer/entities/Entity.h" #include "../layer/entities/Entity.h"
#include "model/TexturedModel.h" #include "model/TexturedModel.h"
@ -19,8 +18,8 @@ class Light;
class MasterRenderer { class MasterRenderer {
private: private:
std::unordered_map<TexturedModel*, std::vector<Entity*>> entities; std::unordered_map<TexturedModel*, std::vector<std::unique_ptr<Entity>>> entities;
std::unordered_map<TexturedModel*, std::vector<HexRenderData*>> terrainTiles; std::unordered_map<TexturedModel*, std::vector<std::unique_ptr<TerrainRenderingData>>> terrainTiles;
glm::mat4 projectionMatrix; glm::mat4 projectionMatrix;
std::unique_ptr<Renderer> entityRenderer; std::unique_ptr<Renderer> entityRenderer;
std::unique_ptr<TerrainRenderer> terrainRenderer; std::unique_ptr<TerrainRenderer> terrainRenderer;
@ -39,8 +38,8 @@ public:
}; };
void render(const Light &light, const Camera &camera); void render(const Light &light, const Camera &camera);
void submitEntity(Entity* entity); void submitEntity(std::unique_ptr<Entity> entity);
void submitTerrainTile(HexRenderData* tile); void submitTerrainTile(std::shared_ptr<TransformComponent> transform, std::shared_ptr<ModelComponent> model, std::shared_ptr<TileRenderComponent>);
[[nodiscard]] glm::mat4 getProjectionMatrix() const {return projectionMatrix;} [[nodiscard]] glm::mat4 getProjectionMatrix() const {return projectionMatrix;}
}; };

View File

@ -6,7 +6,6 @@
#include <ranges> #include <ranges>
#include "../../game/hexWorld/HexTile.h"
#include "../core/Application.h" #include "../core/Application.h"
#include "../layer/entities/Light.h" #include "../layer/entities/Light.h"
#include "../toolbox/MathUtils.h" #include "../toolbox/MathUtils.h"
@ -24,7 +23,8 @@ void Renderer::prepare(const Camera& camera, const Light& light) {
} }
void Renderer::renderEntities(const std::unordered_map<TexturedModel *, std::vector<Entity *>>& entities) { void Renderer::renderEntities(const std::unordered_map<TexturedModel *, std::vector<std::unique_ptr<Entity>>>& entities) {
for (const auto& [texturedModel, batch] : entities) { for (const auto& [texturedModel, batch] : entities) {
prepareTexturedModel(*texturedModel); prepareTexturedModel(*texturedModel);
for (const auto& entity : batch) { for (const auto& entity : batch) {

View File

@ -4,7 +4,6 @@
#ifndef DICEWARS_SIEDLER_RENDERER_H #ifndef DICEWARS_SIEDLER_RENDERER_H
#define DICEWARS_SIEDLER_RENDERER_H #define DICEWARS_SIEDLER_RENDERER_H
#include "../../game/hexWorld/HexTile.h"
#include "../layer/entities/Entity.h" #include "../layer/entities/Entity.h"
#include "model/RawModel.h" #include "model/RawModel.h"
#include "model/TexturedModel.h" #include "model/TexturedModel.h"
@ -24,7 +23,7 @@ public:
}; };
void prepare(const ::Camera &camera, const Light& light); void prepare(const ::Camera &camera, const Light& light);
void renderEntities(const std::unordered_map<TexturedModel *, std::vector<Entity *>> &entities); void renderEntities(const std::unordered_map<TexturedModel *, std::vector<std::unique_ptr<Entity>>> &entities);
void finalizeFrame(); void finalizeFrame();
private: private:
StaticShader staticShader; StaticShader staticShader;

View File

@ -13,7 +13,7 @@ void TerrainRenderer::prepare(const Camera &camera, const Light &light) {
terrainShader.loadViewMatrix(viewMatrix); terrainShader.loadViewMatrix(viewMatrix);
} }
void TerrainRenderer::renderTerrainTiles(const std::unordered_map<TexturedModel *, std::vector<HexRenderData *>> &terrainTiles) { void TerrainRenderer::renderTerrainTiles(const std::unordered_map<TexturedModel *, std::vector<std::unique_ptr<TerrainRenderingData>>> &terrainTiles) {
for (const auto& [texturedModel, batch] : terrainTiles) { for (const auto& [texturedModel, batch] : terrainTiles) {
prepareTexturedModel(*texturedModel); prepareTexturedModel(*texturedModel);
for (const auto& hexTile : batch) { for (const auto& hexTile : batch) {
@ -47,9 +47,9 @@ void TerrainRenderer::unbindTexturedModel() {
glBindVertexArray(0); glBindVertexArray(0);
} }
void TerrainRenderer::prepareInstance(const HexRenderData &hexTile) { void TerrainRenderer::prepareInstance(const TerrainRenderingData &hexTile) {
glm::mat4 transformationMatrix = MathUtils::createTransformationMatrix(hexTile.position, 0,0,0,1); glm::mat4 transformationMatrix = MathUtils::createTransformationMatrix(hexTile.transform->position, 0,0,0,1);
terrainShader.loadTransformationMatrix(transformationMatrix); terrainShader.loadTransformationMatrix(transformationMatrix);
terrainShader.loadIsHighlighted(hexTile.highlight); terrainShader.loadIsHighlighted(hexTile.terrainRenderComponent->isHighlighted);
} }

View File

@ -4,12 +4,13 @@
#ifndef TERRAINRENDERER_H #ifndef TERRAINRENDERER_H
#define TERRAINRENDERER_H #define TERRAINRENDERER_H
#include "../../game/hexWorld/HexTile.h"
#include "model/TexturedModel.h" #include "model/TexturedModel.h"
#include "shaders/StaticShader.h" #include "shaders/StaticShader.h"
#include "../layer/entities/Camera.h" #include "../layer/entities/Camera.h"
#include "../layer/entities/Light.h" #include "../layer/entities/Light.h"
#include "components/TerrainRenderingData.h"
#include "shaders/TerrainShader.h" #include "shaders/TerrainShader.h"
@ -22,14 +23,14 @@ public:
} }
void prepare(const Camera &camera, const Light &light); void prepare(const Camera &camera, const Light &light);
void renderTerrainTiles(const std::unordered_map<TexturedModel*, std::vector<HexRenderData*>>& terrainTiles); void renderTerrainTiles(const std::unordered_map<TexturedModel*, std::vector<std::unique_ptr<TerrainRenderingData>>>& terrainTiles);
void finalizeFrame(); void finalizeFrame();
private: private:
TerrainShader terrainShader; TerrainShader terrainShader;
void prepareTexturedModel(const TexturedModel &texturedModel); void prepareTexturedModel(const TexturedModel &texturedModel);
void unbindTexturedModel(); void unbindTexturedModel();
void prepareInstance(const HexRenderData& hexTile); void prepareInstance(const TerrainRenderingData& hexTile);
}; };

View File

@ -0,0 +1,20 @@
//
// Created by sebastian on 08.02.26.
//
#ifndef TERRAINRENDERINGDATA_H
#define TERRAINRENDERINGDATA_H
#include "../../core/ECS/ModelComponent.h"
#include "../../core/ECS/TileRenderComponent.h"
#include "../../core/ECS/TransformComponent.h"
struct TerrainRenderingData {
std::shared_ptr<TransformComponent> transform;
std::shared_ptr<ModelComponent> modelComponent;
std::shared_ptr<TileRenderComponent> terrainRenderComponent;
};
#endif //TERRAINRENDERINGDATA_H

View File

@ -5,6 +5,7 @@
#include "AssetManager.h" #include "AssetManager.h"
#include <cassert> #include <cassert>
#include <utility>
#include "../loader/OBJLoader.h" #include "../loader/OBJLoader.h"
@ -23,3 +24,8 @@ std::shared_ptr<TexturedModel> AssetManager::getModel(const std::string &name) {
assert(models.contains(name) && "Model not found!"); assert(models.contains(name) && "Model not found!");
return models.at(name); return models.at(name);
} }
void AssetManager::insertGeneratedModel(const std::string &name, std::shared_ptr<TexturedModel> model) {
assert(!models.contains(name) && "Model already exists!");
models[name] = std::move(model);
}

View File

@ -15,6 +15,7 @@ class AssetManager {
public: public:
static std::shared_ptr<TexturedModel> loadModel(const std::string& name, const std::string& objPath, const std::string& texturePath, Loader& loader); static std::shared_ptr<TexturedModel> loadModel(const std::string& name, const std::string& objPath, const std::string& texturePath, Loader& loader);
static std::shared_ptr<TexturedModel> getModel(const std::string& name); static std::shared_ptr<TexturedModel> getModel(const std::string& name);
static void insertGeneratedModel(const std::string& name, std::shared_ptr<TexturedModel> model);
private: private:
static inline std::unordered_map<std::string, std::shared_ptr<TexturedModel>> models; static inline std::unordered_map<std::string, std::shared_ptr<TexturedModel>> models;
}; };

View File

@ -38,10 +38,22 @@ void GameLayer::onAttach()
//treeModel = std::make_unique<TexturedModel>(*OBJLoader::loadModel("assets/trees/lowPolyTree.obj", "assets/trees/lowPolyTree.png", loader)); //treeModel = std::make_unique<TexturedModel>(*OBJLoader::loadModel("assets/trees/lowPolyTree.obj", "assets/trees/lowPolyTree.png", loader));
//treeEntity = std::make_unique<Entity>(Entity(treeModel, glm::vec3(0,0,0), 0,0,0, 0.5f)); //treeEntity = std::make_unique<Entity>(Entity(treeModel, glm::vec3(0,0,0), 0,0,0, 0.5f));
MapGenerator::generateHexMap(*map, 10,10,10.f, mapEntities); MapGenerator::init(loader, 10.f);
printf("Generated Terrain with %lu Tiles!\n", map->tiles.size()); MapGenerator::generateHexMap(*map, 10,10, *entityManager);
//printf("Generated Terrain with %lu Tiles!\n", map->tiles.size());
for (const auto& entity : mapEntities) {
EntityID entityID = entityManager->createEntity();
glm::vec3 pos = entity->getPosition();
glm::vec3 rot = glm::vec3(entity->getRotX(), entity->getRotY(), entity->getRotZ());
float scale = entity->getScale();
entityManager->addComponent(entityID, std::make_shared<TransformComponent>(pos, rot, scale));
entityManager->addComponent(entityID, std::make_shared<ModelComponent>(entity->getModel()));
}
auto cabinModel = AssetManager::loadModel("cabin", "assets/cabin/cabin.obj", "assets/cabin/cabin.jpg", loader); auto cabinModel = AssetManager::loadModel("cabin", "assets/cabin/cabin.obj", "assets/cabin/cabin.jpg", loader);
//entities.push_back(std::make_shared<Entity>(Entity(cabinModel, glm::vec3(0,0,0), 0,0,0, 1.f))); //entities.push_back(std::make_shared<Entity>(Entity(cabinModel, glm::vec3(0,0,0), 0,0,0, 1.f)));
} }
@ -62,26 +74,8 @@ void GameLayer::onUpdate()
camera->move(moveDir, 0.5f); camera->move(moveDir, 0.5f);
tileHighlightSystem->update(*entityManager, *mousePicker, *camera);
for (const auto& entity : entities) { renderSystem->render(*entityManager, *renderer);
renderer->submitEntity(entity.get());
}
tileInteractionSystem->update(*map, *camera, *mousePicker);
tileInteractionSystem->handleBuildAction(*map);
std::vector<HexRenderData> terrainRenderData = map->getTerrainRenderData(hexModelDefault, hexModelWood, hexModelStone);
for (auto& renderData : terrainRenderData) {
renderer->submitTerrainTile(&renderData);
}
for (HexTile& tile : map->tiles) {
for (const auto& entity : tile.entitiesOnTile) {
renderer->submitEntity(entity.get());
}
if (tile.building) renderer->submitEntity(tile.building.get());
}
renderer->render(*light, *camera); renderer->render(*light, *camera);
} }

View File

@ -4,7 +4,7 @@
#ifndef DICEWARS_SIEDLER_GAMELAYER_H #ifndef DICEWARS_SIEDLER_GAMELAYER_H
#define DICEWARS_SIEDLER_GAMELAYER_H #define DICEWARS_SIEDLER_GAMELAYER_H
#include "TileInteractionSystem.h" #include "../engine/core/ECS/RenderSystem.h"
#include "../engine/layer/Layer.h" #include "../engine/layer/Layer.h"
#include "../engine/platform/glfw/MousePicker.h" #include "../engine/platform/glfw/MousePicker.h"
#include "../engine/renderer/Renderer.h" #include "../engine/renderer/Renderer.h"
@ -13,6 +13,7 @@
#include "../engine/layer/entities/Camera.h" #include "../engine/layer/entities/Camera.h"
#include "../engine/renderer/MasterRenderer.h" #include "../engine/renderer/MasterRenderer.h"
#include "hexWorld/Map.h" #include "hexWorld/Map.h"
#include "hexWorld/ecs/systems/TileHighlightSystem.h"
class GameLayer: public Layer { class GameLayer: public Layer {
@ -38,7 +39,11 @@ private:
std::vector<std::shared_ptr<Entity>> mapEntities; std::vector<std::shared_ptr<Entity>> mapEntities;
std::vector<std::shared_ptr<Entity>> entities; std::vector<std::shared_ptr<Entity>> entities;
std::unique_ptr<TileInteractionSystem> tileInteractionSystem = std::make_unique<TileInteractionSystem>();
std::unique_ptr<RenderSystem> renderSystem = std::make_unique<RenderSystem>();
std::unique_ptr<TileHighlightSystem> tileHighlightSystem = std::make_unique<TileHighlightSystem>();
std::unique_ptr<EntityManager> entityManager = std::make_unique<EntityManager>();
std::unique_ptr<Map> map; std::unique_ptr<Map> map;
}; };

View File

@ -1,30 +0,0 @@
//
// Created by sebastian on 08.02.26.
//
#include "TileInteractionSystem.h"
#include "../engine/layer/entities/Camera.h"
#include "../engine/platform/glfw/InputManager.h"
#include "../engine/renderer/model/AssetManager.h"
#include "GLFW/glfw3.h"
void TileInteractionSystem::update(Map &map, const Camera &camera, const MousePicker &mousePicker) {
for (HexTile& tile : map.tiles) {
glm::vec3 intersectionPoint;
tile.isHighlighted = tile.intersect(camera.getPosition(), mousePicker.getCurrentRay(), intersectionPoint);
}
}
void TileInteractionSystem::handleBuildAction(Map &map) {
for (HexTile& tile : map.tiles) {
if (tile.isHighlighted && InputManager::isMouseButtonPressed(GLFW_MOUSE_BUTTON_1)) {
if (!tile.building && tile.resourceType == RessourceType::WOOD) {
tile.entitiesOnTile.clear();
tile.building = std::make_shared<Entity>(AssetManager::getModel("cabin"), tile.worldPos, 0,0,0,1.f);
}
}
}
}

View File

@ -1,19 +0,0 @@
//
// Created by sebastian on 08.02.26.
//
#ifndef TILEINTERACTIONSYSTEM_H
#define TILEINTERACTIONSYSTEM_H
#include "../engine/platform/glfw/MousePicker.h"
#include "hexWorld/Map.h"
class TileInteractionSystem {
public:
void update(Map& map, const Camera& camera, const MousePicker& mousePicker);
void handleBuildAction(Map& map);
};
#endif //TILEINTERACTIONSYSTEM_H

View File

@ -4,7 +4,9 @@
#ifndef HEXMODELFACTORY_H #ifndef HEXMODELFACTORY_H
#define HEXMODELFACTORY_H #define HEXMODELFACTORY_H
#include "HexTile.h"
#include "RessourceType.h"
#include "../../engine/renderer/model/RawModel.h" #include "../../engine/renderer/model/RawModel.h"
#include "../../engine/renderer/model/TexturedModel.h" #include "../../engine/renderer/model/TexturedModel.h"

View File

@ -1,50 +0,0 @@
//
// Created by sebastian on 07.02.26.
//
#ifndef HEXTILE_H
#define HEXTILE_H
#include <memory>
#include "../../engine/layer/entities/Entity.h"
#include "../../engine/renderer/model/TexturedModel.h"
#include "glm/vec2.hpp"
#include "glm/vec3.hpp"
#include "glm/ext/quaternion_geometric.hpp"
enum class RessourceType {
NONE,
WOOD,
STONE
};
struct HexTile {
glm::vec3 worldPos;
int q, r; //Axiale Koordinaten (hex-Koordinaten)
int ownerID = -1;
RessourceType resourceType = RessourceType::NONE;
float radius;
bool isHighlighted = false;
std::vector<std::shared_ptr<Entity>> entitiesOnTile;
std::shared_ptr<Entity> building;
bool intersect(const glm::vec3& rayOrigin, const glm::vec3& rayDirection, glm::vec3& intersectionPoint) const {
float t = -rayOrigin.y / rayDirection.y;
if (t < 0) return false; // Ray zeigt nach oben, nicht getroffen
intersectionPoint = rayOrigin + t * rayDirection;
glm::vec2 diff(intersectionPoint.x - worldPos.x, intersectionPoint.z - worldPos.z);
return glm::length(diff) <= radius -0.1f;
}
};
struct HexRenderData {
std::shared_ptr<TexturedModel> model;
glm::vec3 position;
bool highlight = false;
};
#endif //HEXTILE_H

View File

@ -4,20 +4,13 @@
#include "Map.h" #include "Map.h"
std::vector<HexRenderData> Map::getTerrainRenderData(const std::shared_ptr<TexturedModel>& hexModel, #include "ecs/components/MapEntityComponent.h"
const std::shared_ptr<TexturedModel>& woodModel, const std::shared_ptr<TexturedModel>& stoneModel) { #include "ecs/components/TileGameplayComponent.h"
std::vector<HexRenderData> renderData;
renderData.reserve(tiles.size()); void Map::clear(EntityManager &entityManager) {
for (auto& tile : tiles) { for (EntityID id: entityManager.getAllEntities()) {
HexRenderData data; if (entityManager.getComponent<MapEntityComponent>(id)) {
if (tile.resourceType == RessourceType::WOOD) { entityManager.destroyEntity(id);
data = HexRenderData(woodModel, tile.worldPos, tile.isHighlighted);
} else if (tile.resourceType == RessourceType::NONE) {
data = HexRenderData(hexModel, tile.worldPos, tile.isHighlighted);
} else if (tile.resourceType == RessourceType::STONE) {
data =HexRenderData(stoneModel, tile.worldPos, tile.isHighlighted);
} }
renderData.push_back(data);
} }
return renderData;
} }

View File

@ -4,20 +4,21 @@
#ifndef MAP_H #ifndef MAP_H
#define MAP_H #define MAP_H
#include "HexTile.h" #include "../../engine/core/ECS/EntityManager.h"
struct Area { struct Area {
int id; int id;
std::vector<HexTile*> tiles; std::vector<EntityID> tiles;
}; };
class Map { class Map {
public: public:
std::vector<Area> areas; int width;
std::vector<HexTile> tiles; int height;
std::vector<HexRenderData> getTerrainRenderData(const std::shared_ptr<TexturedModel> &hexModel, const std::shared_ptr<TexturedModel> &woodModel, const std::shared_ptr< std::vector<Area> areas;
TexturedModel> &stoneModel);
void clear(EntityManager& entityManager);
}; };

View File

@ -3,3 +3,60 @@
// //
#include "MapGenerator.h" #include "MapGenerator.h"
float MapGenerator::hexRadius = 10.0f;
void MapGenerator::init(Loader &loader, float hexRadius) {
MapGenerator::hexRadius = hexRadius;
auto hexModelNone = HexModelFactory::createTexturedHexModel(loader, hexRadius, RessourceType::NONE);
auto hexModelWood = HexModelFactory::createTexturedHexModel(loader, hexRadius, RessourceType::WOOD);
auto hexModelStone = HexModelFactory::createTexturedHexModel(loader, hexRadius, RessourceType::STONE);
AssetManager::insertGeneratedModel("hexModelNone", std::make_shared<TexturedModel>(hexModelNone));
AssetManager::insertGeneratedModel("hexModelWood", std::make_shared<TexturedModel>(hexModelWood));
AssetManager::insertGeneratedModel("hexModelStone", std::make_shared<TexturedModel>(hexModelStone));
}
void MapGenerator::generateHexMap(Map &map, int width, int height, EntityManager &entityManager) {
Random random;
map.clear(entityManager);
float xOffset = hexRadius * std::sqrt(3.0f);
float zOffset = hexRadius * 1.5f;
for (int r = 0; r < height; ++r) {
for (int q = 0; q < width; ++q) {
// Pointy-top Hexes
float x = xOffset * (q + 0.5f * (r % 2));
float z = zOffset * r;
glm::vec3 worldPos = glm::vec3(x, 0.0f, z);
float radius = hexRadius;
float randomValue = random.randomFloat(0.0f, 1.0f);
RessourceType resourceType = RessourceType::NONE;
std::shared_ptr<TexturedModel> hexModel = AssetManager::getModel("hexModelNone");
if (randomValue < 0.5f) {
resourceType = RessourceType::WOOD;
ForestTileGenerator forestTileGenerator;
std::vector<EntityID> entities = forestTileGenerator.generateHexTile(entityManager, random, worldPos);
hexModel = AssetManager::getModel("hexModelWood");
} else if (randomValue < 0.75f) {
resourceType = RessourceType::STONE;
hexModel = AssetManager::getModel("hexModelStone");
}
EntityID entityID = entityManager.createEntity();
const auto transformComponent = std::make_shared<TransformComponent>(worldPos, glm::vec3(0), 1.0f);
const auto modelComponent = std::make_shared<ModelComponent>(hexModel);
const auto tileHighlightComponent = std::make_shared<TileRenderComponent>(false);
const auto tileGameplayComponent = std::make_shared<TileGameplayComponent>(q, r, radius, resourceType);
entityManager.addComponent(entityID, transformComponent);
entityManager.addComponent(entityID, modelComponent);
entityManager.addComponent(entityID, tileHighlightComponent);
entityManager.addComponent(entityID, tileGameplayComponent);
entityManager.addComponent(entityID, std::make_shared<MapEntityComponent>());
}
}
}

View File

@ -7,48 +7,25 @@
#include <cmath> #include <cmath>
#include <random> #include <random>
#include "HexTile.h" #include "HexModelFactory.h"
#include "Map.h" #include "Map.h"
#include "../../engine/core/ECS/ModelComponent.h"
#include "../../engine/core/ECS/TileRenderComponent.h"
#include "../../engine/core/ECS/TransformComponent.h"
#include "../../engine/renderer/model/AssetManager.h" #include "../../engine/renderer/model/AssetManager.h"
#include "../../engine/toolbox/Random.h" #include "../../engine/toolbox/Random.h"
#include "ecs/components/MapEntityComponent.h"
#include "ecs/components/TileGameplayComponent.h"
#include "tileGenerator/ForestTileGenerator.h" #include "tileGenerator/ForestTileGenerator.h"
class MapGenerator { class MapGenerator {
private:
static float hexRadius;
public: public:
static void generateHexMap(Map& map, int width, int height, float hexRadius, std::vector<std::shared_ptr<Entity>>& mapEntities) { static void init(Loader& loader, float hexRadius);
// Zufallsgenerator initialisieren (einmal) static void generateHexMap(Map& map, int width, int height, EntityManager& entityManager);
Random random;
map.tiles.clear();
float xOffset = hexRadius * std::sqrt(3.0f);
float zOffset = hexRadius * 1.5f;
for (int r = 0; r < height; ++r) {
for (int q = 0; q < width; ++q) {
HexTile tile;
tile.q = q;
tile.r = r;
// Pointy-top Hexes
float x = xOffset * (q + 0.5f * (r % 2));
float z = zOffset * r;
tile.worldPos = glm::vec3(x, 0.0f, z);
tile.radius = hexRadius;
float randomValue = random.randomFloat(0.0f, 1.0f);
if (randomValue < 0.5f) {
tile.resourceType = RessourceType::WOOD;
ForestTileGenerator forestTileGenerator;
const std::vector<std::shared_ptr<Entity>> forestEntities = forestTileGenerator.generateHexTile(random, tile.worldPos, mapEntities);
tile.entitiesOnTile = forestEntities;
} else if (randomValue < 0.75f) {
tile.resourceType = RessourceType::STONE;
}
map.tiles.push_back(tile);
}
}
}
private: private:

View File

@ -0,0 +1,8 @@
//
// Created by sebastian on 08.02.26.
//
#ifndef RESSOURCETYPE_H
#define RESSOURCETYPE_H
enum class RessourceType {NONE, WOOD, STONE};
#endif //RESSOURCETYPE_H

View File

@ -0,0 +1,14 @@
//
// Created by sebastian on 08.02.26.
//
#ifndef MAPENTITYCOMPONENT_H
#define MAPENTITYCOMPONENT_H
struct MapEntityComponent {};
#endif //MAPENTITYCOMPONENT_H

View File

@ -0,0 +1,5 @@
//
// Created by sebastian on 08.02.26.
//
#include "TileGameplayComponent.h"

View File

@ -0,0 +1,29 @@
//
// Created by sebastian on 08.02.26.
//
#ifndef TILEGAMEPLAYCOMPONENT_H
#define TILEGAMEPLAYCOMPONENT_H
#include "../../../../engine/core/ECS/Component.h"
#include "../../../../engine/core/ECS/EntityManager.h"
enum class RessourceType;
class TileGameplayComponent : public Component {
public:
int q, r;
float radius;
RessourceType ressourceType;
EntityID buildingEntityID = 0;
std::vector<EntityID> entitiesOnTile;
TileGameplayComponent(int q, int r, float radius, RessourceType ressourceType) : q(q), r(r), radius(radius), ressourceType(ressourceType) {};
};
#endif //TILEGAMEPLAYCOMPONENT_H

View File

@ -0,0 +1,42 @@
//
// Created by sebastian on 08.02.26.
//
#include "TileHighlightSystem.h"
#include "../../../../engine/core/ECS/TileRenderComponent.h"
#include "../../../../engine/core/ECS/TransformComponent.h"
#include "../components/TileGameplayComponent.h"
#include "../../../../engine/layer/entities/Camera.h"
void TileHighlightSystem::update(EntityManager &entityManager, const MousePicker& picker, const Camera& camera) {
for (EntityID entityID : entityManager.getAllEntities()) {
auto tileRenderComponent = entityManager.getComponent<TileRenderComponent>(entityID);
auto transformComponent = entityManager.getComponent<TransformComponent>(entityID);
auto tileGameplayComponent = entityManager.getComponent<TileGameplayComponent>(entityID);
if (!tileRenderComponent || !transformComponent || !tileGameplayComponent) {
continue;
}
glm::vec3 rayOrigin = camera.getPosition();
glm::vec3 rayDirection = picker.getCurrentRay();
glm::vec3 intersectionPoint;
if (intersectTile(rayOrigin, rayDirection, transformComponent->position, tileGameplayComponent->radius, intersectionPoint)) {
tileRenderComponent->isHighlighted = true;
} else {
tileRenderComponent->isHighlighted = false;
}
}
}
bool TileHighlightSystem::intersectTile(const glm::vec3 &rayOrigin, const glm::vec3 &rayDirection, glm::vec3 worldPos, float hexRadius, glm::vec3& intersectionPoint) {
float t = -rayOrigin.y / rayDirection.y;
if (t < 0) return false;
intersectionPoint = rayOrigin + rayDirection * t;
glm::vec2 diff(intersectionPoint.x - worldPos.x, intersectionPoint.z - worldPos.z);
return glm::length(diff) <= hexRadius - 0.1f;
}

View File

@ -0,0 +1,20 @@
//
// Created by sebastian on 08.02.26.
//
#ifndef TILEHIGHLIGHTSYSTEM_H
#define TILEHIGHLIGHTSYSTEM_H
#include "../../../../engine/core/ECS/EntityManager.h"
#include "../../../../engine/platform/glfw/MousePicker.h"
class Camera;
class TileHighlightSystem {
public:
void update(EntityManager &entityManager, const MousePicker &picker, const Camera &camera);
private:
bool intersectTile(const glm::vec3 & rayOrigin, const glm::vec3 & rayDirection, glm::vec3 worldPos, float hexRadius, glm::vec3 &interectionPoint);
};
#endif //TILEHIGHLIGHTSYSTEM_H

View File

@ -4,17 +4,18 @@
#include "ForestTileGenerator.h" #include "ForestTileGenerator.h"
#include "../../../engine/core/ECS/ModelComponent.h"
#include "../../../engine/core/ECS/TransformComponent.h"
#include "../../../engine/renderer/model/AssetManager.h" #include "../../../engine/renderer/model/AssetManager.h"
#include "glm/detail/func_geometric.inl" #include "glm/detail/func_geometric.inl"
std::vector<std::shared_ptr<Entity>> ForestTileGenerator::generateHexTile(Random& random, glm::vec3 tilePos, std::vector<std::shared_ptr<Entity>> &mapEntities) const { std::vector<EntityID> ForestTileGenerator::generateHexTile(EntityManager& em, Random& random, glm::vec3 tilePos) const {
int treeCount = random.randomInt(minTreeCount, maxTreeCount); int treeCount = random.randomInt(minTreeCount, maxTreeCount);
std::vector<std::shared_ptr<Entity>> entitiesOnTile;
std::shared_ptr<TexturedModel> treeModel = AssetManager::getModel("lowPolyTree"); std::shared_ptr<TexturedModel> treeModel = AssetManager::getModel("lowPolyTree");
std::shared_ptr<TexturedModel> treeModel2 = AssetManager::getModel("lowPolyTree2"); std::shared_ptr<TexturedModel> treeModel2 = AssetManager::getModel("lowPolyTree2");
std::vector<std::shared_ptr<TexturedModel>> treeModels = {treeModel, treeModel2}; std::vector<std::shared_ptr<TexturedModel>> treeModels = {treeModel, treeModel2};
std::vector<EntityID> entitiyIDs;
for (int i = 0; i < treeCount; ++i) { for (int i = 0; i < treeCount; ++i) {
glm::vec3 treePos; glm::vec3 treePos;
bool validPos = false; bool validPos = false;
@ -28,11 +29,15 @@ std::vector<std::shared_ptr<Entity>> ForestTileGenerator::generateHexTile(Random
treePos.z += random.randomFloat(-hexRadius * 0.5f, hexRadius * 0.5f); treePos.z += random.randomFloat(-hexRadius * 0.5f, hexRadius * 0.5f);
validPos = true; validPos = true;
for (const auto& otherTree : entitiesOnTile) { for (const auto& otherTreeID : entitiyIDs) {
if (glm::distance(treePos, otherTree->getPosition()) < minDistance) { const auto otherTree = em.getComponent<TransformComponent>(otherTreeID);
validPos = false; if (otherTree) {
break; if (glm::distance(treePos, otherTree->position) < minDistance) {
validPos = false;
break;
}
} }
} }
attempts++; attempts++;
} }
@ -40,9 +45,14 @@ std::vector<std::shared_ptr<Entity>> ForestTileGenerator::generateHexTile(Random
if (!validPos) continue; // zu viele Versuche, überspringen if (!validPos) continue; // zu viele Versuche, überspringen
int treeModelIndex = random.randomInt(0, static_cast<int>(treeModels.size()-1)); int treeModelIndex = random.randomInt(0, static_cast<int>(treeModels.size()-1));
auto treeEntity = std::make_shared<Entity>(treeModels[treeModelIndex], treePos, 0, 0, 0, 1.f);
mapEntities.push_back(treeEntity); const auto transformComponent = std::make_shared<TransformComponent>(treePos, glm::vec3(0), 1.f);
entitiesOnTile.push_back(treeEntity); const auto modelComponent = std::make_shared<ModelComponent>(treeModels[treeModelIndex]);
const EntityID entityID = em.createEntity();
em.addComponent(entityID, transformComponent);
em.addComponent(entityID, modelComponent);
entitiyIDs.push_back(entityID);
} }
return entitiesOnTile; return entitiyIDs;
} }

View File

@ -9,7 +9,7 @@
class ForestTileGenerator : public HexTileGeneratorStrategy { class ForestTileGenerator : public HexTileGeneratorStrategy {
public: public:
std::vector<std::shared_ptr<Entity>> generateHexTile(Random& random, glm::vec3 tilePos, std::vector<std::shared_ptr<Entity>>& mapEntities) const override; std::vector<EntityID> generateHexTile(EntityManager& em, Random& random, glm::vec3 tilePos) const override;
private: private:
const int minTreeCount = 3; const int minTreeCount = 3;
const int maxTreeCount = 5; const int maxTreeCount = 5;

View File

@ -7,6 +7,7 @@
#include <memory> #include <memory>
#include <vector> #include <vector>
#include "../../../engine/core/ECS/EntityManager.h"
#include "../../../engine/layer/entities/Entity.h" #include "../../../engine/layer/entities/Entity.h"
#include "../../../engine/toolbox/Random.h" #include "../../../engine/toolbox/Random.h"
@ -15,7 +16,7 @@ class HexTileGeneratorStrategy {
public: public:
virtual ~HexTileGeneratorStrategy() = default; virtual ~HexTileGeneratorStrategy() = default;
virtual std::vector<std::shared_ptr<Entity>> generateHexTile(Random& random, glm::vec3 tilePos, std::vector<std::shared_ptr<Entity>>& mapEntities) const = 0; virtual std::vector<EntityID> generateHexTile(EntityManager& em, Random& random, glm::vec3 tilePos) const = 0;
}; };