Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions include/omath/pathfinding/walk_bot.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//
// Created by orange on 4/12/2026.
//
#pragma once
#include "navigation_mesh.hpp"
#include "omath/linear_algebra/vector3.hpp"
#include <functional>
#include <memory>
namespace omath::pathfinding
{
enum class WalkBotStatus
{
IDLE,
PATHING,
FINISHED
};
class WalkBot
{
public:
WalkBot() = default;
explicit WalkBot(const std::shared_ptr<NavigationMesh>& mesh, float min_node_distance = 1.f);

void set_nav_mesh(const std::shared_ptr<NavigationMesh>& mesh);
void set_min_node_distance(float distance);

void set_target(const Vector3<float>& target);

// Clear navigation state so the bot can be re-routed without stale
// visited-node memory.
void reset();

// Call every game tick with the current bot world position.
void update(const Vector3<float>& bot_position);

void on_path(const std::function<void(const Vector3<float>&)>& callback);
void on_status(const std::function<void(WalkBotStatus)>& callback);

private:
std::weak_ptr<NavigationMesh> m_nav_mesh;
std::optional<std::function<void(const Vector3<float>&)>> m_on_next_path_node;
std::optional<std::function<void(WalkBotStatus)>> m_on_status_update;
std::optional<Vector3<float>> m_last_visited;
std::optional<Vector3<float>> m_target;
float m_min_node_distance{1.f};
};
} // namespace omath::pathfinding
92 changes: 92 additions & 0 deletions source/pathfinding/walk_bot.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
//
// Created by orange on 4/12/2026.
//
#include "omath/pathfinding/walk_bot.hpp"
#include "omath/pathfinding/a_star.hpp"

namespace omath::pathfinding
{

WalkBot::WalkBot(const std::shared_ptr<NavigationMesh>& mesh, const float min_node_distance)
: m_nav_mesh(mesh), m_min_node_distance(min_node_distance) {}

void WalkBot::set_nav_mesh(const std::shared_ptr<NavigationMesh>& mesh)
{
m_nav_mesh = mesh;
}

void WalkBot::set_min_node_distance(const float distance)
{
m_min_node_distance = distance;
}

void WalkBot::set_target(const Vector3<float>& target)
{
m_target = target;
}

void WalkBot::reset()
{
m_last_visited.reset();
}

void WalkBot::update(const Vector3<float>& bot_position)
{
if (!m_target.has_value())
return;

if (m_target->distance_to(bot_position) <= m_min_node_distance)
{
if (m_on_status_update.has_value())
m_on_status_update->operator()(WalkBotStatus::FINISHED);
return;
}

if (!m_on_next_path_node.has_value())
return;

const auto nav_mesh = m_nav_mesh.lock();
if (!nav_mesh)
{
if (m_on_status_update.has_value())
m_on_status_update->operator()(WalkBotStatus::IDLE);
return;
}

const auto path = Astar::find_path(bot_position, *m_target, *nav_mesh);
if (path.empty())
{
if (m_on_status_update.has_value())
m_on_status_update->operator()(WalkBotStatus::IDLE);
return;
}

const auto& nearest = path.front();

// Record the nearest node as visited once we are close enough to it.
if (nearest.distance_to(bot_position) <= m_min_node_distance)
m_last_visited = nearest;

// If the nearest node was already visited, advance to the next one so
// we never oscillate back to a node we just left.
// If the bot was displaced (blown back), nearest will be an unvisited
// node, so we route to it first before continuing forward.
if (m_last_visited.has_value() && *m_last_visited == nearest && path.size() > 1)
m_on_next_path_node->operator()(path[1]);
else
m_on_next_path_node->operator()(nearest);

if (m_on_status_update.has_value())
m_on_status_update->operator()(WalkBotStatus::PATHING);
}

void WalkBot::on_path(const std::function<void(const Vector3<float>&)>& callback)
{
m_on_next_path_node = callback;
}

void WalkBot::on_status(const std::function<void(WalkBotStatus)>& callback)
{
m_on_status_update = callback;
}
} // namespace omath::pathfinding
Loading
Loading