What Is the Proper Method to Check if AI Can Reach a Location?

Hmm, I will try that and get back here. Thanks :slight_smile:.

So , do you think you should use just the pathfinding system for this kind of stuff, I’m just asking to know if it’s worth getting into it to only depend on this without anything else.

1 Like

This is a really long thread already, and I didn’t have time to really read through it, but I did notice that the original question wasn’t really hit upon. I’m not sure if the existing path system is relevant, but I figured I would point out the function for finding a path without navigating to it, in case it is helpful to @FloKron

It does seem like you’re doing some custom path finding work, so I’m not sure if that’s relevant. There are plenty of things inside the Nav System for handling dynamic pathing, though, and I’d probably lean towards making the existing system work for whatever, rather than building my own, unless building my own was much easier or had significant advantages to using what’s built in.

1 Like

No I’,m not doing pathfinding work, I left the AI thing for developing landscapes with RMC and omited it, I got to make an Npc without it and I wanted to get into pathfinding but as a mix where there is a mix system where the AI breaks and go’s into another mode. That is why I was asking, if you want to take it further when you develop it and you get good enough to develop a neural network maybe you want to mix with it, I got access to GPT3 module, they gave me access to it since I was among the first to ask for access on the mailing list.

Where to start with AI mmm, it’s a long road I want to get into.

Okay, I was unable to find FindPathToLocation(), but I found FindPathToLocationSynchronously().

I am not sure on how to check if the path is valid mind. This is my use of that method:

if (EnemySpawnerReference && UtilityFunctionLibraryReference)
	{
		const AActor* EnemySpawner = UGameplayStatics::GetActorOfClass(GetWorld(), AEnemySpawner::StaticClass());
		if (EnemySpawner)
		{
			FVector EnemySpawnLocation = EnemySpawner->GetActorLocation();
			EnemySpawnLocation.Z = UtilityFunctionLibraryReference->GetCharacterGroundLevel();
			ACharacter* TestEnemy = UtilityFunctionLibraryReference->SpawnCharacter(EnemySpawnLocation,false);
			const AActor* EnemyObjective = UGameplayStatics::GetActorOfClass(GetWorld(), AGoalTarget::StaticClass());

			if (TestEnemy && EnemyObjective)
			{
				const UNavigationPath* OutputPath = UNavigationSystemV1::FindPathToLocationSynchronously(GetWorld(),
				TestEnemy->GetActorLocation(), EnemyObjective->GetActorLocation(), TestEnemy);
				bEnemyHasPathToObjective = OutputPath->PathPoints.Num() > 0;;
		
				GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Orange, FString::Printf(
					TEXT("Enemy has path to objective: %hhd"), bEnemyHasPathToObjective));
			}
		}
	}

This still allowed the Player to place walls, that would block the enemies’ path to their objective.

For checking if the path is valid, is OutputPath->PathPoints.Num() > 0; the correct way to check this?

Thanks again for trying to help though .

EDIT: for reference, the signature of the method to spawn an (enemy) character is ACharacter* AUtilityFunctionLibrary::SpawnCharacter(const FVector SpawnLocation, const bool bIsHidden).

Well, might take me a bit to get up to speed on what the whole situation is, but probably the best thing to do is break it down to individual steps, so you can see what’s happening.

If you are in the editor, you can turn on “Show Navigation” (press “P”) and you should be able to see the navigation mesh in the level. If you then use Play-in-Editor, and hit Eject (F8), then you should still be able to see the navmesh, as the game runs. As you place objects in the world, make sure that their collision component is set to Dynamic Obstacle. You should be able to witness the navmesh change as you do this. Path queries will work in the modified navmesh as you go.

There may be difficulties involved with pathing if you’re not using AIController or other built-in classes that may need support from AIController or Pawn or some such.

This might help some? Dynamic Navigation Mesh

This might too Unreal Engine 4 Tutorial - NavMesh Rebuild Realtime - YouTube

1 Like

Thanks for the reply , to clarify my project uses a derived class of AAIController, for enemies.

Current collision settings on the actor are shown below, but I think I found what you were referring to, in regards to making it a Dynamic Obstacle:

(Static Mesh settings shown below):

Cube Static Mesh Editor Navigation Category Properties

The navmesh being used, is set to update when such obstacles are placed (runtime generation enabled). The enemies will still try and move through the wall, closest to their objective though (bottom left corner of the screenshot below, marked with a red circle):

Indeed, with the new Dynamic Obstacle settings for the Cube static-mesh. the obstacles have a red border, outstretched around their perimeter, but still, the Player can block the enemies’ path to their objective.

I thank you for your help so far , and I will have to look at the second link you have provided (I looked at the information provided via the first link).

Please let me know what you think the issue could be here.

EDIT: checking the video on the second link, clarifies what I already know (runtime generation must be enabled, to allow enemies to find a path when obstacles are placed during gameplay).

Set AreaClass to NavArea_None to prevent pathing at all. NavArea_Obstacle sets it very costly to navigate through, so it would prefer almost any other path unless it’s very long, but NavArea_Null should prevent it from considering that area at all.

1 Like

Ah, I will try that, and let everyone know how it goes. Thanks :slight_smile:.

