From 2bf072e3c68c135b73466c3d0c56be0f15e110ac Mon Sep 17 00:00:00 2001 From: Maximilian Fajnberg Date: Fri, 28 Jan 2022 21:58:04 +0100 Subject: [PATCH] implemented diagonal movement --- AdventureCameraPawn.cpp | 8 +-- AdventureMap.cpp | 129 +++++++++++++++++----------------- AdventureMap.h | 86 +++++++++++++---------- AdventurePlayerController.cpp | 2 +- HexTile.h | 8 +-- 5 files changed, 124 insertions(+), 109 deletions(-) diff --git a/AdventureCameraPawn.cpp b/AdventureCameraPawn.cpp index 501edc2..039ec6e 100644 --- a/AdventureCameraPawn.cpp +++ b/AdventureCameraPawn.cpp @@ -19,8 +19,8 @@ AAdventureCameraPawn::AAdventureCameraPawn() PrimaryActorTick.bCanEverTick = true; ESASize = .01; - BaseScrollSpeed = 16; - ScrollAccelleration = BaseScrollSpeed * 2.4; + BaseScrollSpeed = 20; + ScrollAccelleration = BaseScrollSpeed * 3; ESAMaxBoundSlope = 1 / (1 - (1-ESASize)); ESAMaxBoundIntercept = 1 - ESAMaxBoundSlope; @@ -73,13 +73,13 @@ void AAdventureCameraPawn::SetupPlayerInputComponent(UInputComponent* PlayerInpu void AAdventureCameraPawn::ScrollSide(float AxisValue) { - float DeltaLoc = AxisValue * BaseScrollSpeed * 3; + float DeltaLoc = AxisValue * BaseScrollSpeed * 1.7; AddActorLocalOffset(FVector(DeltaLoc, 0, 0)); } void AAdventureCameraPawn::ScrollVert(float AxisValue) { - float DeltaLoc = AxisValue * BaseScrollSpeed * -3; + float DeltaLoc = AxisValue * BaseScrollSpeed * -1.7; AddActorLocalOffset(FVector(0, DeltaLoc, 0)); } diff --git a/AdventureMap.cpp b/AdventureMap.cpp index 640b511..23f28df 100644 --- a/AdventureMap.cpp +++ b/AdventureMap.cpp @@ -10,18 +10,10 @@ // Sets default values AAdventureMap::AAdventureMap() { - NBVectors.Add(NNE); - NBVectors.Add(E); - NBVectors.Add(SSE); - NBVectors.Add(SSW); - NBVectors.Add(W); - NBVectors.Add(NNW); - NBVectorsDiag.Add(N); - NBVectorsDiag.Add(ENE); - NBVectorsDiag.Add(ESE); - NBVectorsDiag.Add(S); - NBVectorsDiag.Add(WSW); - NBVectorsDiag.Add(WNW); + FHexVector NBs[] = { NNE, E, SSE, SSW, W, NNW }; + NeighborUnitVectors.Append(NBs, UE_ARRAY_COUNT(NBs)); + FHexVector DNBs[] = { N, ENE, ESE, S, WSW, WNW }; + DiagonalUnitVectors.Append(DNBs, UE_ARRAY_COUNT(DNBs)); } // Called when the game starts or when spawned @@ -45,25 +37,20 @@ void AAdventureMap::MakeGrid() for (int r = 1; r <= GridSize; r++) { float XOffset = 0.f; - if (r % 2 != 0) { - if (r > 1) { - QOffset--; - } - } + if (r % 2 != 0) { if (r > 1) { QOffset--; } } else { XOffset = HexWidth / 2; } - for (int q = 1; q <= GridSize; q++) { + for (int q = 1; q <= GridSize; q++) { NextHexAt.X = XOffset + (HexWidth * q); NextHexAt.Y = TileSize * 1.5f * r; NextHexAt.Z = 0.f; - FTransform SpawnTransform = FTransform(NextHexAt); + AHexTile* Tile = World->SpawnActor(BaseTileClass, SpawnTransform); + Grid.Add(Tile); Tile->Q = q - 1 + QOffset; Tile->R = r - 1; - - Grid.Add(Tile); } } @@ -99,52 +86,57 @@ AHexTile* AAdventureMap::RandomHex() * This function instead returns TMap */ -TArray AAdventureMap::Neighbors(AHexTile* OfHex) +TArray AAdventureMap::Neighbors(AHexTile* OfHex, bool bFreeOnly = false) { TArray Results; - TArray Indeces; - for (auto& Vec : NBVectors) { - Indeces.Add(GridIndex(OfHex->Q + Vec.Key, OfHex->R + Vec.Value)); + for (auto& V : NeighborUnitVectors) { + int32 I = GridIndex(OfHex->Q + V.Q, OfHex->R + V.R); + if (Grid.IsValidIndex(I)) { + AHexTile* R = Grid[I]; + Results.Add(Grid[I]); + if (bFreeOnly && !R->bFree) { Results.Remove(R); } + } } - for (auto& Ind : Indeces) { - if (Grid.IsValidIndex(Ind)) { - if (OfHex->Distance(Grid[Ind]) == 1) { - Results.Add(Grid[Ind]); - } - } + for (auto& R : Results) { + if (bFreeOnly && !R->bFree) { Results.Remove(R); } } return Results; } -TArray AAdventureMap::Diagonals(AHexTile* OfHex) +TArray AAdventureMap::FreeDiags(AHexTile* OfHex) { TArray Results; - TArray Indeces; - for (auto& Vec : NBVectorsDiag) { - Indeces.Add(GridIndex(OfHex->Q + Vec.Key, OfHex->R + Vec.Value)); - } - for (auto& Ind : Indeces) { - if (Grid.IsValidIndex(Ind)) { - if (OfHex->Distance(Grid[Ind]) == 2) { - Results.Add(Grid[Ind]); + for (auto& V : DiagonalUnitVectors) { + int32 I = GridIndex(OfHex->Q + V.Q, OfHex->R + V.R); + if (!Grid.IsValidIndex(I)) { continue; } + // if (!bFreeOnly) { if (Grid[I]->Distance(OfHex) == 1) { Results.Add(Grid[I]); } } + else { + bool bReachable = true; + for (auto& PotentialBlock : Neighbors(OfHex)) { + if (PotentialBlock->Distance(Grid[I]) != 1) { continue; } + if (!PotentialBlock->bFree) { + bReachable = false; + break; + } + } + if (bReachable) { + Results.Add(Grid[I]); } } } return Results; } -TArray AAdventureMap::BreadthFirstSearch(AHexTile* Start, int32 Radius) +TSet AAdventureMap::BreadthFirstSearch(AHexTile* Start, int32 Radius) { - TArray Results; + TSet Results; TArray ToExamine; TSet Processed; - Results.Add(Start); ToExamine.Add(Start); while (!ToExamine.IsEmpty()) { AHexTile* Candidate = ToExamine[0]; - Processed.Add(Candidate); ToExamine.Remove(Candidate); @@ -160,58 +152,67 @@ TArray AAdventureMap::BreadthFirstSearch(AHexTile* Start, int32 Radiu return Results; } -TArray AAdventureMap::AStar(AHexTile* Start, AHexTile* Goal) +TArray AAdventureMap::FindPathAStar(AHexTile* Start, AHexTile* Goal, bool bDiags) { TArray ToExamine; TSet Processed; ToExamine.Add(Start); - while (!ToExamine.IsEmpty()) { + while (!ToExamine.IsEmpty()) { AHexTile* Candidate = ToExamine[0]; ToExamine.Remove(Candidate); - - // estimate closest known Hex to Goal + // try for Hex with lower estimatet (F)cost for (auto& t : ToExamine) { t->FCost = t->GCost + t->HCost; - if (t->FCost < Candidate->FCost || t->FCost == Candidate->FCost && t->HCost < Candidate->HCost) { Candidate = t; } + if (t->FCost < Candidate->FCost || t->FCost == Candidate->FCost && t->HCost < Candidate->HCost) { Candidate = t; } } Processed.Add(Candidate); - // exit if (Candidate == Goal) { break; } // expand frontier & adjust path data - for (AHexTile* Neighbor : Neighbors(Candidate)) { + for (AHexTile* Neighbor : Neighbors(Candidate, true)) { if (Neighbor->Distance(Candidate) > 1) { continue; } - if (!(Neighbor->bFree)) { continue; } if (Processed.Contains(Neighbor)) { continue; } bool bInToExamine = ToExamine.Contains(Neighbor); - int32 NewGCost = Candidate->GCost + Neighbor->MoveCost; + float NewGCost = Candidate->GCost + Neighbor->MoveCost * 10.f; if (NewGCost < Neighbor->GCost || !bInToExamine) { Neighbor->GCost = NewGCost; Neighbor->CameFrom = Candidate; // chain - if (!bInToExamine) { - Neighbor->HCost = Neighbor->Distance(Goal); + if (!bInToExamine) { + Neighbor->HCost = Neighbor->Distance(Goal) * 10.f; ToExamine.Add(Neighbor); } } - } + } + if (bDiags) { // right now the heuristic for HCost (Distance func) does NOT take diagonals into account + for (AHexTile* Diag : FreeDiags(Candidate)) { + if (Diag->Distance(Candidate) > 2) { continue; } + if (!Diag->bFree) { continue; } + if (Processed.Contains(Diag)) { continue; } + + bool bInToExamine = ToExamine.Contains(Diag); + float NewGCost = Candidate->GCost + Diag->MoveCost * 10.f; + + if (NewGCost < Diag->GCost || !bInToExamine) { + Diag->GCost = NewGCost; + Diag->CameFrom = Candidate; // chain + + if (!bInToExamine) { + Diag->HCost = Diag->Distance(Goal) * 10.f; + ToExamine.Add(Diag); + } + } + } + } } - return LinkPath(Start, Goal); -} - -TArray AAdventureMap::LinkPath(AHexTile* Start, AHexTile* Goal) -{ TArray Path; - if (!IsValid(Goal->CameFrom)) { return Path; } - AHexTile* iPathNode = Goal; - while (iPathNode != Start) { Path.Emplace(iPathNode); iPathNode = iPathNode->CameFrom; diff --git a/AdventureMap.h b/AdventureMap.h index 2d141c4..2672d97 100644 --- a/AdventureMap.h +++ b/AdventureMap.h @@ -5,6 +5,7 @@ #include "CoreMinimal.h" #include "GameFramework/Actor.h" #include "Containers/Map.h" +#include "HexVector.h" #include "AdventureMap.generated.h" class AHexTile; @@ -20,16 +21,14 @@ public: // Sets default values for this actor's properties AAdventureMap(); + UPROPERTY() + UWorld* World; UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Config") TSubclassOf BaseTileClass; - UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Config") - TSubclassOf BasePartyCharacterClass; UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Config") int32 GridSize = 100; // squared is the number of Tiles UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Config") int32 TileSize = 100; - UPROPERTY() - UWorld* World; UFUNCTION(BlueprintCallable, Category = "Generation") void MakeGrid(); @@ -38,43 +37,58 @@ public: UPROPERTY(BlueprintReadOnly, Category = "Generation") TArray Grid; + UFUNCTION(BlueprintCallable, Category = "Utility") + int32 GridIndex(int32 q, int32 r); + UFUNCTION(BlueprintCallable, Category = "Utility") + AHexTile* RandomHex(); + + // Cardinal direction vectors + UPROPERTY(BlueprintReadOnly, VisibleAnywhere) //diag + FHexVector N = FHexVector(1, -2); + UPROPERTY(BlueprintReadOnly, VisibleAnywhere) + FHexVector NNE = FHexVector(1, -1); + UPROPERTY(BlueprintReadOnly, VisibleAnywhere) //diag + FHexVector ENE = FHexVector(2, -1); + UPROPERTY(BlueprintReadOnly, VisibleAnywhere) + FHexVector E = FHexVector(1, 0); + UPROPERTY(BlueprintReadOnly, VisibleAnywhere) //diag + FHexVector ESE = FHexVector(1, 1); + UPROPERTY(BlueprintReadOnly, VisibleAnywhere) + FHexVector SSE = FHexVector(0, 1); + UPROPERTY(BlueprintReadOnly, VisibleAnywhere) //diag + FHexVector S = FHexVector(-1, 2); + UPROPERTY(BlueprintReadOnly, VisibleAnywhere) + FHexVector SSW = FHexVector(-1, 1); + UPROPERTY(BlueprintReadOnly, VisibleAnywhere) //diag + FHexVector WSW = FHexVector(-2, 1); + UPROPERTY(BlueprintReadOnly, VisibleAnywhere) + FHexVector W = FHexVector(-1, 0); + UPROPERTY(BlueprintReadOnly, VisibleAnywhere) //diag + FHexVector WNW = FHexVector(-1, -1); + UPROPERTY(BlueprintReadOnly, VisibleAnywhere) + FHexVector NNW = FHexVector(0, -1); + + UPROPERTY(BlueprintReadOnly, VisibleAnywhere) + TArray NeighborUnitVectors; + UPROPERTY(BlueprintReadOnly, VisibleAnywhere) + TArray DiagonalUnitVectors; + + UFUNCTION(BlueprintCallable, Category = "Runtime") + TArray Neighbors(AHexTile* OfHex, bool bFreeOnly); + UFUNCTION(BlueprintCallable, Category = "Runtime") + TArray FreeDiags(AHexTile* OfHex); + UFUNCTION(BlueprintCallable, Category = "Runtime") + TSet BreadthFirstSearch(AHexTile* Start, int32 Radius); + UFUNCTION(BlueprintCallable, Category = "Runtime") + TArray FindPathAStar(AHexTile* Start, AHexTile* Goal, bool bDiags); + UPROPERTY(BlueprintReadWrite, EditAnywhere) TMap MapObjects; UPROPERTY(BlueprintReadOnly) TMap MapObjectsByID; - int32 IncID = -1; + UPROPERTY(BlueprintReadWrite, VisibleAnywhere) + int32 IncID = -1; - UFUNCTION(BlueprintCallable, Category = "Runtime") - int32 GridIndex(int32 q, int32 r); - UFUNCTION(BlueprintCallable, Category = "Runtime") - AHexTile* RandomHex(); - UFUNCTION(BlueprintCallable, Category = "Runtime") - TArray Neighbors(AHexTile* OfHex); - UFUNCTION(BlueprintCallable, Category = "Runtime") - TArray Diagonals(AHexTile* OfHex); - UFUNCTION(BlueprintCallable, Category = "Runtime") - TArray BreadthFirstSearch(AHexTile* Start, int32 Radius); - UFUNCTION(BlueprintCallable, Category = "Runtime") - TArray AStar(AHexTile* Start, AHexTile* Goal); - UFUNCTION(BlueprintCallable, Category = "Runtime") - TArray LinkPath(AHexTile* Start, AHexTile* Goal); - - // Cardinal direction vectors - TPair N = TPair(1, -2); //diag - TPair NNE = TPair(1, -1); - TPair ENE = TPair(2, -1); //diag - TPair E = TPair(1, 0); - TPair ESE = TPair(1, 1); //diag - TPair SSE = TPair(0, 1); - TPair S = TPair(-1, 2); //diag - TPair SSW = TPair(-1, 1); - TPair WSW = TPair(-2, 1); //diag - TPair W = TPair(-1, 0); - TPair WNW = TPair(-1, -1); //diag - TPair NNW = TPair(0, -1); - - TArray> NBVectors; - TArray> NBVectorsDiag; protected: // Called when the game starts or when spawned diff --git a/AdventurePlayerController.cpp b/AdventurePlayerController.cpp index 33291b9..ea74ded 100644 --- a/AdventurePlayerController.cpp +++ b/AdventurePlayerController.cpp @@ -48,7 +48,7 @@ void AAdventurePlayerController::LeftClick() TArray AAdventurePlayerController::Vision() { TArray Results; - TArray Visible = MapRef->BreadthFirstSearch(CurrentHex, 7); + TSet Visible = MapRef->BreadthFirstSearch(CurrentHex, 4); for (auto& Hex : Visible) { if (ExploredHexes.Contains(Hex)) { continue; } Results.Add(Hex); diff --git a/HexTile.h b/HexTile.h index 1465061..5bbce5f 100644 --- a/HexTile.h +++ b/HexTile.h @@ -46,15 +46,15 @@ public: // Pathfinding UPROPERTY(BlueprintReadWrite, Category = "Movement") - int32 MoveCost = 1; + float MoveCost = 10; UPROPERTY(BlueprintReadWrite, VisibleInstanceOnly, Category = "Movement") AHexTile* CameFrom; UPROPERTY() - int32 FCost; + float FCost; UPROPERTY() - int32 GCost; + float GCost; UPROPERTY() - int32 HCost; + float HCost; // MapObject Placement UPROPERTY(BlueprintReadWrite, VisibleAnywhere)