AI Navigation in C++, Customize Path Following Every Tick
Contents
- 1 Overview
- 2 Rama C++ AI Jumping Video
- 3 Rama Video ~ Training PhysX Simulating Balls to Navigate Narrow Ledges!
- 4 Steps
- 4.1 Have your own Custom ai controller (extends AAIController)
- 4.2 Tell ai controller to use custom path following component, which extends the base UE4 class
- 4.3 Make sure your build.cs is including AIModule as part of your dependencies
- 4.4 Override these functions in your custom PathFollowingComponent!
- 5 Video Result
- 6 More of Ny Customized C++ AI Pathing Videos
- 7 How to Get All UE4 Navigation Polys
- 8 Conclusion
Overview
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</youtube>
More Rama AI Jumping Videos
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</youtube>
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</youtube>
More of Ny Customized C++ AI Pathing Videos
Rama's C++ Jump Pathing Videos
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!