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

@Doublezer0 Thank you.

I already had bEnableShadowVariableWarnings in my local build so that explains why I didn’t see any compilation issues but others did. GameplayTasks however should have already been a part of DonAINavigation.build.cs. In fact, that was the only change in the plugin between 4.11 and 4.12!

Anyway thanks for posting this, should help others get going until I release an update. There’s a ton of important bugfixes that need to come in too and this is probably a good opportunity to publish those as well.

4.13 support released (v1.4)

You can download the latest plugin and sample project using the link below or from the title post of this thread.

In addition to 4.13 support, this release contains some important bugfixes (summarized here, thanks to @rcdarcey for reporting those.) Linux and Mac compilation issues have also been fixed. If you’re targeting these platforms you’ll need to manually build the plugin from source.

Hy,
God, it´s been so long that I was away that I can’t even remember how to crop my own scene :wink:
I would like some guidance in a scenario that bothers my mind: I have a bot that has wings and goes straight path to the goal … cool and all easy here. Now if I put 2 obstacle right and left with one free voxel to pass between it will not pass as each wing want´s a voxel more, but if I change the roll value to 90º it goes in as it´s body only needs a voxel free. My question is what should I mess with to trigger the calculation of the affected voxels with different roll values (or any rotation type) before I choose the right path?
Regards

question.png

Did the green one go straight through the brick wall or did it actually find the right way?

In any case, this is not supported right now and there is no simple code change that will fully support rotation-aware pawns. If a pawn only needs to rotate once before navigation begins to reach its goal then one can easily test various pawn rotations iteratively and test pathfinding for each rotation. But in a more complex scenario a pawn may need to rotate several times in the course of navigation to successfully complete its journey. Maybe it rolls 90 degrees first to squeeze through an opening and then it rolls back to original state to squeeze through another, etc. So if your usecase is that complex you’ll need to test suitable pawn rotations for every voxel and also memorize the rotation used at each voxel as part of the results. I expect this will have very poor performance without carefully tuning and optimizing the algorithm end-to-end specially for this usecase.

If your bot has retractable wings and rolling is not a gameplay requirement then you can apply a workaround:

A creature with retractable wings (like a bird) doesn’t need collision bodies on the wings because a bird will simply retract its wings to squeeze through tight corners. That way you end up with just one collision voxel (on the core body) which is better for performance too. Even if you really need collision on the wings, you can always set their collision profile to something that the plugin isn’t configured to pick up (eg: BlockAllExceptNav). In this scenario you’ll still need to retract the wings to allow the pawn to squeeze through though.

Another workaround along these lines would be to exclude the wing’s collision bodies for navigation queries (like I explained above) and use supplementary triggers/line-traces that tell your bot when it needs to roll to successfully squeeze through a gap.

I have similar challenges with my flying AI in which the path calculated is not always achievable for my AI, due to drag etc. I use line traces to steer my AI a bit in the right direction to take into account the drag. I think you have to do something similar as suggested by which is ignoring the wings and trying to decide how to rotate when you are flying. The risk is that you get a solution in which there is no way to rotate your AI, but i would expect these to be rare and you can probably ensure those spots are marked as not accessable manually (placing a 1x1x1 volume on those spots for example)

This is awesome work!:slight_smile: I applied it to my project, and found a problem
In BTTask_FlyTo.cpp, TaskMemoryFromGenericPayload Function, there is a judgement statement like:

// Is it still working on this task or has it moved on to another one?
if (ownerComp->GetActiveNode() != this)
	return NULL;

11.jpg
If FlyTask is a node of SimpleParallel, It will make task failure. I just commented them out,
Is there some better way to fix it? :confused:

@dxd39

Try changing the check to this:


if (ownerComp->GetActiveInstanceIdx() != payload->ActiveInstanceIdx)
       return NULL;


I haven’t dived into how SimpleParallel actually works, but assuming that the task id is maintained throughout the task’s lifespan (it should be as far as I can tell) then this should work.

To provide some background, that check prevents undefined behavior (via reinterpret_cast) if a bot moves on to a new task by the time the pathfinding result delegate fires. A new task will have a different memory layout so the reinterpret_cast will be rendered unreliable. I’m honestly not a fan of using either “NodeMemory” or C style/reinterpret casts to pass data around but this usage of uint8* NodeMemory seems to be a core part of Unreal’s BT design and the prescribed way of storing task-level data (more on that here)

Thanks for pointing this out!

@, thanks for the advice! It work fine now.

@,

Hey man! Looking for your opinion on how I should go about solving this new “problem” I have. Basically, I implemented a circle strafe action for my flying character which calculates slices along an arc and then iterates through the array of points along the arc w/ FlyTo commands. However, whenever I reach one of the points, the AI unit pauses very briefly before flying to the next point along the arc. From what I can tell, this is just due to how the behavior tree updates. Can you think of a possible solution around it?

Here’s a shot that demonstrates my circle strafe implementation and the sliced up arc.

