Big rework to A*; factored out path linking
This commit is contained in:
parent
80e990c432
commit
239054df49
116
AdventureMap.cpp
116
AdventureMap.cpp
@ -7,7 +7,6 @@
|
|||||||
#include "AdventureCharacter.h"
|
#include "AdventureCharacter.h"
|
||||||
#include "Kismet/GameplayStatics.h"
|
#include "Kismet/GameplayStatics.h"
|
||||||
#include "Algo/Reverse.h"
|
#include "Algo/Reverse.h"
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
// Sets default values
|
// Sets default values
|
||||||
AAdventureMap::AAdventureMap()
|
AAdventureMap::AAdventureMap()
|
||||||
@ -32,21 +31,17 @@ void AAdventureMap::MakeGrid()
|
|||||||
float HexWidth = sqrt(3) * TileSize;
|
float HexWidth = sqrt(3) * TileSize;
|
||||||
int QOffset = 0;
|
int QOffset = 0;
|
||||||
|
|
||||||
for (int r = 1; r <= GridSize; r++)
|
for (int r = 1; r <= GridSize; r++) {
|
||||||
{
|
|
||||||
float XOffset = 0.f;
|
float XOffset = 0.f;
|
||||||
|
|
||||||
if (r % 2 != 0)
|
if (r % 2 != 0) {
|
||||||
{
|
if (r > 1) {
|
||||||
if (r > 1)
|
|
||||||
{
|
|
||||||
QOffset--;
|
QOffset--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else { XOffset = HexWidth / 2; }
|
else { XOffset = HexWidth / 2; }
|
||||||
|
|
||||||
for (int q = 1; q <= GridSize; q++)
|
for (int q = 1; q <= GridSize; q++) {
|
||||||
{
|
|
||||||
NextHexAt.X = XOffset + (HexWidth * q);
|
NextHexAt.X = XOffset + (HexWidth * q);
|
||||||
NextHexAt.Y = TileSize * 1.5f * r;
|
NextHexAt.Y = TileSize * 1.5f * r;
|
||||||
NextHexAt.Z = 0.f;
|
NextHexAt.Z = 0.f;
|
||||||
@ -61,8 +56,7 @@ void AAdventureMap::MakeGrid()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& tile : Grid)
|
for (auto& tile : Grid) {
|
||||||
{
|
|
||||||
tile->Index = GridIndex(tile->Q, tile->R);
|
tile->Index = GridIndex(tile->Q, tile->R);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,77 +89,69 @@ TArray<AHexTile*> AAdventureMap::Neighbors(AHexTile* OfHex)
|
|||||||
TArray<int32> Indeces;
|
TArray<int32> Indeces;
|
||||||
Indeces.Append(Arr, UE_ARRAY_COUNT(Arr));
|
Indeces.Append(Arr, UE_ARRAY_COUNT(Arr));
|
||||||
|
|
||||||
Neighbors.Add(RandomHex());
|
Neighbors.Add(RandomHex()); // pathetic but necessary
|
||||||
for (auto& I : Indeces) { if (Grid.IsValidIndex(I) && OfHex->Distance(Grid[I]) == 1) { Neighbors.Add(Grid[I]); } }
|
for (auto& I : Indeces) { if (Grid.IsValidIndex(I) && OfHex->Distance(Grid[I]) == 1) { Neighbors.Add(Grid[I]); } }
|
||||||
return Neighbors;
|
return Neighbors;
|
||||||
}
|
}
|
||||||
|
|
||||||
TArray<AHexTile*> AAdventureMap::FindPathAStar(AHexTile* Start, AHexTile* Goal)
|
TArray<AHexTile*> AAdventureMap::AStar(AHexTile* Start, AHexTile* Goal)
|
||||||
{
|
{
|
||||||
TArray<AHexTile*> PQ; // Makeshift Priority Queue. (High value = Low priority)
|
TArray<AHexTile*> ToExamine;
|
||||||
PQ.Add(Start);
|
TArray<AHexTile*> Processed;
|
||||||
|
ToExamine.Add(Start);
|
||||||
|
|
||||||
// The goal of this loop is to edit the Hex->CameFrom pointers, So as to chain Hexes from Goal to Start
|
while (!ToExamine.IsEmpty()) {
|
||||||
while (!PQ.IsEmpty())
|
AHexTile* Candidate = ToExamine[0];
|
||||||
{
|
|
||||||
AHexTile* Current = PQ[0];
|
|
||||||
PQ.RemoveAt(0);
|
|
||||||
// UE_LOG(LogTemp, Warning, TEXT("Popping top priority Hex %d:%d"), Current->Q, Current->R); // debug
|
|
||||||
|
|
||||||
if (Current == Goal) // early exit
|
// estimate closest known Hex to Goal
|
||||||
{
|
for (auto& t : ToExamine) {
|
||||||
// UE_LOG(LogTemp, Warning, TEXT("Goal found!!!")); // debug
|
int32 FCost = t->GCost + t->HCost;
|
||||||
break;
|
if (t->FCost < Candidate->FCost || t->FCost == Candidate->FCost && t->HCost < Candidate->HCost) { Candidate = t; }
|
||||||
}
|
}
|
||||||
|
|
||||||
// UE_LOG(LogTemp, Warning, TEXT("Expanding the frontier...")); // debug
|
Processed.Add(Candidate);
|
||||||
for (AHexTile* Next : Neighbors(Current))
|
ToExamine.Remove(Candidate);
|
||||||
{
|
|
||||||
int32 NewCost = Current->CostSoFar + Next->MoveCost;
|
|
||||||
if (NewCost < Next->CostSoFar || !PQ.Contains(Next))
|
|
||||||
{
|
|
||||||
Next->CostSoFar = NewCost;
|
|
||||||
Next->CameFrom = Current;
|
|
||||||
PQ.Remove(Next);
|
|
||||||
int32 NewPrio = NewCost + Next->Distance(Goal); // Higher value = Lower priority
|
|
||||||
|
|
||||||
// no crash up to this point
|
// exit
|
||||||
|
if (Candidate == Goal) { break; }
|
||||||
|
|
||||||
// UE_LOG(LogTemp, Warning, TEXT("Hex %d:%d path info updated. Readjusting priorities..."), Next->Q, Next->R); // debug
|
// expand frontier & adjust path data
|
||||||
if (!PQ.IsEmpty())
|
for (AHexTile* Neighbor : Neighbors(Candidate)) {
|
||||||
{
|
if (Processed.Contains(Neighbor)) { continue; }
|
||||||
int32 OldPrio;
|
|
||||||
int32 OldIndex = PQ.Num() - 1; // default to lowest priority
|
|
||||||
|
|
||||||
for (auto& Hex : PQ)
|
bool bInToExamine = ToExamine.Contains(Neighbor);
|
||||||
{
|
int32 NewGCost = Candidate->GCost + Neighbor->MoveCost;
|
||||||
OldPrio = Hex->CostSoFar + Hex->Distance(Goal);
|
|
||||||
if (NewPrio <= OldPrio) // looking for 1. Hex in "PQ" with a lower priority than that of "Next"
|
if (NewGCost < Neighbor->GCost || !bInToExamine) {
|
||||||
{
|
Neighbor->GCost = NewGCost;
|
||||||
PQ.Find(Hex, OldIndex); // redefine OldIndex
|
Neighbor->CameFrom = Candidate; // chain
|
||||||
break;
|
|
||||||
}
|
if (!bInToExamine) {
|
||||||
}
|
Neighbor->HCost = Neighbor->Distance(Goal);
|
||||||
// UE_LOG(LogTemp, Warning, TEXT("Saving at PQ index %d"), OldIndex); // debug
|
ToExamine.Add(Neighbor);
|
||||||
PQ.Insert(Next, OldIndex);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
PQ.Add(Next);
|
|
||||||
// UE_LOG(LogTemp, Warning, TEXT("Adding (PQ was empty)")); // debug
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return LinkPath(Start, Goal);
|
||||||
|
}
|
||||||
|
|
||||||
|
TArray<AHexTile*> AAdventureMap::LinkPath(AHexTile* Start, AHexTile* Goal)
|
||||||
|
{
|
||||||
TArray<AHexTile*> Path;
|
TArray<AHexTile*> Path;
|
||||||
/*
|
AHexTile* iPathNode = Goal;
|
||||||
AHexTile* Hex = Goal;
|
|
||||||
while (Hex != Start)
|
while (iPathNode != Start) {
|
||||||
{
|
Path.Emplace(iPathNode);
|
||||||
Path.Emplace(Hex);
|
|
||||||
Hex = Hex->CameFrom;
|
if (iPathNode->Distance(iPathNode->CameFrom) > 1) {
|
||||||
|
bChainBroken = true;
|
||||||
|
UE_LOG(LogTemp, Warning, TEXT("Chain is broken in LinkPath function..."));
|
||||||
}
|
}
|
||||||
Algo::Reverse(Path);
|
|
||||||
*/
|
iPathNode = iPathNode->CameFrom;
|
||||||
|
}
|
||||||
|
|
||||||
return Path;
|
return Path;
|
||||||
}
|
}
|
@ -43,9 +43,13 @@ public:
|
|||||||
UFUNCTION(BlueprintCallable, Category = "Runtime")
|
UFUNCTION(BlueprintCallable, Category = "Runtime")
|
||||||
TArray<AHexTile*> Neighbors(AHexTile* OfHex);
|
TArray<AHexTile*> Neighbors(AHexTile* OfHex);
|
||||||
UFUNCTION(BlueprintCallable, Category = "Runtime")
|
UFUNCTION(BlueprintCallable, Category = "Runtime")
|
||||||
TArray<AHexTile*> FindPathAStar(AHexTile* Start, AHexTile* Goal);
|
TArray<AHexTile*> AStar(AHexTile* Start, AHexTile* Goal);
|
||||||
//UFUNCTION(BlueprintCallable, Category = "Runtime")
|
UFUNCTION(BlueprintCallable, Category = "Runtime")
|
||||||
// TArray<AHexTile*> FindPathAStarPQ(AHexTile* Start, AHexTile* Goal);
|
TArray<AHexTile*> LinkPath(AHexTile* Start, AHexTile* Goal);
|
||||||
|
|
||||||
|
UPROPERTY(BlueprintReadOnly, Category = "debug")
|
||||||
|
bool bChainBroken;
|
||||||
|
|
||||||
|
|
||||||
// Player spawn section
|
// Player spawn section
|
||||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
|
UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
|
||||||
|
@ -37,7 +37,7 @@ void AAdventurePlayerController::AdvClick()
|
|||||||
if (IsValid(Hit.GetActor()))
|
if (IsValid(Hit.GetActor()))
|
||||||
{
|
{
|
||||||
AHexTile* HitHex = (AHexTile*)Hit.GetActor();
|
AHexTile* HitHex = (AHexTile*)Hit.GetActor();
|
||||||
MapRef->FindPathAStar(CurrentHex, HitHex);
|
// MapRef->FindPathAStar(CurrentHex, HitHex);
|
||||||
// UE_LOG(LogTemp, Warning, TEXT("%d"), HitHex->Index);
|
// UE_LOG(LogTemp, Warning, TEXT("%d"), HitHex->Index);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -47,10 +47,16 @@ public:
|
|||||||
// Pathfinding
|
// Pathfinding
|
||||||
UPROPERTY(BlueprintReadWrite, Category = "Movement")
|
UPROPERTY(BlueprintReadWrite, Category = "Movement")
|
||||||
int32 MoveCost = 1;
|
int32 MoveCost = 1;
|
||||||
UPROPERTY(VisibleInstanceOnly, Category = "Movement")
|
UPROPERTY(BlueprintReadWrite, VisibleInstanceOnly, Category = "Movement")
|
||||||
AHexTile* CameFrom;
|
AHexTile* CameFrom;
|
||||||
UPROPERTY(VisibleInstanceOnly, Category = "Movement")
|
UPROPERTY(VisibleInstanceOnly, Category = "Movement")
|
||||||
int32 CostSoFar = 0;
|
int32 CostSoFar = 0;
|
||||||
|
UPROPERTY()
|
||||||
|
int32 FCost;
|
||||||
|
UPROPERTY()
|
||||||
|
int32 GCost;
|
||||||
|
UPROPERTY()
|
||||||
|
int32 HCost;
|
||||||
|
|
||||||
FORCEINLINE bool operator == (const AHexTile &Other)
|
FORCEINLINE bool operator == (const AHexTile &Other)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user