DoN's 3D Pathfinding for Flying A.I. [Support Thread]

4.16 version available!

Version 1.7 of DoN’s 3D pathfinding with support for UE 4.16 is now available on Github: https://github.com/VSZue/DonAINavigation

Marketplace version has been submitted too!

Hi!

First of all thank you for everything you do, this is truly great :slight_smile:

I have issues with making this plugin work. I have replicated everything from your test map (number 1), but I end up with this error:

DoNNavigationLog:Error: Error: Invalid Origin (X=-420.000 Y=-3398.000 Z=942.000) passed to navigation path solver
DoNNavigationLog:Error: Error: Invalid Destination (X=116.000 Y=2135.000 Z=4307.000) passed to navigation path solver
DoNNavigationLog:Warning: Pawn’s initial/final position overlaps an obstacle. Attempting to find substitute vector (a nearby free spot) for pathfinding…

Except that there is no overlap nor obstacle at all. I have literally just a sphere (default pawn) trying to reach a smaller one (goal). The rest of the elements are the basic UE setup and the DonNavigationManager that largely encompasses everything.

Also, and it is probably related, I don’t see the voxels at all when I’m moving through the space. I can see the volume bounds alright but not the green/red boxes.

Any idea what could trigger these errors and how to fix them?

Thank you!

Invoke from C++

I am trying to invoke the HotPursuit behavior from C++. I have a Don Navigation Manager placed in my map with appropriate sizing.

Here is my enemy pawn controller source:


#include "Ascentroid.h"
#include "AscEnemyController.h"

AAscEnemyController::AAscEnemyController(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
{
	BlackboardComponent = ObjectInitializer.CreateDefaultSubobject<UBlackboardComponent>(this, TEXT("BlackboardComponent"));
	BehaviorTreeComponent = ObjectInitializer.CreateDefaultSubobject<UBehaviorTreeComponent>(this, TEXT("BehaviorTreeComponent"));

	FString Path = "/Game/DonNavigationSamples/BehaviorTree/HotPursuit";
	BehaviorTree = Cast<UBehaviorTree>(StaticLoadObject(UBehaviorTree::StaticClass(), nullptr, *Path));
}

void AAscEnemyController::Possess(APawn* InPawn)
{
	Super::Possess(InPawn);

	if (BehaviorTree)
	{
		if (GetWorld() != nullptr)
		{
			auto TimerDelegate = FTimerDelegate::CreateUObject(this, &AAscEnemyController::InitAI, InPawn);
			GetWorldTimerManager().SetTimer(AITimer, TimerDelegate, 1.f, true, 0.f);
			//GetWorld()->GetTimerManager().SetTimer(AITimer, this, &AAscEnemyController::InitAI, 1.f, true, 0.f);
		}
	}
}

void AAscEnemyController::InitAI(APawn* InPawn)
{
	if (GetWorld() == nullptr)
	{
		UAscUtil::Debug(TEXT("GetWorld() is null!"));
		return;
	}

	auto CurrentPlayer = static_cast<AAscPlayerCharacter*>(UGameplayStatics::GetPlayerCharacter(GetWorld(), 0));

	if (CurrentPlayer == nullptr)
	{
		UAscUtil::Debug(TEXT("CurrentPlayer is null!"));
		return;
	}

	BlackboardComponent->InitializeBlackboard(*BehaviorTree->BlackboardAsset);

	auto Target = Cast<AAscEnemy>(InPawn);

	BlackboardComponent->SetValueAsObject("Target", Target);
	BlackboardComponent->SetValueAsObject("SelfActor", Target);
	BlackboardComponent->SetValueAsVector("TargetLocationKey", CurrentPlayer->GetActorLocation());
	BlackboardComponent->SetValueAsVector("FlightGoal", CurrentPlayer->GetActorLocation());

	BehaviorTreeComponent->StartTree(*BehaviorTree, EBTExecutionMode::Looped);

	UAscUtil::Debug(TEXT("InitAI started!"));

	GetWorld()->GetTimerManager().ClearTimer(AITimer);
}

When I enter the map, however, I get the following error(s):


[2017.05.30-00.03.39:187][264]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.39:200][265]LogCoherentUIGT:Error: ConsoleAPI coui://UIResources/Gui/static/js/coherent.js:652:16: TypeError: undefined is not an object (evaluating 'n("#asc-hud-content-player-placement-"+t+"-name").css("visibility").indexOf')
	coui://UIResources/Gui/static/js/app.17eabe89d9f670abddf3.js:1:30009
