Pathfinder WIP

This commit is contained in:
Jan Mrna 2025-09-27 17:58:26 +02:00
parent 9b08c057e0
commit ea316ab997
5 changed files with 74 additions and 59 deletions

View File

@ -1,14 +1,45 @@
#pragma once #pragma once
#include <vector> #include <vector>
#include <memory>
#include "math.hpp" #include "math.hpp"
#include "map.hpp"
namespace pathfinder { namespace pathfinder {
using Path = std::vector<TilePos>; using Path = std::vector<WorldPos>;
class PathFinderBase { enum class PathFinderType {
LINEAR = 1,
BFS,
COUNT,
};
class PathFinderBase {
public:
PathFinderBase() = default;
~PathFinderBase() = default;
PathFinderBase(const PathFinderBase&) = delete;
PathFinderBase(PathFinderBase&&) = delete;
PathFinderBase& operator=(const PathFinderBase&) = delete;
PathFinderBase& operator=(PathFinderBase&&) = delete;
void SetMap(std::shared_ptr<Map> map);
virtual Path CalculatePath(WorldPos target) = 0;
private:
std::shared_ptr<Map> m_Map;
};
class LinearPathFinder : public PathFinderBase {
Path CalculatePath(WorldPos target) override;
};
std::unique_ptr<PathFinderBase> create(PathFinderType type);
} // pathfinder namespace
};
}

View File

@ -9,9 +9,14 @@
#include "log.hpp" #include "log.hpp"
#include "map.hpp" #include "map.hpp"
#include "user_input.hpp" #include "user_input.hpp"
#include "pathfinder.hpp"
PathFindingDemo::PathFindingDemo(int width, int height) : m_Map(width, height) { PathFindingDemo::PathFindingDemo(int width, int height) :
m_Map(width, height)
{
LOG_DEBUG("."); LOG_DEBUG(".");
// set default pathfinder method
m_PathFinder = pathfinder::create(pathfinder::PathFinderType::LINEAR);
} }
PathFindingDemo::~PathFindingDemo() { LOG_DEBUG("."); } PathFindingDemo::~PathFindingDemo() { LOG_DEBUG("."); }
@ -35,19 +40,21 @@ WorldPos PathFindingDemo::GetRandomPosition() const {
std::optional<WorldPos> PathFindingDemo::GetMoveTarget() { std::optional<WorldPos> PathFindingDemo::GetMoveTarget() {
WorldPos current_player_pos = GetPlayer()->GetPosition(); WorldPos current_player_pos = GetPlayer()->GetPosition();
if (m_MoveQueue.empty()) { if (m_Path.empty()) {
return {}; return {};
} }
WorldPos next_player_pos = m_MoveQueue.front(); 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) > 10.0) {
// target not reached yet // target not reached yet
return next_player_pos; return next_player_pos;
} }
// target reached, pop it // target reached, pop it
m_MoveQueue.pop(); //m_MoveQueue.pop();
// return nothing - we'll get the next value in the next iteration 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 {}; return {};
} }
@ -56,7 +63,7 @@ void PathFindingDemo::UpdatePlayerVelocity() {
auto current_pos = player->GetPosition(); auto current_pos = player->GetPosition();
double tile_velocity_coeff = m_Map.GetTileVelocityCoeff(current_pos); double tile_velocity_coeff = m_Map.GetTileVelocityCoeff(current_pos);
auto next_pos = GetMoveTarget(); auto next_pos = GetMoveTarget();
auto velocity = WorldPos{}; WorldPos velocity = WorldPos{};
if (next_pos) { if (next_pos) {
velocity = next_pos.value() - current_pos; velocity = next_pos.value() - current_pos;
velocity.normalize(); velocity.normalize();
@ -73,19 +80,15 @@ void PathFindingDemo::HandleActions(const std::vector<UserAction> &actions) {
if (action.type == UserAction::Type::EXIT) { if (action.type == UserAction::Type::EXIT) {
LOG_INFO("Exit requested"); LOG_INFO("Exit requested");
m_ExitRequested = true; m_ExitRequested = true;
} else if (action.type == UserAction::Type::FIRE) { } else if (action.type == UserAction::Type::SET_MOVE_TARGET) {
LOG_INFO("Fire");
// AddEntity(m_Player->CreateBomb());
} else if (action.type == UserAction::Type::MOVE) {
LOG_INFO("Move direction ", action.Argument.position);
m_Player->SetRequestedVelocity(action.Argument.position * 4.0f);
} else if (action.type == UserAction::Type::MOVE_TARGET) {
WorldPos wp = action.Argument.position; WorldPos wp = action.Argument.position;
TilePos p = m_Map.WorldToTile(wp); LOG_INFO("Calculating path to target: ", wp);
LOG_INFO("Clearing current move queue and inserting new target: ", wp); m_Path = m_PathFinder->CalculatePath(wp);
std::queue<WorldPos> empty; LOG_INFO("Done, path node count: ", m_Path.size());
std::swap(empty, m_MoveQueue); } else if (action.type == UserAction::Type::SELECT_PATHFINDER) {
m_MoveQueue.push(wp); using namespace pathfinder;
PathFinderType type = static_cast<PathFinderType>(action.Argument.number);
m_PathFinder = create(type);
} }
}; };
} }

View File

