Hello again everyone,
After help from this forum helped me resolve my previous issue, which involved getting a reference to a character, ‘created’ (from an object pool) in another class, I now have this issue.
I wanted this (AI) character, to use as a ‘dummy’ to check if they would be able to reach their objective, when the Player is placing a wall obstacle (which could block their path to their objective).
I have had four* methods tried for this, these being (all using UNavigationSystemV1
):
- Using
GetRandomReachablePointInRadius()
, with the objective’s location passed in as the Origin, with a radius of 100 UU. This method would sometimes return true or false for an unknown reason - Using
GetRandomPointInNavigableRadius()
with the same parameters again. This method would always return true (matches up to this method’s description) - I then tried that of getting the bespoke AI controller of the ‘dummy’ enemy (
AGruxAIController
), and having theMoveToLocation()
method called on that controller - After this, as a final attempt, the
ProjectPointToNavigation()
method was used, with the location of the objective, passed in as the point to project to the navigation system
All of these methods, allowed the Player to place walls, that would block off a path to the Enemy’s objective (video shows the third method detailed above, using the MoveToLocation()
method to test this:
The code of this method, is just below:
bool AGridPlacementSystem::EnemyHasPathToObjective()
{
bool bEnemyHasPathToObjective = false;
if (EnemySpawnerReference && UtilityFunctionLibraryReference)
{
AActor* TestObstacle = PlaceObstacle(EGridSquareType::Gst_WallSquare, false, true);
if (TestObstacle)
{
FVector TestEnemySpawnLocation = EnemySpawnerReference->GetActorLocation();
TestEnemySpawnLocation.Z = UtilityFunctionLibraryReference->GetCharacterGroundLevel();
ACharacter* TestEnemy = UtilityFunctionLibraryReference->SpawnCharacter(TestEnemySpawnLocation, true);
if (TestEnemy)
{
// First method:
//FNavLocation ResultLocation;
// This method will always return true:
//bEnemyHasPathToObjective = NavigationSystemReference->GetRandomPointInNavigableRadius(GoalTargetReference->GetActorLocation(),
// 10.f, ResultLocation);
// Second method:
// Use of the method below, sometimes returns true or false, I am not quite sure why...
//NavigationSystemReference->GetRandomReachablePointInRadius(GoalTargetReference->GetActorLocation(),
// 10.f, ResultLocation);
// Third method:
//AGruxAIController* GruxControllerReference = Cast<AGruxAIController>(TestEnemy->GetController());
//if (GruxControllerReference)
//{
// const EPathFollowingRequestResult::Type MovementResult = GruxControllerReference->MoveToLocation(
// GruxControllerReference->GetTargetLocationKeyValue());
// switch (MovementResult)
// {
// case EPathFollowingRequestResult::AlreadyAtGoal:
// case EPathFollowingRequestResult::RequestSuccessful:
// bEnemyHasPathToObjective = true;
// break;
// case EPathFollowingRequestResult::Failed:
// bEnemyHasPathToObjective = false;
// break;
// default:
// break;
// }
//}
// Fourth method:
const AActor* GoalTargetReference = UGameplayStatics::GetActorOfClass(GetWorld(), AGoalTarget::StaticClass());
UNavigationSystemV1* NavigationSystemReference = UNavigationSystemV1::GetCurrent(GetWorld());
if (GoalTargetReference && NavigationSystemReference)
{
FNavLocation OutLocation;
bEnemyHasPathToObjective = NavigationSystemReference->ProjectPointToNavigation(GoalTargetReference->GetActorLocation(),
OutLocation);
}
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Orange, FString::Printf(
TEXT("Enemy has path to objective: %hhd"), bEnemyHasPathToObjective));
TestObstacle->Destroy();
UtilityFunctionLibraryReference->ReturnObjectToPool(TestEnemy);
}
}
}
return bEnemyHasPathToObjective;
}
Please let me know of a method to allow the AI to check if it can reach a location. I am not sure if I am overcomplicating things this time…
Please also let me know if you require any further details, to help me resolve this issue, thanks .
EDIT: This is the latest version for path checking I am trying:
Okay, this is the logic I have for my new method of checking to see if a path is valid before placing the final obstacle:
bool AGridPlacementSystem::EnemyHasPathToObjective()
{
bool bEnemyHasPathToObjective = false;
if (!TestObstacleReference)
{
TestObstacleReference = PlaceObstacle(EGridSquareType::Gst_WallSquare, false);
}
if (EnemySpawnerReference && UtilityFunctionLibraryReference)
{
bEnemyHasPathToObjective = CheckGridPathToObjective();
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Orange, FString::Printf(
TEXT("Enemy has path to objective: %hhd"), bEnemyHasPathToObjective));
}
return bEnemyHasPathToObjective;
}
bool AGridPlacementSystem::CheckGridPathToObjective()
{
bool bEnemyHasGridPathToObjective = false;
int CurrentValidPathIndex = 0;
while (CurrentValidPathIndex < BaseGridTileActors.Num())
{
TArray<AActor*> BasicGridTileAttachedActors = TArray<AActor*>();
// If path traversal has got to the last node in the grid, there must be a path to
// the objective:
if (CurrentValidPathIndex == BaseGridTileActors.Num() - 1)
{
bEnemyHasGridPathToObjective = true;
break;
}
// There is a path on the column to the left:
if (BaseGridTileActors.IsValidIndex(CurrentValidPathIndex + 4))
{
BaseGridTileActors[CurrentValidPathIndex + 4]->GetAttachedActors(
BasicGridTileAttachedActors);
if (BasicGridTileAttachedActors.Num() <= 1)
{
CurrentValidPathIndex += 4;
}
}
// There is not a path on the column to the left, so try the node below the current one:
else if (BaseGridTileActors.IsValidIndex(CurrentValidPathIndex + 1))
{
BaseGridTileActors[CurrentValidPathIndex + 1]->GetAttachedActors(
BasicGridTileAttachedActors);
if (BasicGridTileAttachedActors.Num() <= 1)
{
++CurrentValidPathIndex;
}
}
// There is not a path on the column to the left, or of the node below the
// current one, so move back one node if possible:
else if (BaseGridTileActors.IsValidIndex(CurrentValidPathIndex - 1))
{
BaseGridTileActors[CurrentValidPathIndex - 1]->GetAttachedActors(
BasicGridTileAttachedActors);
if (BasicGridTileAttachedActors.Num() <= 1)
{
--CurrentValidPathIndex;
}
}
// There is not a path on the column to the left, or of the node below the
// current one, or of the previous node, so move back a column to the right:
else if (BaseGridTileActors.IsValidIndex(CurrentValidPathIndex - 4))
{
BaseGridTileActors[CurrentValidPathIndex - 4]->GetAttachedActors(
BasicGridTileAttachedActors);
if (BasicGridTileAttachedActors.Num() <= 1)
{
CurrentValidPathIndex -= 4;
}
}
// There is no path to the objective:
else
{
bEnemyHasGridPathToObjective = false;
break;
}
}
return bEnemyHasGridPathToObjective;
}
The grid is as shown here (with virtual tiles, placed starting at the top right hand corner and ending and the bottom left hand corner):
The red circles indicate the spawn points of the enemies.
This new method would still allow the Player to place walls that completely block the enemies’ path to the objective.
Please let me know what the faults in more logic could be/any further information you require to help me resolve this problem.