[2017.05.30-00.03.39:210][266]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.39:225][268]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.39:244][270]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.39:266][272]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.39:288][274]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.39:308][276]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.39:329][278]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.39:350][280]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.39:372][282]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.39:392][284]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.39:413][286]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.39:435][288]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.39:454][290]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.39:474][292]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.39:495][294]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.39:495][294]LogScript:Warning: Accessed None trying to read property CallFunc_Get_Goal_Bot_Pawn
	Service_Pursuit_Helper_C /Game/Ascentroid/Maps/UEDPIE_0_MAP_Scale_Testing.MAP_Scale_Testing:PersistentLevel.AscEnemyController_0.BehaviorTreeComponent.Service_Pursuit_Helper_C_0
	Function /Game/DonNavigationSamples/BehaviorTree/Service_Pursuit_Helper.Service_Pursuit_Helper_C:ExecuteUbergraph_Service_Pursuit_Helper:00AA
[2017.05.30-00.03.39:495][294]PIE:Error: Error Blueprint Runtime Error: Accessed None trying to read property CallFunc_Get_Goal_Bot_Pawn from function: 'ExecuteUbergraph_Service_Pursuit_Helper' from node: Branch in graph: EventGraph in object: Service_Pursuit_Helper with description: Accessed None trying to read property CallFunc_Get_Goal_Bot_Pawn
[2017.05.30-00.03.39:496][294]LogBlueprintUserMessages: [Service_Pursuit_Helper_C_0] RESTARTING PURSUIT (Player has gotten way ahead of this bot!)
[2017.05.30-00.03.39:517][296]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.39:537][298]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.39:557][300]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.39:579][302]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.39:599][304]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.39:619][306]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.39:640][308]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.39:661][310]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.39:682][312]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.39:718][314]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.39:736][316]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.39:758][318]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.39:786][320]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.39:805][322]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.39:826][324]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.39:848][326]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.39:868][328]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.39:888][330]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.39:910][332]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.39:930][334]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.39:950][336]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.39:971][338]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.39:991][340]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.40:002][341]LogScript:Warning: Accessed None trying to read property CallFunc_Get_Goal_Bot_Pawn
	Service_Pursuit_Helper_C /Game/Ascentroid/Maps/UEDPIE_0_MAP_Scale_Testing.MAP_Scale_Testing:PersistentLevel.AscEnemyController_0.BehaviorTreeComponent.Service_Pursuit_Helper_C_0
	Function /Game/DonNavigationSamples/BehaviorTree/Service_Pursuit_Helper.Service_Pursuit_Helper_C:ExecuteUbergraph_Service_Pursuit_Helper:00AA
[2017.05.30-00.03.40:003][341]PIE:Error: Error Blueprint Runtime Error: Accessed None trying to read property CallFunc_Get_Goal_Bot_Pawn from function: 'ExecuteUbergraph_Service_Pursuit_Helper' from node: Branch in graph: EventGraph in object: Service_Pursuit_Helper with description: Accessed None trying to read property CallFunc_Get_Goal_Bot_Pawn
[2017.05.30-00.03.40:003][341]LogBlueprintUserMessages: [Service_Pursuit_Helper_C_0] RESTARTING PURSUIT (Player has gotten way ahead of this bot!)
[2017.05.30-00.03.40:012][342]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.40:033][344]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.40:053][346]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.40:073][348]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.40:092][350]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.40:112][352]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.40:132][354]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.40:151][356]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.40:171][358]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.40:190][360]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.40:210][362]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.40:229][364]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.40:249][366]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.40:268][368]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.40:287][370]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.40:307][372]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.40:326][374]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.40:345][376]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.40:364][378]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.40:383][380]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.40:402][382]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.40:421][384]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.40:440][386]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.40:459][388]LogBlueprintUserMessages: [PickBot_C_0] Invalid Goal
[2017.05.30-00.03.40:468][389]LogScript:Warning: Accessed None trying to read property CallFunc_Get_Goal_Bot_Pawn
	Service_Pursuit_Helper_C /Game/Ascentroid/Maps/UEDPIE_0_MAP_Scale_Testing.MAP_Scale_Testing:PersistentLevel.AscEnemyController_0.BehaviorTreeComponent.Service_Pursuit_Helper_C_0
	Function /Game/DonNavigationSamples/BehaviorTree/Service_Pursuit_Helper.Service_Pursuit_Helper_C:ExecuteUbergraph_Service_Pursuit_Helper:00AA