Here’s a shot of the portion of the behavior tree that’s driving the navigation. Basically, this sequence loops every time a section of the strafe arc is completed, but there’s a pause for like a frame or two when that loops happens.

My initial thought was to create a version of the FlyTo node that takes an array of points, but blackboard keys can’t be arrays. My next thought was to create a DonNavigation interface class which I can add to my pawn, and that interface could then gain access to an array of fly to locations that I store on the pawn.

Thanks in advance for your help and hope you’re doing well!

So the behavior tree framework itself is the only bottleneck here?

If so, one trick I can think of is to move your bot via velocity (tick based) rather than via offsets (AddMovementInput based). You will still use AddMovementInput to set a “desired velocity” but this velocity will be consumed in your bot’s tick. That way even if the behavior tree is busy realigning itself between tasks your bot will continue to be mobile for those few frames thus creating the illusion of continuity.

Another approach is to write a new BT task that condenses your entire “Circle Strafe” sequence into a single unit. This task would generate the strafe points, call the low-level pathfinding API directly and being self-contained there should be no downtime. However if you want to write a “Fly To Multiple” to support future usecases as well then you can create a simple UObject data container which holds your TArray<FVector> inside of it and pass that around via a blackboard UObject key (I found this tip from Unreal’s BT dev on answerhub btw)

Great to hear from you too! My project will releasing very soon and I can’t wait to share Drunk On Nectar with the world :slight_smile:

GAH! Brilliant. Super simple solution. Just reduced braking deceleration in Character Movement Component and looks perfect. Thank you!

Great to hear! Super stoked to see the final product. Definitely let people know here when you release. My reach isn’t too far on the internet, but I’m totally happy to echo any marketing efforts you have.

@,

Another quick question for you…

I moved over to use your “unbound” navigation manager a while ago. It’s been working great, but ran into an issue and tried to use DrawDebugVolumesAroundPoint to help debug, but that no longer seems to display any volumes with the unbound manager. Is that expected? Looks like this call right at the top of the function is returning null.


void ADonNavigationManager::Debug_DrawVolumesAroundPoint(FVector Location, int32 CubeSize, bool DrawPersistentLines, float Duration, float LineThickness, bool bAutoInitializeVolumes/* = false*/)
{
	auto volume = VolumeAt(Location);
	if (!volume)
		return;
...

Thanks!

Hi,
In my game Ai always follow the players, but I found that the player position change Ai Pathfinding is not always updated, how should I do?
watch?v=m6LDPvAK0T0

@rcdarcey - For debugging unbound volumes you need to get their center point by calling FVector myLocation = VolumeOriginAt(FVector WorldLocation); and then manually draw the volume yourself. Best option would be to make a new function by porting the rest of the code from Debug_DrawVolumesAroundPoint.

Make sure you remove all dependencies on FDonNavigationVoxel* by using its FVector equivalent functions. Unbound managers do not store any volumes in memory at all so you’re always dealing with FVectors instead of pointers. This is what allows the “infinite” pathfinding feature while making them much slower as a trade-off.

@wuyukang - Hey, I didn’t watch all 6 minutes of that video, but here are some recommendations based on what I observed:

  1. Have you looked at the Pursuit example (5.A) in the sample project? Your behavior tree needs to make sure that the Fly To task is retriggered whenever then player-to-bot distance has exceeded a certain threshold. Without this the bots will just hang around at their last travel point waiting for something to happen. Check out the sample project’s Pursuit behavior tree, Service_Pursuit_Helper blueprint and SimpleAbortWrapper decorator to see this in action.
  2. Check the logs and see what kind of pathfinding failures (if any) are reported there.
  3. For initial testing, reduce the number of A.I. bots to just one instead of three. Once you fix all pathfinding issues for a single bot and know everything is working then you can start adding more.

Thanks, @! I’ll look into that if I end up sticking with the unbound volumes. In the short term, went back to bound volumes to help debug and figured I’d keep it that way for performance reasons until (if) my levels ever really required them.

Thanks for your help, I use your behavior tree and now the navigation looks right. I added 10 bots, no problem. Thanks again for writing this awesome plugin.

https://www…com/watch?v=Vgx7ZWn-fwE

Glad to hear! :slight_smile:

Keep an eye on the logs from time to time (especially during complex usecases like the inside of the glass enclosure). It will keep you informed about query timeouts or other pathfinding issues (due to voxels size being too large, etc). I did notice some debug spheres showing up in your video and those are usually an indication of some pathfinding issue that the logs will tell you more about.

My map is large, so the voxel is relatively large, if I reduce the voxels size, will greatly increase the load time, but the size of the map for my game is still relatively small. I tried to use unbound volumes, but sometimes there will be serious performance problems.

However, I rarely need the game indoors, voxels bigger no problem

Thanks a lot for this, it is awesome! Has anyone tried compiling it for 4.14 yet? Any luck?