How can I edit the path that tells an AI character where to move?

I’m currently trying to create a custom Path Following Component for my AI character so that I can edit the path that it tells the character to go. The reason I am doing this is because I want to create a smoother path for the character (when changing directions and turning around corners). I am thinking of adding a few more points around the target location to create a curved path, but I’m not really sure where I can override the path points in the path following component. This is all done in C++.

If there is another way to solve this problem (smoothing out turns for AI characters) I would be willing to try something else as well.

I can suggest deriving a class from PathFollowingComponent and overriding RequestMove and UpdateMove. In those functions you can post-process new path before passing it over to the super call.

Let me know if you run into issues while trying this out.

Cheers,

–mieszko

1 Like

#More Info and a Wiki

To expand on Mieszko’s (Hi Miezsko!) answer,

I have a wiki with code samples on the subject of custom UE4 AI pathing here!

Wiki on Custom AI Pathing in UE4

Rama

Hey guys, thanks for the reply! I’ve put this task off for a bit but now I’m back at it.

I’ve attempted to make some changes in SetMoveSegment() and FollowPathSegment(), and I’ve got my path following component initialized in my custom ai controller (I think):

ASKIMEGController::ASKIMEGController(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer.SetDefaultSubobjectClass<USKIMEGPathFollowingComponent>(TEXT("MEGPathFollowingComponent")))

I ran the game in the editor and everything worked as if I never changed anything. Then I ran it in debugging editor mode, and placed breakpoints in those two overridden functions. After running my AI in the game for a few minutes the breakpoints were never triggered, meaning my path following component isn’t being run in the first place. Am I initializing it incorrectly?

The name passed into SetDefaultSubobjectClass should match up with the name that the parent class (in this case AAIController) uses when creating the component. It’s a bid fiddly, as far as I know currently you just have to check the engine source code to find out. So anyway, you need to change it to:

ObjectInitializer.SetDefaultSubobjectClass<USKIMEGPathFollowingComponent>(TEXT("PathFollowingComponent"))

So I’ve been tinkering around with RequestMove and UpdateMove, or well HandlePathUpdateEvent now actually. I’m not quite sure how to add points to the InPath though… I’ve been looking at FMetaNavMeshPath and creating one and passing it as the InPath to my Super. Is this the right, or even a valid way to go about it? The description of FMetaNavMeshPath sounds like what i need, since it talks about guided paths with past set waypoints

To answer my own question a bit. Yes this works, once i finally managed to pass FMetaNavMeshPath as a FNavPathSharedPtr which i did by first setting it up as a TSharedPtr like so:

TSharedPtr<FMetaNavMeshPath, ESPMode::ThreadSafe> MetaPathPtr(new FMetaNavMeshPath(Waypoints, *Controller));

…bla bla bla code here bla bla…

InPath = FNavPathSharedPtr(MetaPathPtr);

Maybe there’s an easier way to go about it? but FNavPathSharedPtr doesn’t seem to have a suitable constructor for FMetaNavMeshPath

1 Like

Hey, just a question, which FMetaNavMeshPath constructor are you using? Because i’m trying to use the “TArray, AController” version but compiler still throw me the following error:

error C2664: 'FMetaNavMeshPath::FMetaNavMeshPath(FMetaNavMeshPath &&)': cannot convert argument 1 from 'TArray<FVector,FDefaultAllocator>' to 'const TArray<FMetaPathWayPoint,FDefaultAllocator> &'

looks like he want to use only this constructor

FMetaNavMeshPath(const TArray<FMetaPathWayPoint>& InWaypoints, const ANavigationData& NavData);

instead of

FMetaNavMeshPath(const TArray& InWaypoints, const AController& Owner);

any idea?

Snippet of my testing code here:

TArray<FVector> Locations;
Locations.Add(FVector(250.f, 250.f, 0.f));
Locations.Add(FVector(450.f, -250.f, 0.f));
Locations.Add(FVector(650.f, 250.f, 0.f));
	
AController* Controller = Cast<AController>(GetOwner());
FMetaNavMeshPath *MetaNavMeshPath = new FMetaNavMeshPath(Locations, Controller);

TSharedPtr<FMetaNavMeshPath, ESPMode::ThreadSafe> MetaPathPtr(MetaNavMeshPath);

InPath = FNavPathSharedPtr(MetaPathPtr);

You need to dereference your pointer. It’s not common in the engine, but some of the AI code has (sensibly) started to use references instead of pointers to objects in function parameters.
FMetaNavMeshPath(Locations, *Controller);

Holy cow! That was simple! Thanks a lot :slight_smile: