Working on minor fixing, working on pathfinding
This commit is contained in:
parent
a11c4614ea
commit
c9c6b7e7fb
@ -25,7 +25,7 @@ macro(check_and_download URL ARCHIVE_NAME EXTRACTED_DIR_NAME CHECK_FILE)
|
|||||||
endmacro()
|
endmacro()
|
||||||
|
|
||||||
# 1) ZLIB (Нужна только для инклудов, если не используете emscripten порты)
|
# 1) ZLIB (Нужна только для инклудов, если не используете emscripten порты)
|
||||||
check_and_download("https://www.zlib.net/zlib132.zip" "zlib132.zip" "zlib-1.3.2" "CMakeLists.txt")
|
check_and_download("https://github.com/madler/zlib/releases/download/v1.3.2/zlib132.zip" "zlib132.zip" "zlib-1.3.2" "CMakeLists.txt")
|
||||||
|
|
||||||
# 2) SDL2
|
# 2) SDL2
|
||||||
check_and_download("https://github.com/libsdl-org/SDL/archive/refs/tags/release-2.32.10.zip" "release-2.32.10.zip" "SDL-release-2.32.10" "CMakeLists.txt")
|
check_and_download("https://github.com/libsdl-org/SDL/archive/refs/tags/release-2.32.10.zip" "release-2.32.10.zip" "SDL-release-2.32.10" "CMakeLists.txt")
|
||||||
|
|||||||
@ -173,8 +173,8 @@ void Character::resetPlayerAfterDeath() {
|
|||||||
state.attack = 0;
|
state.attack = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Character::forceReplan() {
|
bool Character::forceReplan() {
|
||||||
if (!pathPlanner) return;
|
if (!pathPlanner) return false;
|
||||||
|
|
||||||
const Eigen::Vector3f normalizedTarget(state.requestedWalkTarget.x(), 0.f, state.requestedWalkTarget.z());
|
const Eigen::Vector3f normalizedTarget(state.requestedWalkTarget.x(), 0.f, state.requestedWalkTarget.z());
|
||||||
state.pathWaypoints = pathPlanner(state.position, normalizedTarget);
|
state.pathWaypoints = pathPlanner(state.position, normalizedTarget);
|
||||||
@ -185,12 +185,13 @@ void Character::forceReplan() {
|
|||||||
waypoint.y() = 0.f;
|
waypoint.y() = 0.f;
|
||||||
}
|
}
|
||||||
state.walkTarget = state.pathWaypoints.back();
|
state.walkTarget = state.pathWaypoints.back();
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.walkTarget = Eigen::Vector3f(state.position.x(), 0.f, state.position.z());
|
state.walkTarget = Eigen::Vector3f(state.position.x(), 0.f, state.position.z());
|
||||||
state.onArrivedCallback = nullptr;
|
state.onArrivedCallback = nullptr;
|
||||||
state.onArrivedCallbackName.clear();
|
state.onArrivedCallbackName.clear();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Character::setTexture(std::shared_ptr<FRG::Texture> texture) {
|
void Character::setTexture(std::shared_ptr<FRG::Texture> texture) {
|
||||||
|
|||||||
@ -59,7 +59,7 @@ public:
|
|||||||
void setTarget(const Eigen::Vector3f& target, std::function<void()> onArrived = nullptr, std::string callbackName = "");
|
void setTarget(const Eigen::Vector3f& target, std::function<void()> onArrived = nullptr, std::string callbackName = "");
|
||||||
void stopAndRotateToTarget(int npcId);
|
void stopAndRotateToTarget(int npcId);
|
||||||
|
|
||||||
void forceReplan();
|
bool forceReplan();
|
||||||
void stopInPlace() { state.stopInPlace(); }
|
void stopInPlace() { state.stopInPlace(); }
|
||||||
|
|
||||||
// ---- Reset ----
|
// ---- Reset ----
|
||||||
|
|||||||
@ -103,7 +103,8 @@ public:
|
|||||||
float modelScale = 0.12f;
|
float modelScale = 0.12f;
|
||||||
Eigen::Quaternionf modelCorrectionRotation = Eigen::Quaternionf::Identity();
|
Eigen::Quaternionf modelCorrectionRotation = Eigen::Quaternionf::Identity();
|
||||||
bool useGpuSkinning = true;
|
bool useGpuSkinning = true;
|
||||||
float collisionRadius = 0.45f;
|
//float collisionRadius = 0.45f;
|
||||||
|
float collisionRadius = 0.65f;
|
||||||
float interactionRadius = 0.0f;
|
float interactionRadius = 0.0f;
|
||||||
|
|
||||||
// --- Pathfinding data (serializable parts) ---
|
// --- Pathfinding data (serializable parts) ---
|
||||||
|
|||||||
@ -22,8 +22,8 @@ int Environment::width = CONST_DEFAULT_WIDTH;
|
|||||||
int Environment::height = CONST_DEFAULT_HEIGHT;
|
int Environment::height = CONST_DEFAULT_HEIGHT;
|
||||||
float Environment::zoom = DEFAULT_ZOOM;
|
float Environment::zoom = DEFAULT_ZOOM;
|
||||||
|
|
||||||
bool Environment::enableLogging = false;
|
//bool Environment::enableLogging = false;
|
||||||
//bool Environment::enableLogging = true;
|
bool Environment::enableLogging = true;
|
||||||
|
|
||||||
SDL_Window* Environment::window = nullptr;
|
SDL_Window* Environment::window = nullptr;
|
||||||
|
|
||||||
|
|||||||
12
src/Game.cpp
12
src/Game.cpp
@ -1452,6 +1452,18 @@ namespace FRG
|
|||||||
if (event.key.keysym.sym == SDLK_BACKSPACE) {
|
if (event.key.keysym.sym == SDLK_BACKSPACE) {
|
||||||
//menuManager.uiManager.onKeyBackspace();
|
//menuManager.uiManager.onKeyBackspace();
|
||||||
}
|
}
|
||||||
|
if (event.key.keysym.sym == SDLK_1)
|
||||||
|
{
|
||||||
|
startNightTransition();
|
||||||
|
}
|
||||||
|
if (event.key.keysym.sym == SDLK_2)
|
||||||
|
{
|
||||||
|
startDarklandsTransition();
|
||||||
|
}
|
||||||
|
if (event.key.keysym.sym == SDLK_3)
|
||||||
|
{
|
||||||
|
logger() << "Player position: " << currentLocation()->player->state.position.transpose() << std::endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.type == SDL_TEXTINPUT) {
|
if (event.type == SDL_TEXTINPUT) {
|
||||||
|
|||||||
113
src/Location.cpp
113
src/Location.cpp
@ -450,7 +450,16 @@ namespace FRG
|
|||||||
const auto addCharacter = [&](const Character* other) {
|
const auto addCharacter = [&](const Character* other) {
|
||||||
if (!other || other == self) return;
|
if (!other || other == self) return;
|
||||||
if (other->getHp() <= 0.f || !other->state.enabled) return;
|
if (other->getHp() <= 0.f || !other->state.enabled) return;
|
||||||
if (other->isMoving()) return;
|
|
||||||
|
// Проверяем, находятся ли персонажи в состоянии фактического столкновения
|
||||||
|
const float dist = (other->state.position - self->state.position).norm();
|
||||||
|
const float collisionDist = self->state.collisionRadius + other->state.collisionRadius;
|
||||||
|
|
||||||
|
// Добавляем epsilon (0.1f) для учета погрешностей при входе в зону коллизии
|
||||||
|
const bool isColliding = dist <= (collisionDist + 0.1f);
|
||||||
|
|
||||||
|
// Игнорируем движущегося персонажа, только если мы с ним НЕ сталкиваемся в данный момент
|
||||||
|
if (other->isMoving() && !isColliding) return;
|
||||||
|
|
||||||
if (distancePointToSegmentXZ(other->state.position, start, end) > kDynamicObstacleInfluenceDist) {
|
if (distancePointToSegmentXZ(other->state.position, start, end) > kDynamicObstacleInfluenceDist) {
|
||||||
return;
|
return;
|
||||||
@ -460,7 +469,7 @@ namespace FRG
|
|||||||
obs.position = Eigen::Vector3f(other->state.position.x(), navigation->getFloorY(), other->state.position.z());
|
obs.position = Eigen::Vector3f(other->state.position.x(), navigation->getFloorY(), other->state.position.z());
|
||||||
obs.radius = (std::max)(0.0f, other->state.collisionRadius * 0.6f);
|
obs.radius = (std::max)(0.0f, other->state.collisionRadius * 0.6f);
|
||||||
dynamicObstacles.push_back(obs);
|
dynamicObstacles.push_back(obs);
|
||||||
};
|
};
|
||||||
|
|
||||||
addCharacter(player.get());
|
addCharacter(player.get());
|
||||||
for (const auto& npc : npcs) {
|
for (const auto& npc : npcs) {
|
||||||
@ -468,8 +477,8 @@ namespace FRG
|
|||||||
}
|
}
|
||||||
|
|
||||||
return navigation->findPathToNearest(start, end, dynamicObstacles);
|
return navigation->findPathToNearest(start, end, dynamicObstacles);
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
|
||||||
if (player) {
|
if (player) {
|
||||||
player->setPathPlanner(makePlanner(player.get()));
|
player->setPathPlanner(makePlanner(player.get()));
|
||||||
@ -591,6 +600,9 @@ namespace FRG
|
|||||||
}
|
}
|
||||||
|
|
||||||
Character* Location::raycastNpcs(const Eigen::Vector3f& rayOrigin, const Eigen::Vector3f& rayDir, float maxDistance) {
|
Character* Location::raycastNpcs(const Eigen::Vector3f& rayOrigin, const Eigen::Vector3f& rayDir, float maxDistance) {
|
||||||
|
|
||||||
|
//return nullptr;
|
||||||
|
|
||||||
// Every NPC is treated as a vertical cylinder: radius 1.0m, height 1.85m,
|
// Every NPC is treated as a vertical cylinder: radius 1.0m, height 1.85m,
|
||||||
// base at npc->position (the model's foot). Intersection = circle hit in the
|
// base at npc->position (the model's foot). Intersection = circle hit in the
|
||||||
// XZ plane, then clip the entry/exit t against the [yFoot, yFoot+height] slab.
|
// XZ plane, then clip the entry/exit t against the [yFoot, yFoot+height] slab.
|
||||||
@ -654,7 +666,6 @@ namespace FRG
|
|||||||
else {
|
else {
|
||||||
//logger() << "[RAYCAST_NPC] No NPC hit" << std::endl;
|
//logger() << "[RAYCAST_NPC] No NPC hit" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
return closestNpc;
|
return closestNpc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1413,53 +1424,83 @@ namespace FRG
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Eigen::Vector2f normal(1.f, 0.f);
|
// 1. ПЕРЕСТРОЕНИЕ ПУТИ (выполняется при пересечении обычного радиуса коллизии)
|
||||||
if (dist > kMinSeparationEps) {
|
|
||||||
normal = delta / dist;
|
|
||||||
}
|
|
||||||
|
|
||||||
const float penetration = (minDist - dist);
|
|
||||||
const float push = penetration * 0.5f;
|
|
||||||
|
|
||||||
Eigen::Vector3f newA = a->state.position;
|
|
||||||
Eigen::Vector3f newB = b->state.position;
|
|
||||||
newA.x() -= normal.x() * push;
|
|
||||||
newA.z() -= normal.y() * push;
|
|
||||||
newA.y() = 0.f;
|
|
||||||
newB.x() += normal.x() * push;
|
|
||||||
newB.z() += normal.y() * push;
|
|
||||||
newB.y() = 0.f;
|
|
||||||
|
|
||||||
const bool aWasMoving = a->isMoving();
|
const bool aWasMoving = a->isMoving();
|
||||||
const bool bWasMoving = b->isMoving();
|
const bool bWasMoving = b->isMoving();
|
||||||
|
|
||||||
if (navigation && navigation->isReady()) {
|
auto tryReplan = [&](Character* c) {
|
||||||
const bool aOk = navigation->isWalkable(newA);
|
if (replanCooldownRemainingMs.find(c) != replanCooldownRemainingMs.end()) {
|
||||||
const bool bOk = navigation->isWalkable(newB);
|
return; // Уже находится в откате
|
||||||
|
}
|
||||||
|
if (c->forceReplan()) {
|
||||||
|
replanCooldownRemainingMs[c] = 500; // таймаут на спам
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (aOk && bOk) {
|
if (aWasMoving || bWasMoving) {
|
||||||
|
if (aWasMoving && bWasMoving) {
|
||||||
|
tryReplan(a);
|
||||||
|
tryReplan(b);
|
||||||
|
}
|
||||||
|
else if (aWasMoving) {
|
||||||
|
tryReplan(a);
|
||||||
|
}
|
||||||
|
else if (bWasMoving) {
|
||||||
|
tryReplan(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. ФИЗИЧЕСКОЕ ВЫТАЛКИВАНИЕ (только если дистанция меньше 0.5 * minDist)
|
||||||
|
const float hardMinDist = minDist * 0.5f;
|
||||||
|
if (dist < hardMinDist) {
|
||||||
|
Eigen::Vector2f normal(1.f, 0.f);
|
||||||
|
if (dist > kMinSeparationEps) {
|
||||||
|
normal = delta / dist;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Вычисляем выталкивание относительно hardMinDist, а не minDist
|
||||||
|
const float penetration = (hardMinDist - dist);
|
||||||
|
const float push = penetration * 0.5f;
|
||||||
|
|
||||||
|
Eigen::Vector3f newA = a->state.position;
|
||||||
|
Eigen::Vector3f newB = b->state.position;
|
||||||
|
newA.x() -= normal.x() * push;
|
||||||
|
newA.z() -= normal.y() * push;
|
||||||
|
newA.y() = 0.f;
|
||||||
|
newB.x() += normal.x() * push;
|
||||||
|
newB.z() += normal.y() * push;
|
||||||
|
newB.y() = 0.f;
|
||||||
|
|
||||||
|
if (navigation && navigation->isReady()) {
|
||||||
|
const bool aOk = navigation->isWalkable(newA);
|
||||||
|
const bool bOk = navigation->isWalkable(newB);
|
||||||
|
|
||||||
|
if (aOk && bOk) {
|
||||||
|
a->state.position = newA;
|
||||||
|
b->state.position = newB;
|
||||||
|
}
|
||||||
|
else if (aOk && !bOk) {
|
||||||
|
a->state.position = newA;
|
||||||
|
}
|
||||||
|
else if (!aOk && bOk) {
|
||||||
|
b->state.position = newB;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
a->state.position = newA;
|
a->state.position = newA;
|
||||||
b->state.position = newB;
|
b->state.position = newB;
|
||||||
}
|
}
|
||||||
else if (aOk && !bOk) {
|
|
||||||
a->state.position = newA;
|
|
||||||
}
|
|
||||||
else if (!aOk && bOk) {
|
|
||||||
b->state.position = newB;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
a->state.position = newA;
|
|
||||||
b->state.position = newB;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 3. ОСТАНОВКА И КОЛЛБЭКИ
|
||||||
if (a->state.isPlayer && !aWasMoving) a->stopInPlace();
|
if (a->state.isPlayer && !aWasMoving) a->stopInPlace();
|
||||||
if (b->state.isPlayer && !bWasMoving) b->stopInPlace();
|
if (b->state.isPlayer && !bWasMoving) b->stopInPlace();
|
||||||
|
|
||||||
if (aWasMoving && !bWasMoving) {
|
if (aWasMoving && !bWasMoving) {
|
||||||
nudgeCharacterAside(b, a->state.position);
|
nudgeCharacterAside(b, a->state.position);
|
||||||
fireBumpCallbacks(a, b);
|
fireBumpCallbacks(a, b);
|
||||||
} else if (bWasMoving && !aWasMoving) {
|
}
|
||||||
|
else if (bWasMoving && !aWasMoving) {
|
||||||
nudgeCharacterAside(a, b->state.position);
|
nudgeCharacterAside(a, b->state.position);
|
||||||
fireBumpCallbacks(b, a);
|
fireBumpCallbacks(b, a);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -124,6 +124,68 @@ void PathFinder::build(const std::string& configPath,
|
|||||||
<< ", cell=" << cellSize << ", areas=" << areas.size() << "\n";
|
<< ", cell=" << cellSize << ", areas=" << areas.size() << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<Eigen::Vector3f> PathFinder::findPath(const Eigen::Vector3f& start,
|
||||||
|
const Eigen::Vector3f& end) const
|
||||||
|
{
|
||||||
|
return runAStar(start, end, walkable, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Eigen::Vector3f> PathFinder::findPath(const Eigen::Vector3f& start,
|
||||||
|
const Eigen::Vector3f& end,
|
||||||
|
const std::vector<DynamicObstacle>& dynamicObstacles) const
|
||||||
|
{
|
||||||
|
if (!ready || walkable.empty()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<unsigned char> walkableGrid = walkable;
|
||||||
|
// --- Логика внесения dynamicObstacles остается здесь ---
|
||||||
|
if (!dynamicObstacles.empty()) {
|
||||||
|
for (const DynamicObstacle& obstacle : dynamicObstacles) {
|
||||||
|
const float radius = obstacle.radius + agentRadius;
|
||||||
|
if (radius <= 0.0f) continue;
|
||||||
|
|
||||||
|
const float minWorldX = obstacle.position.x() - radius;
|
||||||
|
const float maxWorldX = obstacle.position.x() + radius;
|
||||||
|
const float minWorldZ = obstacle.position.z() - radius;
|
||||||
|
const float maxWorldZ = obstacle.position.z() + radius;
|
||||||
|
|
||||||
|
const int minCellX = (std::max)(0, static_cast<int>(std::floor((minWorldX - minX) / cellSize)));
|
||||||
|
const int maxCellX = (std::min)(gridWidth - 1, static_cast<int>(std::floor((maxWorldX - minX) / cellSize)));
|
||||||
|
const int minCellZ = (std::max)(0, static_cast<int>(std::floor((minWorldZ - minZ) / cellSize)));
|
||||||
|
const int maxCellZ = (std::min)(gridDepth - 1, static_cast<int>(std::floor((maxWorldZ - minZ) / cellSize)));
|
||||||
|
|
||||||
|
const float radiusSq = radius * radius;
|
||||||
|
for (int z = minCellZ; z <= maxCellZ; ++z) {
|
||||||
|
for (int x = minCellX; x <= maxCellX; ++x) {
|
||||||
|
const Cell cell{ x, z };
|
||||||
|
const Eigen::Vector3f center = cellCenter(cell);
|
||||||
|
const float dx = center.x() - obstacle.position.x();
|
||||||
|
const float dz = center.z() - obstacle.position.z();
|
||||||
|
if (dx * dx + dz * dz <= radiusSq) {
|
||||||
|
const int idx = indexOf(cell);
|
||||||
|
if (idx >= 0 && idx < static_cast<int>(walkableGrid.size())) {
|
||||||
|
walkableGrid[static_cast<size_t>(idx)] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return runAStar(start, end, walkableGrid, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Eigen::Vector3f> PathFinder::findNearestReachableImpl(
|
||||||
|
const Eigen::Vector3f& start,
|
||||||
|
const Eigen::Vector3f& end,
|
||||||
|
const std::vector<unsigned char>& walkableGrid) const
|
||||||
|
{
|
||||||
|
return runAStar(start, end, walkableGrid, true);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
|
||||||
std::vector<Eigen::Vector3f> PathFinder::findPath(const Eigen::Vector3f& start,
|
std::vector<Eigen::Vector3f> PathFinder::findPath(const Eigen::Vector3f& start,
|
||||||
const Eigen::Vector3f& end) const
|
const Eigen::Vector3f& end) const
|
||||||
{
|
{
|
||||||
@ -494,7 +556,7 @@ std::vector<Eigen::Vector3f> PathFinder::findNearestReachableImpl(
|
|||||||
path.erase(path.begin());
|
path.erase(path.begin());
|
||||||
|
|
||||||
return path;
|
return path;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
std::vector<Eigen::Vector3f> PathFinder::findPathToNearest(
|
std::vector<Eigen::Vector3f> PathFinder::findPathToNearest(
|
||||||
const Eigen::Vector3f& start, const Eigen::Vector3f& end) const
|
const Eigen::Vector3f& start, const Eigen::Vector3f& end) const
|
||||||
@ -1075,4 +1137,125 @@ bool PathFinder::pointInPolygon(float x, float z, const std::vector<Eigen::Vecto
|
|||||||
return inside;
|
return inside;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<Eigen::Vector3f> PathFinder::runAStar(
|
||||||
|
const Eigen::Vector3f& start,
|
||||||
|
const Eigen::Vector3f& end,
|
||||||
|
const std::vector<unsigned char>& grid,
|
||||||
|
bool returnNearestOnFail) const
|
||||||
|
{
|
||||||
|
if (!ready || grid.empty()) return {};
|
||||||
|
|
||||||
|
Cell startCell, endCell;
|
||||||
|
if (!findNearestWalkableCell(start, startCell, grid) ||
|
||||||
|
!findNearestWalkableCell(end, endCell, grid)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startCell.x == endCell.x && startCell.z == endCell.z) {
|
||||||
|
return { cellCenter(endCell) };
|
||||||
|
}
|
||||||
|
|
||||||
|
struct QueueNode {
|
||||||
|
int index = 0;
|
||||||
|
float priority = 0.0f;
|
||||||
|
bool operator<(const QueueNode& other) const { return priority > other.priority; }
|
||||||
|
};
|
||||||
|
|
||||||
|
const int cellCount = gridWidth * gridDepth;
|
||||||
|
std::vector<float> cost(static_cast<size_t>(cellCount), (std::numeric_limits<float>::max)());
|
||||||
|
std::vector<int> cameFrom(static_cast<size_t>(cellCount), -1);
|
||||||
|
std::priority_queue<QueueNode> open;
|
||||||
|
|
||||||
|
const int startIndex = indexOf(startCell);
|
||||||
|
const int endIndex = indexOf(endCell);
|
||||||
|
|
||||||
|
cost[static_cast<size_t>(startIndex)] = 0.0f;
|
||||||
|
open.push({ startIndex, 0.0f });
|
||||||
|
|
||||||
|
int bestIndex = startIndex;
|
||||||
|
float bestDist = distanceCells(startCell, endCell);
|
||||||
|
|
||||||
|
static const int offsets[8][2] = {
|
||||||
|
{ 1, 0 }, { -1, 0 }, { 0, 1 }, { 0, -1 },
|
||||||
|
{ 1, 1 }, { 1, -1 }, { -1, 1 }, { -1, -1 }
|
||||||
|
};
|
||||||
|
|
||||||
|
bool reached = false;
|
||||||
|
|
||||||
|
while (!open.empty()) {
|
||||||
|
const QueueNode current = open.top();
|
||||||
|
open.pop();
|
||||||
|
|
||||||
|
if (current.index == endIndex) {
|
||||||
|
bestIndex = endIndex;
|
||||||
|
reached = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Cell currentCell{ current.index % gridWidth, current.index / gridWidth };
|
||||||
|
|
||||||
|
if (cost[static_cast<size_t>(current.index)] < current.priority - distanceCells(currentCell, endCell)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (returnNearestOnFail) {
|
||||||
|
const float d = distanceCells(currentCell, endCell);
|
||||||
|
if (d < bestDist) {
|
||||||
|
bestDist = d;
|
||||||
|
bestIndex = current.index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& offset : offsets) {
|
||||||
|
Cell next{ currentCell.x + offset[0], currentCell.z + offset[1] };
|
||||||
|
if (!isCellWalkable(next, grid)) continue;
|
||||||
|
|
||||||
|
const bool diagonal = offset[0] != 0 && offset[1] != 0;
|
||||||
|
if (diagonal) {
|
||||||
|
Cell h{ currentCell.x + offset[0], currentCell.z };
|
||||||
|
Cell v{ currentCell.x, currentCell.z + offset[1] };
|
||||||
|
if (!isCellWalkable(h, grid) || !isCellWalkable(v, grid)) continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int nextIndex = indexOf(next);
|
||||||
|
const float stepCost = diagonal ? 1.41421356f : 1.0f;
|
||||||
|
const float newCost = cost[static_cast<size_t>(current.index)] + stepCost;
|
||||||
|
|
||||||
|
if (newCost >= cost[static_cast<size_t>(nextIndex)]) continue;
|
||||||
|
|
||||||
|
cost[static_cast<size_t>(nextIndex)] = newCost;
|
||||||
|
cameFrom[static_cast<size_t>(nextIndex)] = current.index;
|
||||||
|
open.push({ nextIndex, newCost + distanceCells(next, endCell) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!reached && !returnNearestOnFail) return {};
|
||||||
|
if (bestIndex == startIndex) return {};
|
||||||
|
|
||||||
|
std::vector<Cell> cells;
|
||||||
|
for (int cur = bestIndex; cur != -1; cur = cameFrom[static_cast<size_t>(cur)]) {
|
||||||
|
cells.push_back({ cur % gridWidth, cur / gridWidth });
|
||||||
|
if (cur == startIndex) break;
|
||||||
|
}
|
||||||
|
std::reverse(cells.begin(), cells.end());
|
||||||
|
cells = smoothCells(cells, grid);
|
||||||
|
|
||||||
|
std::vector<Eigen::Vector3f> path;
|
||||||
|
path.reserve(cells.size());
|
||||||
|
for (const Cell& cell : cells) path.push_back(cellCenter(cell));
|
||||||
|
|
||||||
|
if (!path.empty() && (path.front() - Eigen::Vector3f(start.x(), floorY, start.z())).norm() < cellSize * 0.75f) {
|
||||||
|
path.erase(path.begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reached && !path.empty()) {
|
||||||
|
Cell requestedEndCell;
|
||||||
|
if (worldToCell(end, requestedEndCell) && requestedEndCell.x == endCell.x && requestedEndCell.z == endCell.z) {
|
||||||
|
path.back() = Eigen::Vector3f(end.x(), floorY, end.z());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace FRG
|
} // namespace FRG
|
||||||
|
|||||||
@ -109,6 +109,11 @@ private:
|
|||||||
std::vector<Eigen::Vector3f> findNearestReachableImpl(const Eigen::Vector3f& start,
|
std::vector<Eigen::Vector3f> findNearestReachableImpl(const Eigen::Vector3f& start,
|
||||||
const Eigen::Vector3f& end,
|
const Eigen::Vector3f& end,
|
||||||
const std::vector<unsigned char>& walkableGrid) const;
|
const std::vector<unsigned char>& walkableGrid) const;
|
||||||
|
|
||||||
|
std::vector<Eigen::Vector3f> runAStar(const Eigen::Vector3f& start,
|
||||||
|
const Eigen::Vector3f& end,
|
||||||
|
const std::vector<unsigned char>& grid,
|
||||||
|
bool returnNearestOnFail) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace FRG
|
} // namespace FRG
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user