AI Navigation in C++, Customize Path Following Every Tick

From Epic Wiki

Overview

RamaAIJump.gif

Original Author: ( )

Dear Community, As gif above shows there is a way to modify standard UE4 pathing via C++ to introduce customized path following behavior without rewriting the whole system!

In gif above I am telling the unit how to jump from one nav mesh piece to another using only C++ (nothing was placed in the editor other than basic nav mesh, and I can add new level geometry any time)

I am also drawing each UE4 Nav Area that the unit is passing through, and I use Nav Mesh Traces + the area info to identify how the unit should jump, and at what angle, to path over terrain that requires jumping.

The point of this wiki is that my method involves only C++ code and a custom Path Following Component.

I am adding to the stock UE4 code, keeping its benefits, while introducing my own custom path following mechanics.

Rama C++ AI Jumping Video

I used the code structure I am describing in this wiki to train my AI to do jump pathing calculations using just UE4 C++!

<youtube> https://www.youtube.com/watch?v=sMMSQdnyt6o

More Rama AI Jumping Videos

https://forums.unrealengine.com/showthread.php?25410-Rama-s-Multi-Threaded-Dynamic-Pathing-System-Full-Physics-Support&p=251216&viewfull=1#post251216

Rama Video ~ Training PhysX Simulating Balls to Navigate Narrow Ledges!

In this video you can see that I use a custom path following component ( as explained in this wiki ) to enable PhysX simulating characters to navigate narrow ledges!

<youtube> https://www.youtube.com/watch?v=d7mAhStMABo

Steps

You need to do several things

Have your own Custom ai controller (extends AAIController)

Here's the required header info

#pragma once

//Super
#include "AIController.h"

#include "JoyController.generated.h"


UCLASS() 
class AJoyController : public AAIController
{ 
    GENERATED_BODY()
public:
    AJoyController(const FObjectInitializer& ObjectInitializer);

Tell ai controller to use custom path following component, which extends the base UE4 class

Here's the code for that, you just modify the constructor

//		UJoyPathFollowComp is used here!
AJoyController::AJoyController(const FObjectInitializer& ObjectInitializer)
   : Super(ObjectInitializer.SetDefaultSubobjectClass<UJoyPathFollowComp>(TEXT("PathFollowingComponent")))
{

Make sure your build.cs is including AIModule as part of your dependencies

PublicDependencyModuleNames.AddRange(new string[] { 
	"Core", 
	"CoreUObject", 
	"Engine", 
	"InputCore" ,
        "Landscape",
       "UMG",

        "PhysX",  "APEX",

	"AIModule"      //<~~~~~~
});

Override these functions in your custom PathFollowingComponent!

Especially FollowPathSegment !

This is where you can take control of exact pathing methods!


//~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~
//	  Per Tick Modification of Path Following
//			this is how you really customize 
//				how units follow the path! 

/** follow current path segment */
virtual void FollowPathSegment(float DeltaTime) override;

/** sets variables related to current move segment */
virtual void SetMoveSegment(int32 SegmentStartIndex) override;
	
/** check state of path following, update move segment if needed */
virtual void UpdatePathSegment() override;

Video Result

Using the above method is how I made custom C++ Jump pathing calculations that dont need any in-editor additions to the level, just the basic nav mesh volume.

<youtube> https://www.youtube.com/watch?v=0GX-1x-wACI&feature=youtu.be

More of Ny Customized C++ AI Pathing Videos

Rama's C++ Jump Pathing Videos

How to Get All UE4 Navigation Polys

In the video above you will see I get all the UE4 Nav Polys in order to do all my C++ AI Jump calculations!

Here's the function I wrote to do this, just for you!

//Nav Data Main
	FORCEINLINE const ANavigationData* GetMainNavData(FNavigationSystem::ECreateIfEmpty CreateNewIfNoneFound)
	{
		UNavigationSystem* NavSys = GetWorld()->GetNavigationSystem();
		if(!NavSys) return NULL; 
		return NavSys->GetMainNavData(CreateNewIfNoneFound);
	}

	//Choose Which Nav Data To Use
	FORCEINLINE const ANavigationData* JoyGetNavData() const
	{
		const FNavAgentProperties& AgentProperties = MovementComp->GetNavAgentPropertiesRef() ;
		const ANavigationData* NavData = GetNavDataForProps(AgentProperties) ;
		if (NavData == NULL)
		{
			VSCREENMSG("ERROR USING MAIN NAV DATA"); 
			NavData = GetMainNavData();
		}
		   
		return NavData;
	}

//VERY IMPORTANT FOR CRASH PROTECTION !!!!!
FORCEINLINE bool TileIsValid(const ARecastNavMesh* NavMesh,int32 TileIndex) const
{
	if(!NavMesh) return false;
	//~~~~~~~~~~~~~~
	const FBox TileBounds = NavMesh->GetNavMeshTileBounds(TileIndex);
	
	return TileBounds.IsValid != 0;
}

//add this to your custom path follow component!
bool NavPoly_GetAllPolys(TArray<NavNodeRef>& Polys);
//Rama's UE4 Nav code to get all the nav polys!
bool UJoyPathFollowComp::NavPoly_GetAllPolys(TArray<NavNodeRef>& Polys)
{
	if(!MovementComp) return false;
	//~~~~~~~~~~~~~~~~~~
	
	//Get Nav Data
	const ANavigationData* NavData = JoyGetNavData();
	 
	const ARecastNavMesh* NavMesh = Cast<ARecastNavMesh>(NavData);
	if(!NavMesh)
	{
		return false;
	}
	
	TArray<FNavPoly> EachPolys;
	for(int32 v = 0; v < NavMesh->GetNavMeshTilesCount(); v++)
	{
		
                //CHECK IS VALID FIRST OR WILL CRASH!!! 
               //     256 entries but only few are valid!
                // use continue in case the valid polys are not stored sequentially
		if(!TileIsValid(NavMesh,v)) 
                {
                    continue;
		}	
	
		NavMesh->GetPolysInTile(v,EachPolys);
	}	
	  
	
	//Add them all!
	for(int32 v = 0; v < EachPolys.Num(); v++)
	{
		Polys.Add(EachPolys[v].Ref);
	}
}

Conclusion

Have fun writing your own custom Path Following code that integrates with the UE4 standard Path Following code!

Now you can extend the wonderful foundation that the AI engineers at Epic built for you!

Enjoy!

( )