@ -9,6 +9,7 @@
#include "log.hpp" #include "log.hpp"
#include "map.hpp" #include "map.hpp"
#include "user_input.hpp" #include "user_input.hpp"
#include "pathfinder.hpp"
class PathFindingDemo { class PathFindingDemo {
public: public:
@ -34,8 +35,9 @@ public:
private: private:
bool m_ExitRequested = false; bool m_ExitRequested = false;
Map m_Map;
std::vector<std::shared_ptr<Entity>> m_Entities; std::vector<std::shared_ptr<Entity>> m_Entities;
std::shared_ptr<Player> m_Player; std::shared_ptr<Player> m_Player;
std::queue<WorldPos> m_MoveQueue; pathfinder::Path m_Path;
Map m_Map; std::unique_ptr<pathfinder::PathFinderBase> m_PathFinder;
}; };

View File

@ -22,7 +22,6 @@ std::expected<void, std::string> UserInput::Init() { return {}; }
const std::vector<UserAction> &UserInput::GetActions() { const std::vector<UserAction> &UserInput::GetActions() {
m_Actions.clear(); m_Actions.clear();
static WorldPos move_direction = {0.0f, 0.0f}; static WorldPos move_direction = {0.0f, 0.0f};
static bool send_move_action = false;
SDL_Event event; SDL_Event event;
while (SDL_PollEvent(&event)) { while (SDL_PollEvent(&event)) {
@ -41,34 +40,16 @@ const std::vector<UserAction> &UserInput::GetActions() {
m_Actions.emplace_back(UserAction::Type::EXIT); m_Actions.emplace_back(UserAction::Type::EXIT);
// further processing of inputs is not needed // further processing of inputs is not needed
return m_Actions; return m_Actions;
case 'w': case '1':
case 's': case '2':
case 'a': case '3':
case 'd': case '4':
case SDLK_UP: if (key_down) {
case SDLK_DOWN: int selection = kbd_event.key - '0';
case SDLK_LEFT: m_Actions.emplace_back(UserAction::Type::SELECT_PATHFINDER, selection);
case SDLK_RIGHT: { LOG_INFO("Pathfinder selected: ", selection);
static std::map<char, WorldPos> move_base{ }
{'w', {0.0, 1.0}}, break;
{'s', {0.0, -1.0}},
{'a', {1.0, 0.0}},
{'d', {-1.0, 0.0}},
{static_cast<char>(SDLK_UP), {0.0, 1.0}},
{static_cast<char>(SDLK_DOWN), {0.0, -1.0}},
{static_cast<char>(SDLK_LEFT), {1.0, 0.0}},
{static_cast<char>(SDLK_RIGHT), {-1.0, 0.0}},
};
float direction = key_down ? -1.0f : 1.0f;
send_move_action = true;
move_direction += move_base[kbd_event.key] * direction;
break;
}
case SDLK_SPACE:
if (key_down)
m_Actions.emplace_back(UserAction::Type::FIRE);
break;
default: default:
LOG_INFO("Key '", static_cast<char>(kbd_event.key), "' not mapped"); LOG_INFO("Key '", static_cast<char>(kbd_event.key), "' not mapped");
break; break;
@ -76,16 +57,12 @@ const std::vector<UserAction> &UserInput::GetActions() {
} else if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN) { } else if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN) {
SDL_MouseButtonEvent mouse_event = event.button; SDL_MouseButtonEvent mouse_event = event.button;
LOG_DEBUG("Mouse down: ", mouse_event.x, ", ", mouse_event.y); LOG_DEBUG("Mouse down: ", mouse_event.x, ", ", mouse_event.y);
m_Actions.emplace_back(UserAction::Type::MOVE_TARGET, m_Actions.emplace_back(UserAction::Type::SET_MOVE_TARGET,
WorldPos{mouse_event.x, mouse_event.y}); WorldPos{mouse_event.x, mouse_event.y});
} else { } else {
// TODO uncomment, for now too much noise // TODO uncomment, for now too much noise
// LOG_WARNING("Action not processed"); // LOG_WARNING("Action not processed");
} }
} }
if (send_move_action) {
m_Actions.emplace_back(UserAction::Type::MOVE, move_direction.normalized());
send_move_action = false;
}
return m_Actions; return m_Actions;
} }

View File

@ -9,12 +9,13 @@
class UserAction { class UserAction {
public: public:
enum class Type { NONE, EXIT, MOVE, CROUCH, STAND, FIRE, MOVE_TARGET }; enum class Type { NONE, EXIT, SET_MOVE_TARGET, SELECT_PATHFINDER };
UserAction() = default; UserAction() = default;
UserAction(Type t) : type(t) {} UserAction(Type t) : type(t) {}
UserAction(Type t, char key) : type(t), Argument{.key = key} {} UserAction(Type t, char key) : type(t), Argument{.key = key} {}
UserAction(Type t, WorldPos v) : type(t), Argument{.position = v} {} UserAction(Type t, WorldPos v) : type(t), Argument{.position = v} {}
UserAction(Type t, int arg) : type(t), Argument{.number = arg} {}
~UserAction() = default; ~UserAction() = default;
Type type; Type type;
@ -22,6 +23,7 @@ public:
union { union {
WorldPos position; WorldPos position;
char key; char key;
int number;
} Argument; } Argument;
}; };