DoN's 3D-Pathfinding / Flying-AI system (with full source!)

So, just realizing that the SchedulePathfindingTask call is working perfectly, but that it only calculates the path and doesn’t handle any locomotion. There’s quite a bit more in BTTask_FlyTo that actually moves the pawn. I’m considering adjusting the implementation for my project to be more like BTTask_MoveTo where it sets up the navigation in the task, but performs all the locomotion inside the AIController. That way I’ll be able to more easily create an interface for calling custom scripted FlyTo events.

…but yeah, maybe not until I really need it :stuck_out_tongue:

@, another new kid question…is there any way to abort an AI unit’s FlyTo task? I’m setting up some basic AI States off a selector, but new FlyTo commands don’t seem to register until the FlyTo command from the previous state completes. Is that expected? Apologies if this is a basic function of AI Behavior Trees…I literally just started working with them a few days ago, but from the tutorials I’ve been watching, this doesn’t seem to be the behavior for MoveTo tasks. They seem to abort immediately when the selector chooses a new sequence.

Behavior Trees will automatically abort a task for you subject to the following conditions:-

  1. A higher priority task (i.e to the left) has evaluated its decorator condition to true (so it’s ready to run)
  2. This higher priority task has explicitly set the “Observer Aborts” field in the detail panel to an appropriate value (typically “Aborts Both”). Use the color-coding provided in the details panel to figure out which nodes will be aborted when the node you’ve currently selected is ready to run.

For what you’re doing here behavior trees look like a good fit, though it’s also a good idea to constantly reevaluate whether BTs are the right solution for a particular AI problem or if you’d be more productive in moving to a more traditional setup (easier to debug IMO). Over time I’ve found myself using a more “hybrid” solution for AI logic.

@, I had a feeling it was something stupid I was missing >_< Forgot to set the “Observer Aborts” value properly! Must have gotten distracted during the tutorial I was watching. Works perfectly now :slight_smile:

Also, totally agree about being wary when it comes to implementing too much in BTs or Blueprints in general. They are a nightmare to maintain and debug, but I do plan on having non-coders assist with AI development at some point so I want to keep higher level logic exposed in BTs, then wrap more complicated logic in native functions.

Thanks again for the help!

Hey @ - Any news on the 4.12 version of this plugin?

I wasn’t working on a 4.12 version at any point myself so unless @MatzeOGH or someone else has ported and tested it, probably not soon!

The v1.3 update that I posted last week might even work with 4.12, I simply do not know. Worst case, if Epic has deprecated some APIs there may be some compilation errors and that should be the only issue to get it working.

It takes me a fair amount of time to patch, test and reupload stuff so with the rapid pace at which Epic releases new versions it will always take me some time to release matching updates.

Yes I will port the plugin to 4.12 in the future but it got moved to the next sprint. We are also planning on getting this plugin more in line with the Unreal AI system so designers only have to learn them once

This sounds awesome!

4.12 support for v1.3 released

Latest plugin and sample project downloads are available both in the OP and in this link:

I installed 4.12 for the first time today and while I was at it I thought I’d port the plugin to 4.12.

Just to set expectations on future releases: my project is still in 4.11 so any new features/bugfixes will most likely funnel over to 4.11 first. I’ve created a separate 4.12 branch in Github for today’s release.

Thanks for the 4.12 update, @!

Also, just wanted to let you know that I was able to fix the ensure(MyNavData) crash in this function…


void UPathFollowingComponent::RecacheNavigationData()
{
	if (MovementComp != NULL)
	{
		const FNavAgentProperties& NavAgentProps = MovementComp->GetNavAgentPropertiesRef();

		if (ensure(GetWorld() && GetWorld()->GetNavigationSystem()))
		{
			MyNavData = GetWorld()->GetNavigationSystem()->GetNavDataForProps(NavAgentProps);
			// we should get something by now!
			ensure(MyNavData);
		}
	}
}

…by destroying the default PathFollowingComponent in my project’s AI controller constructor:


APFAIController::APFAIController(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
	bUpdatePitchWithFocus = true;

	PathFollowingComponent->DestroyComponent();
	PathFollowingComponent = nullptr;
}


Kinda ugly and not sure how safe it is, but everything seems OK for now.

Howdy! This is incredibly useful. I’ve been struggling to get functional flying AI in my project and this is the only thing that’s looked promising to me.

With that said, I’ve hit a snag. Not a bug, just a snag.

ue4_donflying_behaviortree.png

I’m having trouble getting my flying pawn to chase the player. It flies between waypoints fine, but whenever the player steps into its vision cone it loses confidence. It will fly to the player’s location, but then it stalls and waits. If the player reactivates its senses by making a noise or stepping out and into its vision cone again, then it repeats.

I’ve tried to replicate the same behavior tree you use in your sample project, and it looks very simple, but I’m having trouble.

Anyway, cheers and thank you.

What’s your BTD_CheckDistance doing? I was running into problems using a standard UE4 distance check (can’t remember which one and don’t have access to my project right now) cause there was a (bad) assumption made in the function that in order for the distance check to return true, there was an additional check to ensure there was some minimal vertical offset between the characters that had nothing to do with the point-to-point distance.

@Cinebeast - The first thing I’d do is to dock your behavior tree window alongside the viewport and watch watch exactly how the control flows when the player gets close enough. If there’s any setup issues or if your nodes aren’t aborting along expected lines this will immediately let you know.

Pressing the ’ key with your bot on the screen activates Unreal’s AI debugger which will automatically set the behavior tree’s debugger to pick up your bot’s actions. This is useful because the behavior tree needs to know which AI controller it should show you debug info for and ’ key automatically does that while also displaying both the pawn’s name and the pawn’s controller name too.

