Fixed freezes on controller calling MarkPath() to invalid locations
This commit is contained in:
parent
28a226e35d
commit
26d3289962
146
AdventureMap.cpp
146
AdventureMap.cpp
@ -7,6 +7,7 @@
|
||||
#include "AdventurePlayerController.h"
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
#include "Algo/Reverse.h"
|
||||
#include "Util/IndexPriorityQueue.h"
|
||||
|
||||
// Sets default values
|
||||
AAdventureMap::AAdventureMap()
|
||||
@ -85,12 +86,12 @@ AHexTile* AAdventureMap::RandomHex()
|
||||
TArray<AHexTile*> AAdventureMap::Neighbors(AHexTile* OfHex, bool bFreeOnly = false)
|
||||
{
|
||||
TArray<AHexTile*> Results;
|
||||
for (auto& V : UnitVectors) {
|
||||
int32 I = GridIndex(OfHex->Q + V.Q, OfHex->R + V.R);
|
||||
if (Grid.IsValidIndex(I)) {
|
||||
AHexTile* H = Grid[I];
|
||||
if (bFreeOnly && !H->bFree) { continue; }
|
||||
if (H->Distance(OfHex) == 1) { Results.Add(H); }
|
||||
for (FHexVector NeighborVector : UnitVectors) {
|
||||
int32 Index = GridIndex(OfHex->Q + NeighborVector.Q, OfHex->R + NeighborVector.R);
|
||||
if (Grid.IsValidIndex(Index)) {
|
||||
AHexTile* Hex = Grid[Index];
|
||||
if (bFreeOnly && !Hex->bFree) { continue; }
|
||||
if (Hex->Distance(OfHex) == 1) { Results.Add(Hex); }
|
||||
}
|
||||
}
|
||||
return Results;
|
||||
@ -120,66 +121,66 @@ TArray<AHexTile*> AAdventureMap::FreeDiagonals(AHexTile* OfHex)
|
||||
TSet<AHexTile*> AAdventureMap::BreadthFirstSearch(AHexTile* Start, int32 Radius)
|
||||
{
|
||||
TSet<AHexTile*> Results;
|
||||
TArray<AHexTile*> ToExamine;
|
||||
TArray<AHexTile*> Frontier;
|
||||
TSet<AHexTile*> Processed;
|
||||
Results.Add(Start);
|
||||
ToExamine.Add(Start);
|
||||
Frontier.Add(Start);
|
||||
|
||||
while (!ToExamine.IsEmpty()) {
|
||||
AHexTile* Candidate = ToExamine[0];
|
||||
Processed.Add(Candidate);
|
||||
ToExamine.Remove(Candidate);
|
||||
while (!Frontier.IsEmpty()) {
|
||||
AHexTile* Current = Frontier[0];
|
||||
Processed.Add(Current);
|
||||
Frontier.Remove(Current);
|
||||
|
||||
for (AHexTile* Neighbor : Neighbors(Candidate)) {
|
||||
if (Neighbor->Distance(Candidate) > 1) { continue; }
|
||||
for (AHexTile* Neighbor : Neighbors(Current)) {
|
||||
if (Neighbor->Distance(Current) > 1) { continue; }
|
||||
if (Processed.Contains(Neighbor)) { continue; }
|
||||
if (Neighbor->Distance(Start) > Radius) { continue; }
|
||||
|
||||
ToExamine.Add(Neighbor);
|
||||
Frontier.Add(Neighbor);
|
||||
Results.Add(Neighbor);
|
||||
}
|
||||
}
|
||||
return Results;
|
||||
}
|
||||
|
||||
/* // Faulty implementation which uses an actual PriorityQueue
|
||||
TArray<AHexTile*> AAdventureMap::FindPathAStar(AHexTile* Start, AHexTile* Goal, bool bDiags)
|
||||
{
|
||||
TArray<AHexTile*> ToExamine;
|
||||
|
||||
TSet<AHexTile*> Processed;
|
||||
ToExamine.Add(Start);
|
||||
TSet<AHexTile*> ToSearch;
|
||||
TPriorityQueue<AHexTile*> Frontier;
|
||||
|
||||
while (!ToExamine.IsEmpty()) {
|
||||
AHexTile* Candidate = ToExamine[0];
|
||||
ToExamine.Remove(Candidate);
|
||||
// find Hex with lower estimatet F-cost
|
||||
for (AHexTile* t : ToExamine) {
|
||||
t->FCost = t->GCost + t->HCost;
|
||||
if (t->FCost < Candidate->FCost || t->FCost == Candidate->FCost && t->HCost < Candidate->HCost) { Candidate = t; }
|
||||
}
|
||||
Start->GCost = 0;
|
||||
Frontier.Push(Start, .0f);
|
||||
ToSearch.Add(Start);
|
||||
|
||||
Processed.Add(Candidate);
|
||||
// exit
|
||||
if (Candidate == Goal) { break; }
|
||||
while (!Frontier.IsEmpty()) {
|
||||
AHexTile* Current = Frontier.Pop();
|
||||
ToSearch.Remove(Current);
|
||||
|
||||
// expand frontier & adjust path data
|
||||
for (AHexTile* Neighbor : Neighbors(Candidate, true)) {
|
||||
if (!Neighbor->bFree) { continue; }
|
||||
if (Current == Goal) { break; }
|
||||
Processed.Add(Current);
|
||||
|
||||
for (AHexTile* Neighbor : Neighbors(Current, true)) {
|
||||
if (Processed.Contains(Neighbor)) { continue; }
|
||||
bool bInToSearch = ToSearch.Contains(Start);
|
||||
|
||||
bool bInToExamine = ToExamine.Contains(Neighbor);
|
||||
float NewGCost = Candidate->GCost + Neighbor->MoveCost;
|
||||
int32 NewGCost = Current->GCost + Neighbor->MoveCost;
|
||||
|
||||
if (NewGCost < Neighbor->GCost || !bInToExamine) {
|
||||
if (!bInToSearch || NewGCost < Neighbor->GCost) {
|
||||
Neighbor->GCost = NewGCost;
|
||||
Neighbor->CameFrom = Candidate; // chain
|
||||
Neighbor->bDiagMove = false;
|
||||
Neighbor->CameFrom = Current;
|
||||
|
||||
if (!bInToExamine) {
|
||||
if (!bInToSearch) {
|
||||
Neighbor->HCost = Neighbor->Distance(Goal);
|
||||
ToExamine.Add(Neighbor);
|
||||
} }
|
||||
Frontier.Push(Neighbor, NewGCost + Neighbor->HCost);
|
||||
ToSearch.Add(Neighbor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TArray<AHexTile*> Path;
|
||||
if (!IsValid(Goal->CameFrom)) { return Path; }
|
||||
AHexTile* iPathNode = Goal;
|
||||
@ -188,10 +189,62 @@ TArray<AHexTile*> AAdventureMap::FindPathAStar(AHexTile* Start, AHexTile* Goal,
|
||||
iPathNode = iPathNode->CameFrom;
|
||||
}
|
||||
Algo::Reverse(Path);
|
||||
|
||||
if (bDiags) { // DO NOT USE
|
||||
Path = ShortcutAStar(Path);
|
||||
return Path;
|
||||
}
|
||||
*/
|
||||
|
||||
TArray<AHexTile*> AAdventureMap::FindPathAStar(AHexTile * Start, AHexTile * Goal, bool bDiags)
|
||||
{
|
||||
TArray<AHexTile*> Frontier;
|
||||
TSet<AHexTile*> Processed;
|
||||
Frontier.Add(Start);
|
||||
while (!Frontier.IsEmpty()) {
|
||||
// Pop
|
||||
AHexTile* Candidate = Frontier[0];
|
||||
|
||||
// Exit
|
||||
if (Candidate == Goal) { break; }
|
||||
|
||||
// Find contender with an even lower F-cost
|
||||
for (AHexTile* Other : Frontier) {
|
||||
if (Other->FCost < Candidate->FCost
|
||||
|| Other->FCost == Candidate->FCost && Other->HCost < Candidate->HCost)
|
||||
{ Candidate = Other; }
|
||||
}
|
||||
|
||||
Frontier.Remove(Candidate);
|
||||
Processed.Add(Candidate);
|
||||
// Expand frontier, make connections when appropriate
|
||||
for (AHexTile* Neighbor : Neighbors(Candidate, true)) {
|
||||
if (Processed.Contains(Neighbor)) { continue; }
|
||||
|
||||
bool bInFrontier = Frontier.Contains(Neighbor);
|
||||
int32 NewGCost = Candidate->GCost + Neighbor->MoveCost;
|
||||
|
||||
if (NewGCost < Neighbor->GCost || !bInFrontier) {
|
||||
Neighbor->GCost = NewGCost;
|
||||
Neighbor->HCost = Neighbor->Distance(Goal);
|
||||
Neighbor->FCost = Neighbor->GCost + Neighbor->HCost;
|
||||
Neighbor->CameFrom = Candidate; // chain
|
||||
if (!bInFrontier) {
|
||||
Frontier.Add(Neighbor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build and return path
|
||||
TArray<AHexTile*> Path;
|
||||
AHexTile* iPathNode = Goal;
|
||||
while (iPathNode != Start) {
|
||||
if (!IsValid(iPathNode->CameFrom) || !iPathNode->bFree) {
|
||||
Path.Empty();
|
||||
return Path;
|
||||
}
|
||||
Path.Emplace(iPathNode);
|
||||
iPathNode = iPathNode->CameFrom;
|
||||
}
|
||||
Algo::Reverse(Path);
|
||||
return Path;
|
||||
}
|
||||
|
||||
@ -246,11 +299,11 @@ TArray<AHexTile*> AAdventureMap::ShortcutAStar(TArray<AHexTile*> Path)
|
||||
if (DiagIsReachable(CurrentHex, UnitDiag)) {
|
||||
int32 CanIndex = GridIndex(CurrentHex->Q + UnitDiag.Q, CurrentHex->R + UnitDiag.R);
|
||||
if (Grid.IsValidIndex(CanIndex)) {
|
||||
AHexTile* Candidate = Grid[CanIndex];
|
||||
if (Candidate->bFree) {
|
||||
Shortcut.Add(Candidate);
|
||||
AHexTile* Current = Grid[CanIndex];
|
||||
if (Current->bFree) {
|
||||
Shortcut.Add(Current);
|
||||
bDiagAdded = true;
|
||||
CurrentHex = Candidate;
|
||||
CurrentHex = Current;
|
||||
NumDiags--;
|
||||
continue;
|
||||
} } }
|
||||
@ -299,3 +352,4 @@ bool AAdventureMap::DiagIsReachable(AHexTile* InStart, FHexVector InDiagUnitVec)
|
||||
AHexTile* HexB = Grid[IndexB];
|
||||
return (HexA->bFree && HexB->bFree);
|
||||
}
|
||||
|
||||
|
@ -106,3 +106,63 @@ protected:
|
||||
// Called when the game starts or when spawned
|
||||
virtual void BeginPlay() override;
|
||||
};
|
||||
|
||||
|
||||
// only used for an experimental implementation of A*
|
||||
template <typename InElementType>
|
||||
struct TPriorityQueueNode {
|
||||
InElementType Element;
|
||||
float Priority;
|
||||
|
||||
TPriorityQueueNode()
|
||||
{
|
||||
}
|
||||
|
||||
TPriorityQueueNode(InElementType InElement, float InPriority)
|
||||
{
|
||||
Element = InElement;
|
||||
Priority = InPriority;
|
||||
}
|
||||
|
||||
bool operator<(const TPriorityQueueNode<InElementType> Other) const
|
||||
{
|
||||
return Priority < Other.Priority;
|
||||
}
|
||||
};
|
||||
template <typename InElementType>
|
||||
class TPriorityQueue {
|
||||
public:
|
||||
TPriorityQueue()
|
||||
{
|
||||
Array.Heapify();
|
||||
}
|
||||
|
||||
public:
|
||||
// Always check if IsEmpty() before Pop-ing!
|
||||
InElementType Pop()
|
||||
{
|
||||
TPriorityQueueNode<InElementType> Node;
|
||||
Array.HeapPop(Node);
|
||||
return Node.Element;
|
||||
}
|
||||
|
||||
TPriorityQueueNode<InElementType> PopNode()
|
||||
{
|
||||
TPriorityQueueNode<InElementType> Node;
|
||||
Array.HeapPop(Node);
|
||||
return Node;
|
||||
}
|
||||
|
||||
void Push(InElementType Element, float Priority)
|
||||
{
|
||||
Array.HeapPush(TPriorityQueueNode<InElementType>(Element, Priority));
|
||||
}
|
||||
|
||||
bool IsEmpty() const
|
||||
{
|
||||
return Array.Num() == 0;
|
||||
}
|
||||
|
||||
public: // make private later on
|
||||
TArray<TPriorityQueueNode<InElementType>> Array;
|
||||
};
|
@ -59,7 +59,6 @@ TArray<AHexTile*> AAdventurePlayerController::Vision(int32 Radius)
|
||||
{
|
||||
TArray<AHexTile*> Results;
|
||||
TSet<AHexTile*> Visible;
|
||||
if (CurrentHex->bDiagMove) { Radius = FMath::FloorToInt(float(Radius) * (2.f / 3.f)); }
|
||||
Visible = MapRef->BreadthFirstSearch(CurrentHex, Radius);
|
||||
for (auto& Hex : Visible) {
|
||||
if (ExploredHexes.Contains(Hex)) { continue; }
|
||||
@ -71,6 +70,10 @@ TArray<AHexTile*> AAdventurePlayerController::Vision(int32 Radius)
|
||||
|
||||
void AAdventurePlayerController::MarkPath(TArray<AHexTile*> Path)
|
||||
{
|
||||
if (Path.IsEmpty()) { return; }
|
||||
else if (!IsValid(Path[0])) { return; }
|
||||
else if (CurrentHex->Distance(Path[0])>1) { return; }
|
||||
|
||||
FHexVector DirA = FHexVector(Path[0]->Q - CurrentHex->Q, Path[0]->R - CurrentHex->R);
|
||||
FHexVector DirB;
|
||||
for (int32 i = 0; i < Path.Num() - 1; i++)
|
||||
|
18
HexTile.h
18
HexTile.h
@ -48,19 +48,15 @@ public:
|
||||
|
||||
// Pathfinding
|
||||
UPROPERTY(BlueprintReadWrite)
|
||||
float MoveCost = 10;
|
||||
int32 MoveCost = 1;
|
||||
UPROPERTY()
|
||||
int32 FCost;
|
||||
UPROPERTY()
|
||||
int32 GCost = 9999;
|
||||
UPROPERTY()
|
||||
int32 HCost;
|
||||
UPROPERTY(BlueprintReadWrite, VisibleInstanceOnly, Category = "Runtime")
|
||||
AHexTile* CameFrom;
|
||||
UPROPERTY(BlueprintReadWrite, VisibleInstanceOnly, Category = "Runtime")
|
||||
AHexTile* LeadsTo;
|
||||
UPROPERTY(BlueprintReadWrite, VisibleInstanceOnly, Category = "Runtime")
|
||||
bool bDiagMove = false;
|
||||
UPROPERTY()
|
||||
float FCost;
|
||||
UPROPERTY()
|
||||
float GCost;
|
||||
UPROPERTY()
|
||||
float HCost = 9999;
|
||||
|
||||
// MapObject Placement
|
||||
UPROPERTY(BlueprintReadWrite, VisibleAnywhere)
|
||||
|
@ -26,60 +26,51 @@ AMovementArrow::AMovementArrow()
|
||||
}
|
||||
|
||||
void AMovementArrow::SetVariant(FHexVector InVector, FHexVector OutVector) {
|
||||
|
||||
int32 InVectorIndex = MapRef->UnitVectors.Find(InVector);
|
||||
int32 OutVectorIndex = MapRef->UnitVectors.Find(OutVector);
|
||||
int32 VariantIndex;
|
||||
|
||||
if (InVectorIndex == 0)
|
||||
{
|
||||
if (InVectorIndex == 0) {
|
||||
if (OutVectorIndex == 0) { VariantIndex = 0; }
|
||||
if (OutVectorIndex == 1) { VariantIndex = 1; }
|
||||
if (OutVectorIndex == 2) { VariantIndex = 2; }
|
||||
if (OutVectorIndex == 4) { VariantIndex = 3; }
|
||||
if (OutVectorIndex == 5) { VariantIndex = 4; }
|
||||
}
|
||||
if (InVectorIndex == 1)
|
||||
{
|
||||
if (InVectorIndex == 1) {
|
||||
if (OutVectorIndex == 0) { VariantIndex = 4; }
|
||||
if (OutVectorIndex == 1) { VariantIndex = 0; }
|
||||
if (OutVectorIndex == 2) { VariantIndex = 1; }
|
||||
if (OutVectorIndex == 3) { VariantIndex = 2; }
|
||||
if (OutVectorIndex == 5) { VariantIndex = 3; }
|
||||
}
|
||||
if (InVectorIndex == 2)
|
||||
{
|
||||
if (InVectorIndex == 2) {
|
||||
if (OutVectorIndex == 0) { VariantIndex = 3; }
|
||||
if (OutVectorIndex == 1) { VariantIndex = 4; }
|
||||
if (OutVectorIndex == 2) { VariantIndex = 0; }
|
||||
if (OutVectorIndex == 3) { VariantIndex = 1; }
|
||||
if (OutVectorIndex == 4) { VariantIndex = 2; }
|
||||
}
|
||||
if (InVectorIndex == 3)
|
||||
{
|
||||
if (InVectorIndex == 3) {
|
||||
if (OutVectorIndex == 1) { VariantIndex = 3; }
|
||||
if (OutVectorIndex == 2) { VariantIndex = 4; }
|
||||
if (OutVectorIndex == 3) { VariantIndex = 0; }
|
||||
if (OutVectorIndex == 4) { VariantIndex = 1; }
|
||||
if (OutVectorIndex == 5) { VariantIndex = 2; }
|
||||
}
|
||||
if (InVectorIndex == 4)
|
||||
{
|
||||
if (InVectorIndex == 4) {
|
||||
if (OutVectorIndex == 0) { VariantIndex = 2; }
|
||||
if (OutVectorIndex == 2) { VariantIndex = 3; }
|
||||
if (OutVectorIndex == 3) { VariantIndex = 4; }
|
||||
if (OutVectorIndex == 4) { VariantIndex = 0; }
|
||||
if (OutVectorIndex == 5) { VariantIndex = 1; }
|
||||
}
|
||||
if (InVectorIndex == 5)
|
||||
{
|
||||
if (InVectorIndex == 5) {
|
||||
if (OutVectorIndex == 0) { VariantIndex = 1; }
|
||||
if (OutVectorIndex == 1) { VariantIndex = 2; }
|
||||
if (OutVectorIndex == 3) { VariantIndex = 3; }
|
||||
if (OutVectorIndex == 4) { VariantIndex = 4; }
|
||||
if (OutVectorIndex == 5) { VariantIndex = 0; }
|
||||
}
|
||||
|
||||
SceneComponent->SetRelativeRotation(FRotator(0, InVectorIndex * 60.f, 0));
|
||||
MeshVariants[VariantIndex]->ToggleVisibility();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user