[2017.05.30-00.03.40:468][389]PIE:Error: Error Blueprint Runtime Error: Accessed None trying to read property CallFunc_Get_Goal_Bot_Pawn from function: 'ExecuteUbergraph_Service_Pursuit_Helper' from node: Branch in graph: EventGraph in object: Service_Pursuit_Helper with description: Accessed None trying to read property CallFunc_Get_Goal_Bot_Pawn
[2017.05.30-00.03.40:468][389]LogBlueprintUserMessages: [Service_Pursuit_Helper_C_0] RESTARTING PURSUIT (Player has gotten way ahead of this bot!)

It seems like the “Get Goal Bot” function call in “Service_Pursuit_Helper” is returning ‘None’. I can’t figure out why. Any ideas? It feels like I am missing something obvious. You can see I try setting the target/pawn with SetValueAsObject, but I have no idea if this is the right way to do it.

Thanks!

@Aurel - A screenshot would help, but my first guess is that your target pawn has a collision body or prop on it whose collision profile is set to WorldStatic or WorldDynamic and is therefore being picked up as a navigation obstacle by the system. That would explain why your origin/goal is always invalid.

   @jvukovich - Looks like you're reusing the sample behavior tree of the demo project. The demo project has been setup in such a way that it picks goals from pawns via the "Goal Actor" public variable (which is set on a pawn from the map via world outliner). Additionally, each pawn implements a simple interface called "Demo Pawn Interface" which allows the behavior tree task to extract the goal from any arbitrary pawn. Now, if you're testing with the sample BT and not doing either of these, then the "Invalid Goal" message which you're seeing appears.

Of course you will eventually want to switch out the sample “Pick Goal” / “Pick Bot” tasks with your BT task/logic/implementation which sets the FlightLocationKey blackboard to a suitable vector depending on the specific gameplay tasks your A.I. is trying to achieve.

Edit:- I notice you’re already setting the BB keys directly. In that case you should not be using the Pick Bot (“Refresh Pursuit Goal”) node in your behavior tree at all. That node will attempt to override the goal with its own logic which is not desirable in your case.

Would someone be able to build and upload the plugin for mac? I’m having some problems trying to do that.

Have you tried compiling from the latest version on Github? (4.16 version). It has some fixes for platform specific includes that may help.

I’ve been using Linux as a target platform for the plugin for a while now although I’ve never tested on Mac. I’m on a slightly older version of the plugin myself though so if you can post the compilation errors you’re seeing someone may be able to suggest a quick fix or workaround.

If someone already has a Mac build handy do feel free to post it here :slight_smile:

I was having trouble building the source but I can comment on building the plugin once I get that working. Another thing I would love to ask you in the meantime is your thoughts on using this for 2D. I only ask because I got to the point in your tutorial video DoN's 3D Pathfinding for Flying AI - Free UE4 plugin! - YouTube where the schedule dynamic collision update takes in a mesh as an argument whereas in many 2D games this would be a sprite. I suppose the 2D game could have meshes used for dynamic collision objects though. Do you have any other thoughts or advice on using this for 2D? Other than obvious things like set the number of voxels in the Y direction to 1. By the way thank you for releasing such a great looking plugin, I cannot wait to try it!

@sotroll - You should be fine. That function takes in a Primitive Component (even though its called Mesh) so assuming you’re using something like the Paper Sprite Component (which is a primitive component) everything should just work in theory. In practice though, this flow hasn’t been tested before, so there’s that. If you really need dynamic collision handling for your usecases, another option is to use the Infinite/Unbound Manager and thus free yourself completely from manual handling of dynamic collision. The Unbound mode is fully automatic/uncached so it picks up all dynamic changes instantly. This does make it much slower though.

Other than that, expect to spend some time on initial setup / tweaking the world grid and voxel sizes, etc. There’s a sweet spot for every project and it can take some experimentation to find the right one for yours.

OH! I get it now, thank you so much! I am new to behavior trees, but now that you have explained it, it makes perfect sense. Thanks so much for this awesome plugin & helping out the community.

4.16 Marketplace version released!

Changelog:-

  1. Platform specific include fixes
  2. Linux compilation warnings cleaned up

Thank you :slight_smile:

I tried installing this is 4.15.1 today (BP only) and from the launcher it looks like the install was completed fine, but unlike any other plugin, I can’t find the nodes when searching for them. The only thing I can find is one icon in engine assets folder for the plugin.

Can anyone help?

If you go to the plugin manager in the editor does DonAINavigation show up under “AI Navigation”? (Edit/Plugins) If so, is it enabled?

