From b6d24da9828e579de6231517fb1c29d7f8fd76ed Mon Sep 17 00:00:00 2001 From: Jan Mrna Date: Sun, 28 Sep 2025 20:42:07 +0200 Subject: [PATCH] Add terrain painting functions --- cpp/src/main.cpp | 2 +- cpp/src/map.cpp | 75 +++++++++++++++++++++++++------------ cpp/src/map.hpp | 5 ++- cpp/src/math.hpp | 30 +++++++++++++-- cpp/src/pathfindingdemo.cpp | 18 +++++++-- cpp/src/tile.cpp | 15 +++++--- cpp/src/tile.hpp | 11 +++++- 7 files changed, 118 insertions(+), 38 deletions(-) diff --git a/cpp/src/main.cpp b/cpp/src/main.cpp index fde36f4..3e1fcff 100644 --- a/cpp/src/main.cpp +++ b/cpp/src/main.cpp @@ -28,7 +28,7 @@ int main() { * Initialize the map and run the pathfinding demo */ - auto demo = std::make_unique(10, 10); + auto demo = std::make_unique(100, 100); demo->CreateMap(); auto game_loop = GameLoop{}; diff --git a/cpp/src/map.cpp b/cpp/src/map.cpp index 3c065d6..a9e5886 100644 --- a/cpp/src/map.cpp +++ b/cpp/src/map.cpp @@ -6,19 +6,13 @@ #include "tile.hpp" Map::Map(int rows, int cols) : m_Cols(cols), m_Rows(rows) { - bool sw = true; LOG_DEBUG("cols = ", cols, " rows = ", rows); m_Tiles = std::vector>{}; for (size_t row = 0; row < m_Rows; row++) { m_Tiles.push_back(std::vector{}); for (size_t col = 0; col < m_Cols; col++) { - if (sw) - m_Tiles[row].push_back(&tile_types.at("Grass")); - else - m_Tiles[row].push_back(&tile_types.at("Water")); - sw = !sw; + m_Tiles[row].push_back(&tile_types.at(TileType::GRASS)); } - //sw = !sw; } } @@ -49,8 +43,10 @@ const Tile *Map::GetTileAt(WorldPos p) const { } bool Map::IsTilePosValid(TilePos p) const { - size_t row = p.x; - size_t col = p.y; + if (p.x < 0 || p.y < 0) + return false; + size_t row = static_cast(p.x); + size_t col = static_cast(p.y); return row < m_Tiles.size() && col < m_Tiles[0].size(); } @@ -62,10 +58,10 @@ std::vector Map::GetNeighbors(TilePos center) const neighbours.reserve(4); std::array candidates = { - center + TilePos{1,0}, - center + TilePos{-1,0}, - center + TilePos{0, 1}, - center + TilePos{0, -1}, + center + TilePos{ 1, 0}, + center + TilePos{-1, 0}, + center + TilePos{ 0, 1}, + center + TilePos{ 0, -1}, }; for (const auto& c : candidates) { @@ -74,13 +70,46 @@ std::vector Map::GetNeighbors(TilePos center) const } return neighbours; } -// std::vector neighbours; -// neighbours.reserve(8); -// for (int dx = -1; dx <= 1; ++dx) { -// for (int dy = -1; dy <= 1; ++dy) { -// if (dx == 0 && dy == 0) continue; -// TilePos p{center.x + dx, center.y + dy}; -// if (IsTilePosValid(p)) neighbours.push_back(std::move(p)); -// } -// } -// return neighbours; + + +void Map::PaintCircle(TilePos center, unsigned radius, TileType tile_type) +{ + // get rectangle that wraps the circle + TilePos corner1 = TilePos{center.x - radius, center.y - radius}; + TilePos corner2 = TilePos{center.x + radius, center.y + radius}; + // iterate through all valid points, setting the type + const unsigned radius_squared = radius * radius; + for (int x = corner1.x; x < corner2.x; x++) { + for (int y = corner1.y; y < corner2.y; y++) { + TilePos current_tile = {x, y}; + unsigned distance_squared = center.distance_squared(current_tile); + if (IsTilePosValid(current_tile) && distance_squared < radius_squared) + { + // y is row, x is col + m_Tiles[y][x] = &tile_types.at(tile_type); + } + } + } +} + +void Map::PaintLine(TilePos start_tile, TilePos stop_tile, double width, TileType tile_type) +{ + const Vec2D start(start_tile); + const Vec2D stop(stop_tile); + const double line_length = start.distance(stop); + const Vec2D step((stop-start)/line_length); + const Vec2D ortho = step.orthogonal(); + LOG_DEBUG("step = ", step, " ortho = ", ortho); + + for (double t = 0; t < line_length; t += 1.0) { + for (double ortho_t = 0; ortho_t < width; ortho_t += 0.1) { + auto tile_pos = start + step * t + ortho * ortho_t; + size_t row = static_cast(tile_pos.x); + size_t col = static_cast(tile_pos.y); + if (IsTilePosValid(tile_pos)) { + m_Tiles[row][col] = &tile_types.at(tile_type); + } + } + } +} + diff --git a/cpp/src/map.hpp b/cpp/src/map.hpp index ced9085..763ecf8 100644 --- a/cpp/src/map.hpp +++ b/cpp/src/map.hpp @@ -9,7 +9,7 @@ using TileGrid = std::vector>; class Map { public: - static constexpr float TILE_SIZE = 100.0f; // tile size in world + static constexpr float TILE_SIZE = 10.0f; // tile size in world Map(int rows, int cols); Map() : Map(0, 0) {} @@ -32,6 +32,9 @@ public: bool IsTilePosValid(TilePos p) const; + // methods for drawing on the map + void PaintCircle(TilePos center, unsigned radius, TileType tile_type); + void PaintLine(TilePos start, TilePos stop, double width, TileType tile_type); std::vector GetNeighbors(TilePos center) const; float GetCost(TilePos pos) const { return GetTileAt(pos)->cost; } diff --git a/cpp/src/math.hpp b/cpp/src/math.hpp index 002a73a..d48eaaf 100644 --- a/cpp/src/math.hpp +++ b/cpp/src/math.hpp @@ -14,12 +14,26 @@ public: Vec2D() = default; ~Vec2D() = default; - Vec2D &operator+=(const Vec2D &other) { + template + Vec2D(Vec2D other) { + this->x = static_cast(other.x); + this->y = static_cast(other.y); + } + + Vec2D& operator+=(const Vec2D &other) { x += other.x; y += other.y; return *this; } + template + requires std::is_arithmetic_v + Vec2D& operator/=(U k) { + this->x /= static_cast(k); + this->y /= static_cast(k); + return *this; + } + friend Vec2D operator+(const Vec2D &a, const Vec2D &b) { return Vec2D{a.x + b.x, a.y + b.y}; } @@ -28,10 +42,20 @@ public: return Vec2D{a.x - b.x, a.y - b.y}; } - friend Vec2D operator*(float k, const Vec2D &v) { + template + requires std::is_arithmetic_v + friend Vec2D operator*(U k, const Vec2D &v) + { return Vec2D{k * v.x, k * v.y}; } + template + requires std::is_arithmetic_v + friend Vec2D operator/(const Vec2D &v, U k) + { + return Vec2D{v.x / k, v.y / k}; + } + friend bool operator==(const Vec2D &a, const Vec2D &b) { if constexpr (std::is_integral_v) { return a.x == b.x && a.y == b.y; @@ -76,7 +100,7 @@ public: return v; } - Vec2D orthogonal() + Vec2D orthogonal() const { Vec2D v(*this); diff --git a/cpp/src/pathfindingdemo.cpp b/cpp/src/pathfindingdemo.cpp index 55e7c0a..39fe40e 100644 --- a/cpp/src/pathfindingdemo.cpp +++ b/cpp/src/pathfindingdemo.cpp @@ -11,6 +11,7 @@ #include "user_input.hpp" #include "pathfinder/base.hpp" #include "pathfinder/utils.hpp" +#include "tile.hpp" PathFindingDemo::PathFindingDemo(int width, int height) : m_Map(width, height) @@ -28,9 +29,18 @@ void PathFindingDemo::AddEntity(std::shared_ptr e) { } void PathFindingDemo::CreateMap() { + // create the map + m_Map.PaintCircle(TilePos{50, 50}, 10, TileType::WATER); + m_Map.PaintCircle(TilePos{75, 100}, 50, TileType::WATER); + m_Map.PaintLine(TilePos{0,0}, TilePos{100,100}, 3.0, TileType::WATER); + m_Map.PaintLine(TilePos{17,6}, TilePos{100,6}, 5.0, TileType::ROAD); + m_Map.PaintLine(TilePos{10,17}, TilePos{10,100}, 5.0, TileType::ROAD); + m_Map.PaintLine(TilePos{20,10}, TilePos{10,20}, 5.0, TileType::ROAD); + + // add player m_Entities.clear(); m_Player = std::make_shared(); - m_Player->SetPosition(WorldPos{200.0f, 200.0f}); + m_Player->SetPosition(WorldPos{250.0f, 200.0f}); m_Entities.push_back(m_Player); } @@ -47,7 +57,7 @@ std::optional PathFindingDemo::GetMoveTarget() { WorldPos next_player_pos = m_Path.front(); - if (current_player_pos.distance(next_player_pos) > 10.0) { + if (current_player_pos.distance(next_player_pos) > 1.0) { // target not reached yet return next_player_pos; } @@ -68,8 +78,8 @@ void PathFindingDemo::UpdatePlayerVelocity() { if (next_pos) { velocity = next_pos.value() - current_pos; velocity.normalize(); - LOG_DEBUG("I want to move to: ", next_pos.value(), - ", velocity: ", velocity); + //LOG_DEBUG("I want to move to: ", next_pos.value(), + // ", velocity: ", velocity); } player->SetActualVelocity(velocity * tile_velocity_coeff); float time_delta = 1.0f; diff --git a/cpp/src/tile.cpp b/cpp/src/tile.cpp index 98e7b6a..1a85a1d 100644 --- a/cpp/src/tile.cpp +++ b/cpp/src/tile.cpp @@ -1,8 +1,13 @@ +#include +#include + #include "tile.hpp" -const std::map tile_types = { - {"Grass", {1.0, 0, 200, 0, 255}}, - {"Mud", {2.0, 100, 100, 100, 255}}, - {"Road", {0.5, 200, 200, 200, 255}}, - {"Water", {10.0, 0, 50, 200, 255}}, +// we could use array here, but this is more explicit, +// and we don't access tile_types that often, so it should be ok +const std::unordered_map tile_types = { + { TileType::GRASS, Tile{1.0, 0, 200, 0, 255}}, + { TileType::MUD, Tile{2.0, 100, 100, 100, 255}}, + { TileType::ROAD, Tile{0.5, 20, 20, 20, 255}}, + { TileType::WATER, Tile{10.0, 0, 50, 200, 255}}, }; diff --git a/cpp/src/tile.hpp b/cpp/src/tile.hpp index 7799fec..c7d3535 100644 --- a/cpp/src/tile.hpp +++ b/cpp/src/tile.hpp @@ -3,10 +3,19 @@ #include #include #include +#include +#include struct Tile { float cost; uint8_t R, G, B, A; }; -extern const std::map tile_types; +enum class TileType { + GRASS, + MUD, + ROAD, + WATER, +}; + +extern const std::unordered_map tile_types;