If you can record a GIF/video of the viewport and BT tree alongside that would help too. Sometimes I do this myself for really complicated behavior tree scenarios where things happen so fast that you need to record a video, slow it down (in VLC, etc) to learn exactly what the behavior tree is doing!

Here’s my Check Distance decorator:

ue4_behavior_distance_check.png

Nothing fancy, as you can see. I’m using this same decorator in a similar behavior tree for a land-based enemy, who works fine.

I cobbled together a gif of the tree and the viewpoint in tandem:

http://gph.is/292viCn

I do a bit of hopping around, weaving in and out of the enemy’s attention. As soon as I weave in, it wakes up and flies to me, but it will only fly to where I was standing when I weaved in, instead of continuously following me.

Basically the enemy will fly to wherever the player was last standing and then spams that branch. I don’t think the player’s position is being updated – then again, if it weren’t, wouldn’t the “Close to player?” branch be activated? So I don’t know.

Weirdly enough, I don’t think I’ve ever made a gif before. It’s easier than I thought it would be.

Anyway, thank you for your help.

Looking at the last part of the GIF where the bot stalls, the Combat —> Fly To execution path is repeatedly aborted and then re-activated, so “Fly To” barely gets a to run itself. We need to understand why your “DummyState” keeps switching rapidly between “Combat” and “Something else” (the GIF doesn’t show that part of your BT).

During initial testing it’s useful to turn on the debug visualizer in the Fly To node (under Debug Params -> “Visualize Optimized Path”). This will tell you exactly when paths are generated and what they look like. Also check your “Output Log” for any error messages from the navigation system.

I’m also curious to know what “FlightLocationKey” points to in your “Fly To” node, where are you setting it from and how often you’re setting it.

Checking the output log, I see I’m getting a lot of errors like this:

Destination (XYZ) is outside world bounds. Please clamp your destination within the navigable world or expand world size under settings if necessary.

That sounds bad. Something to do with the World Dimensions of the Don Navigation Manager, maybe?

As for your other points: FlightLocationKey is being set at the beginning of the tree in a service:

ue4_bat_bts_dummystate.png

Every tick we check to see if some variables have changed, including the player’s position, which is saved as Target Location, which is later used by the Fly To node. I know that part is working because I can see the vector updating at runtime, the Fly To node just doesn’t seem to recognize it.

The Dummy State seems to be working fine too – as soon as the player’s spotted it switches from Patrol to Combat, and it sticks there. (According to the behavior tree panel and a print string.)

Absolutely. The plugin is telling you that you’re doing pathfinding outside “world bounds” so your query will simply be rejected.

Use the DonNavigationManager actor to increase the size of the navigable world to fully encompass your world.
N.B. the larger the bounds, the longer your game will take to load the map.

“Infinite” / Unbound worlds are also supported as an experimental feature (v1.3 onwards) so if you’re willing to trade lower pathfinding performance for being able to pathfind anywhere (with zero load time for your game too) then check out this post of mine to turn that feature on. I thought people would have been really excited by that feature but no one seems to have noticed that post lol :slight_smile:

If your map is small, I recommend just increasing the size of your navigable world and keeping it bound. Any additional load time you incur will not be that bad in packaged builds (if you’re debugging Visual Studio in DebugEditor mode you’ll notice a real hit though).

Always keep an eye on the output logs. The plugin uses logs to communicate common issues and their resolutions too.

That was it! The enemy flies to the correct point now.

However, he’ll only follow the player if they make a move after the enemy has stopped flying. If the player gets out of the enemy’s way and holds still, even if he’s twenty feet away, the enemy won’t follow until he moves again.

This has the effect of making the enemy look like it’s blind, only reacting to noise or abrupt movements, but it’s not really what I’m going for.

And in the behavior tree, the same issue persists. The “Fly To” node in the right branch is still being spammed, despite the fact that the player is out of reach.

Two issues here:

  1. If your bot is not chasing the player’s updated location while it is still traveling, that is expected behavior (see “live pursuit” below).

  2. If your bot is totally dead after traveling once (until the player comes near again) that’s got be a a setup issue in your behavior tree!

Your DummyState is probably no longer “Combat” because the bot went out of the player’s range OR your service is constantly flipping it between Combat and Patrol for some reason; only that or a rejected query (logs will tell you) would explain the spamming on the right branch.

Live Pursuit

Back to 1) - FlyTo node doesn’t provide this out-of-the-box! It is a low-level task that simply 1) solves a path and 2) moves your pawn along that path.

In my game I use a simple distance delta check during pursuit and abort FlyTo based on that. If the distance between the “last known target location” (i.e. the location we’re currently moving to) is significantly greater than the “live target location” (i.e. the current location of the pawn we’re chasing) then I abort and restart the FlyTo task using a boolean decorator.

Maybe you can chain a boolean decorator (for easy aborts) with your inverse distance check. You’ll have to play around with it a bit to get the aborts firing correctly though.


Plugin enhancement idea:
The plugin can be enhanced to provide “live-pursuit” functionality OOTB (using a TargetActor" BB key), but I’m extremely busy working on my project right now and can’t commit to this, so managing it in your behavior tree and service is your best bet for now.

Thanks for the pointers, let me see.

I checked the output log and I am getting some more errors:

DoNNavigationLog:Error: Pawn’s initial position overlaps an obstacle. Pathfinding will not work from here, pawn needs to move to a nearby free spot first.
DoNNavigationLog:Error: Error: Invalid Destination (XYZ) passed to navigation path solver

I’m sorry to say I don’t understand these.

I like the sound of this, but I’m not sure how or where to implement this. How do I get the inverse distance check to recognize the difference between the “last” and “live” target locations and abort accordingly? I’m very much a behavior tree novice.

Seems like I’m getting closer and closer to what I want, though. Thanks again for the help.