On a side note, it sounds like you’ve installed it into the engine folder, you could try installing it to just your project and see if that works instead. (in YourProjectName/Plugins/DonAINavigation/)

How is it that I can stop the movement caused by the “Fly To” Task?

Abort the current “Fly To” task in the behavior tree. If you’re unfamiliar w/ how to do that, I suggest running through some tutorials :slight_smile: I think I ran through this one:

Got a little treat to share for any of you working w/ large levels & large AI counts. Enables/Disables AI processes, as needed. This saved me about 4.6ms:


void APFAIController::UpdateAIProcessCulling()
{
    if (PFCPawn.IsValid())
    {
        APFGameState* PFGS = Cast<APFGameState>(GetWorld()->GetGameState());

        // Check nearest enemy distance (not just player locations cause AI fight other AI)
        TArray<APFCharacter*> enemyList;
        PFGS->GetEnemyPawns(this, enemyList);
        float closestDistSq = BIG_NUMBER;
        for (int i = 0; i < enemyList.Num(); ++i)
        {
            const float distSq = (PFCPawn->GetActorLocation() - enemyList*->GetActorLocation()).SizeSquared();
            if (distSq < closestDistSq)
            {
                closestDistSq = distSq;
            }
        }

        const bool bWantsKillProcesses = !PFCPawn->WasRecentlyRendered() && closestDistSq > FMath::Square(processCullingDistance);
        if (IsActorTickEnabled() == bWantsKillProcesses)
        {
            // Enable/Disable movement component
            UPFCharacterMovementComponent* PFCMC = PFCPawn->GetPFCharacterMovement();
            if (PFCMC->MovementMode != EMovementMode::MOVE_None)
            {
                PFCMC->SetActive(!bWantsKillProcesses);
                PFCMC->SetComponentTickEnabled(!bWantsKillProcesses);
            }

            // Enable/Disable sight/sound/damage senses
            GetAIPerceptionComponent()->SetActive(!bWantsKillProcesses);
            GetAIPerceptionComponent()->SetComponentTickEnabled(!bWantsKillProcesses);

            SetBehaviorTreePaused(bWantsKillProcesses);

            // For good measure, kill weapon tick, too...
            AWeapon* equippedWeapon = PFCPawn->Inventory->GetEquippedWeapon();
            if (equippedWeapon != nullptr)
            {
                equippedWeapon->SetActorTickEnabled(!bWantsKillProcesses);
            }

            // Controller & pawn should both get their ticks updated
            SetActorTickEnabled(!bWantsKillProcesses);
            PFCPawn->SetActorTickEnabled(!bWantsKillProcesses);
        }
    }
}

I cannot figure out why the unit is not moving, it generates the path but doesn’t move. The Fly To task is definitely being called and the target location is correct. There are also no messages in the output log.

Interesting! Curious to know whether packaged builds (shipping) also benefit to the same degree, often seen major gains in the debug/dev builds that translate a bit differently to the optimized binaries. Anyway, I’ve long been planning a move away from BTs for my A.I. and I think stats like these just add to that impetus. There’s a lot going on in there!

Hey, the plugin routes all locomotion to your pawn for you to determine how it should move.

This is handled inside the “Add Movement Input” function. For the default character/pawn classes Unreal already provides a basic implementation for this function so most pawns will automatically move.

To debug this I would check what is happening when the Fly To node calls your pawn’s Add Movement Input. If it’s a BP only project you can also implement the “DoN Navigator” interface and the “Add Movement Input Custom” function to intercept the call (either for your testing purposes, or to provide your own locomotion logic).

HTH!

I compared on a “Test” build w/ cooked content. I’ll try out a packaged shipping build and get back to you :smiley:

Sorry, I should have explained better. The flying AI works perfectly fine if it doesn’t have to move past the obstacle but if it does have to move near it, like in the image, it doesn’t move at all.

I would still recommend the same debug strategy: You need to see what is happening inside your pawn’s Add Movement Input code and why it is not responding to movement requests for this case.

Going by your description that a) the path is generated successfully and b) The Fly To node is busy/active in the behavior tree. If both of these conditions are indeed satisfied, the plugin’s work pretty much ends there and it is up to the pawn to do the rest. That’s why you need to check what your pawn’s Add Movement Input is doing.

Now if the path was not generated, then there are other factors to consider like insufficient clearance area above the floor for either your pawn or for its goal (which can be solved by nudging the height of the goal/etc). However that’s likely to happen only for large bots and if something like that did happen, the logs would have definitely told you why pathfinding did not occur. In your case the logs are clean, so the issue is likely elsewhere.