Unfortunately, it still produces similar results (the Player is able to block off the enemies’ path to their objective (circled in red below), and enemies will still try to move to the objective (if say, a path that was available at the top of the level, is blocked off by the Player)*, as if there was no wall there, but stop when trying to move through the wall, of course):

I am still not sure how I can solve this problem. If anyone can provide assistance, that would be greatly appreciated :slight_smile:.*

I don’t know what your logic looks like for moving them around, but my guess would be that you’re laying down the blockers, but they are still on a move that would’ve taken them through? Probably should have them stop and recalculate whenever a new thing is placed? Something like that ?

1 Like

Dynamic navigation mesh is enabled, it is only when an obstacle that blocks their route entirely, would they simply try and beeline to their objective (with the origin of such a route being their spawn point, not their current location).

The movement is handled as so:

// Handle this task:
EBTNodeResult::Type UGruxMoveToGoal::ExecuteTask(UBehaviorTreeComponent& OwnerComponent, uint8* NodeMemory) 
{
	Super::ExecuteTask(OwnerComponent, NodeMemory);

	if (OwnerComponent.IsValidLowLevel())
	{
		AGruxAIController* OwnerController = Cast<AGruxAIController>(OwnerComponent.GetAIOwner());		
		if (OwnerController && OwnerController->GetIsAliveKeyValue())
		{
			const EPathFollowingRequestResult::Type MovementResult = OwnerController->MoveToLocation(
					OwnerController->GetTargetLocationKeyValue());
			if (MovementResult == EPathFollowingRequestResult::RequestSuccessful)
			{
				return EBTNodeResult::InProgress;
			}
			if (MovementResult == EPathFollowingRequestResult::AlreadyAtGoal)
			{
				return EBTNodeResult::Succeeded;
			}
			if (MovementResult == EPathFollowingRequestResult::Failed)
			{
				return EBTNodeResult::Failed;
			}
		}
		else
		{
			// This character is dead:
			OwnerController->StopMovement();
			return EBTNodeResult::Failed;
		}
	}
	return EBTNodeResult::Failed;
}

What I will try instead, will be something similar to the pathfinding used in Orcs Must Die!, where if all paths to the objective are blocked off, the enemies will simply try and destroy the nearest blockade to them, to be able to trace a path to their objective.

AIController::MoveToLocation() has several parameters that can be adjusted, and my gut feel is you might have a look at setting the last parameter, bAllowPartialPath = false.

If you need further control over movement, you can also construct your own FAIMoveRequest and configure it as necessary, then pass it to AIController::MoveTo().

You might also find it useful to scan through UPathFollowingComponent – one of those by default should be attached to your AI, but creating your own subclass of UPathFollowingComponent and attaching that to your AI instead can be pretty useful. You can use events that occur in your game to trigger path following updates, including re-pathing, or aborting a move that can no longer be made. (or the ‘simple’ way is to just have every AI check for a new path whenever you drop something, but you can do it more elegantly by notifying the Path that it should change)

Most of these things are things that I’m aware of, not things that I’ve actually implemented before, so I’m kinda spitballing here.

1 Like

That’s okay, I will take those hints into consideration , and get back with an update soon :slight_smile:.

Alright, I have had two of the parameters of AIController::MoveToLocation() adjusted from their default values, that of bProjectDestinationToNavigation and bAllowPartialPath (as can be seen below):

This would not have altered their behavior, so I had the ‘DoesPathExist’ decorator added to their behavior tree:

This caused them not to move towards the objective if their path is blocked, but they still move to another spot, instead of just stopping (see below, their objective’s location is circled in red):

Runtime Fully Blocked Path - Using DoesPathExist Decorator

I will want to look into having a custom movement request made/scanning through UPathFollowingComponent, for future reference. For now though, I think I will have the behavior that I outlined previously, implemented (having them attempt to destroy a blockade, if they have no path to their objective).

If the path disappears while they are on an active move, then you’ll probably need to call StopMovement() or issue a new move.

Assuming that your GruxMoveToGoal is just a MoveToLocation that completes when the move is done, or fails when it fails, then once you enter that branch of the tree, it’s going to stay on that branch until the move either succeeds or fails. Just adding a decorator to it, with no other branches, isn’t going to re-check that. If you check it in your SERVICE, though, then you can set a blackboard variable that will force it into a different branch of the tree, though, if the path becomes blocked.

1 Like

Thanks for the remarks . I am working on the system I have mentioned before (with Grux stopping movement to attack a wall that blocks their path, if their path to the objective is blocked).

Indeed, a new Blackboard key will be set, to have them attack the wall blocking them, instead of continuing to walk into it. When they have destroyed the wall, they will then try and start moving to their objective again.

I will report back here, with how that is coming along in the days ahead.

1 Like

It looks like we’re making something very similar. I’m using the paragon assets to create an Orcs Must Die style game as well. I’ve been stuck on the AI pathfinding for days so I’m definitely going to follow this thread closely!

1 Like

That’s interesting to see MasterFuzzFuzz, I will show what I have come up with soon, on this thread :slight_smile:.

I have not quite finished my implementation of this custom AI yet, but for reference, with the new AI Controller constructor implementation signature (as shown below), the enemies no longer clump together, when trying to reach their objective :slight_smile::

AGruxAIController::AGruxAIController(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer.SetDefaultSubobjectClass<UCrowdFollowingComponent>(TEXT("PathFollowingComponent")))