From 3d34b68133754ed1eccc5eef8fe6e034321f2353 Mon Sep 17 00:00:00 2001 From: Jan Mrna Date: Fri, 10 Oct 2025 10:45:27 +0200 Subject: [PATCH] Multiple entities + pathfinding --- cpp/src/entities.cpp | 22 +++++++++ cpp/src/entities.hpp | 8 ++++ cpp/src/gameloop.cpp | 20 +++++--- cpp/src/pathfindingdemo.cpp | 96 +++++++++++++++++++++---------------- cpp/src/pathfindingdemo.hpp | 8 +--- 5 files changed, 99 insertions(+), 55 deletions(-) diff --git a/cpp/src/entities.cpp b/cpp/src/entities.cpp index d8e1ad2..a162d25 100644 --- a/cpp/src/entities.cpp +++ b/cpp/src/entities.cpp @@ -46,6 +46,28 @@ void Entity::Update(float time_delta) { m_Position += m_ActualVelocity * time_delta; } +std::optional Entity::GetMoveTarget() +{ + auto& path = GetPath(); + if (path.empty()) { + return {}; + } + + WorldPos current_pos = GetPosition(); + WorldPos next_pos = path.front(); + + if (current_pos.DistanceTo(next_pos) > 1.0) { + // target not reached yet + return next_pos; + } + // target reached, pop it + //m_MoveQueue.pop(); + path.erase(path.begin()); + // return nothing - if there's next point in the queue, + // we'll get it in the next iteration + return {}; +} + Player::Player() { LOG_DEBUG("."); if (m_Sprite == nullptr) { diff --git a/cpp/src/entities.hpp b/cpp/src/entities.hpp index bed7204..430e23b 100644 --- a/cpp/src/entities.hpp +++ b/cpp/src/entities.hpp @@ -5,10 +5,12 @@ #include #include #include +#include #include "log.hpp" #include "math.hpp" #include "sprite.hpp" +#include "pathfinder/base.hpp" class Entity { public: @@ -56,10 +58,16 @@ public: void ZeroActualVelocityInDirection(WorldPos direction); + const pathfinder::Path& GetPath() const { return m_Path; } + pathfinder::Path& GetPath() { return m_Path; } + void SetPath(pathfinder::Path& path) { m_Path = path; } + std::optional GetMoveTarget(); + protected: WorldPos m_Position; WorldPos m_ActualVelocity; WorldPos m_RequestedVelocity; + pathfinder::Path m_Path; private: bool m_FlagExpired = false; diff --git a/cpp/src/gameloop.cpp b/cpp/src/gameloop.cpp index 5e35aa4..5a12f4e 100644 --- a/cpp/src/gameloop.cpp +++ b/cpp/src/gameloop.cpp @@ -27,12 +27,17 @@ void GameLoop::Draw() { } // draw the path, if it exists - WorldPos start_pos = m_Game->GetPlayer()->GetPosition(); - for (const auto &next_pos : m_Game->GetPath()) { - const auto &camera = m_Game->GetCamera(); - m_Window->DrawLine(camera.WorldToWindow(start_pos), - camera.WorldToWindow(next_pos)); - start_pos = next_pos; + + for (const auto& entity : m_Game->GetEntities()) + { + WorldPos start_pos = entity->GetPosition(); + for (const auto &next_pos : entity->GetPath()) + { + const auto &camera = m_Game->GetCamera(); + m_Window->DrawLine(camera.WorldToWindow(start_pos), + camera.WorldToWindow(next_pos)); + start_pos = next_pos; + } } // draw all the entities (player etc) @@ -49,9 +54,10 @@ void GameLoop::Run() { LOG_INFO("Running the game"); while (!m_Game->IsExitRequested()) { m_Game->HandleActions(m_UserInput->GetActions()); - m_Game->UpdatePlayerVelocity(); + m_Game->UpdateWorld(); // TODO measure fps, draw only if delay for target fps was reached + // or create a separate thread for drawing m_Window->ClearWindow(); Draw(); m_Window->Flush(); diff --git a/cpp/src/pathfindingdemo.cpp b/cpp/src/pathfindingdemo.cpp index a467c91..e013ea0 100644 --- a/cpp/src/pathfindingdemo.cpp +++ b/cpp/src/pathfindingdemo.cpp @@ -55,54 +55,57 @@ void PathFindingDemo::CreateMap() { m_Map.PaintLine(TilePos{84,81}, TilePos{84,96}, 1.0, TileType::WALL); m_Map.PaintLine(TilePos{78,87}, TilePos{78,100}, 1.0, TileType::WALL); - // add player + // add some controllable entities m_Entities.clear(); - m_Player = std::make_shared(); - m_Player->SetPosition(m_Map.TileToWorld(TilePos{25, 20})); - m_Entities.push_back(m_Player); + auto player = std::make_shared(); + player->SetPosition(m_Map.TileToWorld(TilePos{25, 20})); + AddEntity(player); + + auto player2 = std::make_shared(); + player2->SetPosition(m_Map.TileToWorld(TilePos{50, 20})); + AddEntity(player2); + + + // select everything - TODO this is just temporary for testing + for (const auto& entity : m_Entities) + { + m_SelectedEntities.push_back(entity); + } } WorldPos PathFindingDemo::GetRandomPosition() const { return WorldPos{0.0f, 0.0f}; // totally random! } -std::optional PathFindingDemo::GetMoveTarget() { - WorldPos player_current_pos = GetPlayer()->GetPosition(); - if (m_Path.empty()) { - return {}; - } - WorldPos player_next_pos = m_Path.front(); - - if (player_current_pos.DistanceTo(player_next_pos) > 1.0) { - // target not reached yet - return player_next_pos; - } - // target reached, pop it - //m_MoveQueue.pop(); - m_Path.erase(m_Path.begin()); - // return nothing - if there's next point in the queue, - // we'll get it in the next iteration - return {}; -} - -void PathFindingDemo::UpdatePlayerVelocity() { - auto player = GetPlayer(); - auto current_pos = player->GetPosition(); - double tile_velocity_coeff = m_Map.GetTileVelocityCoeff(current_pos); - auto next_pos = GetMoveTarget(); - WorldPos velocity = WorldPos{}; - if (next_pos) - { - velocity = next_pos.value() - current_pos; - velocity.Normalize(); - //LOG_DEBUG("I want to move to: ", next_pos.value(), - // ", velocity: ", velocity); - } - player->SetActualVelocity(velocity * tile_velocity_coeff); +// Update entity positions, handle collisions +void PathFindingDemo::UpdateWorld() { + float time_delta = 1.0f; - player->Update(time_delta); + + for (auto& entity : m_Entities) + { + // calculate the velocity + auto current_pos = entity->GetPosition(); + double tile_velocity_coeff = m_Map.GetTileVelocityCoeff(current_pos); + auto next_pos = entity->GetMoveTarget(); + WorldPos velocity = WorldPos{}; + if (next_pos) + { + velocity = next_pos.value() - current_pos; + velocity.Normalize(); + //LOG_DEBUG("I want to move to: ", next_pos.value(), + // ", velocity: ", velocity); + } + entity->SetActualVelocity(velocity * tile_velocity_coeff); + + + // handle collisions - these may modify the velocity + + // update the position + entity->Update(time_delta); + } } void PathFindingDemo::HandleActions(const std::vector &actions) @@ -116,10 +119,19 @@ void PathFindingDemo::HandleActions(const std::vector &actions) } else if (action.type == UserAction::Type::SET_MOVE_TARGET) { - WorldPos wp = m_Camera.WindowToWorld(action.Argument.position); - LOG_INFO("Calculating path to target: ", wp); - m_Path = m_PathFinder->CalculatePath(m_Player->GetPosition(), wp); - LOG_INFO("Done, path node count: ", m_Path.size()); + WorldPos target_pos = m_Camera.WindowToWorld(action.Argument.position); + for (auto& selected_entity : m_SelectedEntities) + { + LOG_INFO("Calculating path to target: ", target_pos); + if (auto sp = selected_entity.lock()) + { + auto path = m_PathFinder->CalculatePath(sp->GetPosition(), target_pos); + sp->SetPath(path); + LOG_INFO("Done, path node count: ", path.size()); + } else { + LOG_INFO("Cannot calculate path for destroyed entity (weak_ptr.lock() failed)"); + } + } } else if (action.type == UserAction::Type::SELECT_PATHFINDER) { diff --git a/cpp/src/pathfindingdemo.hpp b/cpp/src/pathfindingdemo.hpp index 9743c86..8a3d9c5 100644 --- a/cpp/src/pathfindingdemo.hpp +++ b/cpp/src/pathfindingdemo.hpp @@ -22,17 +22,14 @@ public: PathFindingDemo &operator=(const PathFindingDemo &) = delete; PathFindingDemo &operator=(PathFindingDemo &&) = delete; - std::shared_ptr GetPlayer() { return m_Player; } std::vector>& GetEntities() { return m_Entities; } const Map& GetMap() const { return m_Map; } const Camera& GetCamera() const { return m_Camera; } - const pathfinder::Path& GetPath() const { return m_Path; } bool IsExitRequested() const { return m_ExitRequested; } void AddEntity(std::shared_ptr e); void CreateMap(); - std::optional GetMoveTarget(); - void UpdatePlayerVelocity(); + void UpdateWorld(); void HandleActions(const std::vector &actions); WorldPos GetRandomPosition() const; @@ -41,7 +38,6 @@ private: Map m_Map; Camera m_Camera; std::vector> m_Entities; - std::shared_ptr m_Player; - pathfinder::Path m_Path; std::unique_ptr m_PathFinder; + std::vector> m_SelectedEntities; };