A Demo Project for the UnrealEngineSDK
Loading...
Searching...
No Matches
VRPathFollowingComponent.cpp
Go to the documentation of this file.
1// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
2
4//#include "Runtime/Engine/Private/EnginePrivate.h"
5
6//#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 13
7#include "Navigation/MetaNavMeshPath.h"
8#include "NavLinkCustomInterface.h"
9//#endif
10
11// Force to use new movement comp
12
13DEFINE_LOG_CATEGORY(LogPathFollowingVR);
14
15void UVRPathFollowingComponent::SetMovementComponent(UNavMovementComponent* MoveComp)
16{
17 Super::SetMovementComponent(MoveComp);
18
19 VRMovementComp = Cast<UVRBaseCharacterMovementComponent>(MovementComp);
20
22 {
24 }
25}
26
27bool UVRPathFollowingComponent::HasReached(const FVector& TestPoint, EPathFollowingReachMode ReachMode, float InAcceptanceRadius) const
28{
29 // simple test for stationary agent, used as early finish condition
30 const FVector CurrentLocation = MovementComp ? (VRMovementComp != nullptr ? VRMovementComp->GetActorFeetLocationVR() : MovementComp->GetActorFeetLocation()) : FVector::ZeroVector;
31 const float GoalRadius = 0.0f;
32 const float GoalHalfHeight = 0.0f;
33 if (InAcceptanceRadius == UPathFollowingComponent::DefaultAcceptanceRadius)
34 {
35 InAcceptanceRadius = MyDefaultAcceptanceRadius;
36 }
37
38 const float AgentRadiusMod = (ReachMode == EPathFollowingReachMode::ExactLocation) || (ReachMode == EPathFollowingReachMode::OverlapGoal) ? 0.0f : MinAgentRadiusPct;
39 return HasReachedInternal(TestPoint, GoalRadius, GoalHalfHeight, CurrentLocation, InAcceptanceRadius, AgentRadiusMod);
40}
41
42bool UVRPathFollowingComponent::HasReached(const AActor& TestGoal, EPathFollowingReachMode ReachMode, float InAcceptanceRadius, bool bUseNavAgentGoalLocation) const
43{
44 // simple test for stationary agent, used as early finish condition
45 float GoalRadius = 0.0f;
46 float GoalHalfHeight = 0.0f;
47 FVector GoalOffset = FVector::ZeroVector;
48 FVector TestPoint = TestGoal.GetActorLocation();
49 if (InAcceptanceRadius == UPathFollowingComponent::DefaultAcceptanceRadius)
50 {
51 InAcceptanceRadius = MyDefaultAcceptanceRadius;
52 }
53
54 if (bUseNavAgentGoalLocation)
55 {
56 const INavAgentInterface* NavAgent = Cast<const INavAgentInterface>(&TestGoal);
57 if (NavAgent)
58 {
59 const AActor* OwnerActor = GetOwner();
60 const FVector GoalMoveOffset = NavAgent->GetMoveGoalOffset(OwnerActor);
61 NavAgent->GetMoveGoalReachTest(OwnerActor, GoalMoveOffset, GoalOffset, GoalRadius, GoalHalfHeight);
62 TestPoint = FQuatRotationTranslationMatrix(TestGoal.GetActorQuat(), NavAgent->GetNavAgentLocation()).TransformPosition(GoalOffset);
63
64 if ((ReachMode == EPathFollowingReachMode::ExactLocation) || (ReachMode == EPathFollowingReachMode::OverlapAgent))
65 {
66 GoalRadius = 0.0f;
67 }
68 }
69 }
70
71 const FVector CurrentLocation = MovementComp ? (VRMovementComp != nullptr ? VRMovementComp->GetActorFeetLocationVR() : MovementComp->GetActorFeetLocation()) : FVector::ZeroVector;
72 const float AgentRadiusMod = (ReachMode == EPathFollowingReachMode::ExactLocation) || (ReachMode == EPathFollowingReachMode::OverlapGoal) ? 0.0f : MinAgentRadiusPct;
73 return HasReachedInternal(TestPoint, GoalRadius, GoalHalfHeight, CurrentLocation, InAcceptanceRadius, AgentRadiusMod);
74}
75
76void UVRPathFollowingComponent::GetDebugStringTokens(TArray<FString>& Tokens, TArray<EPathFollowingDebugTokens::Type>& Flags) const
77{
78 Tokens.Add(GetStatusDesc());
79 Flags.Add(EPathFollowingDebugTokens::Description);
80
81 if (Status != EPathFollowingStatus::Moving)
82 {
83 return;
84 }
85
86 FString& StatusDesc = Tokens[0];
87 if (Path.IsValid())
88 {
89 const int32 NumMoveSegments = (Path.IsValid() && Path->IsValid()) ? Path->GetPathPoints().Num() : -1;
90 const bool bIsDirect = (Path->CastPath<FAbstractNavigationPath>() != NULL);
91 const bool bIsCustomLink = CurrentCustomLinkOb.IsValid();
92
93 if (!bIsDirect)
94 {
95 StatusDesc += FString::Printf(TEXT(" (%d..%d/%d)%s"), MoveSegmentStartIndex + 1, MoveSegmentEndIndex + 1, NumMoveSegments,
96 bIsCustomLink ? TEXT(" (custom NavLink)") : TEXT(""));
97 }
98 else
99 {
100 StatusDesc += TEXT(" (direct)");
101 }
102 }
103 else
104 {
105 StatusDesc += TEXT(" (invalid path)");
106 }
107
108 // add debug params
109 float CurrentDot = 0.0f, CurrentDistance = 0.0f, CurrentHeight = 0.0f;
110 uint8 bFailedDot = 0, bFailedDistance = 0, bFailedHeight = 0;
111 DebugReachTest(CurrentDot, CurrentDistance, CurrentHeight, bFailedHeight, bFailedDistance, bFailedHeight);
112
113 Tokens.Add(TEXT("dot"));
114 Flags.Add(EPathFollowingDebugTokens::ParamName);
115 Tokens.Add(FString::Printf(TEXT("%.2f"), CurrentDot));
116 Flags.Add(bFailedDot ? EPathFollowingDebugTokens::FailedValue : EPathFollowingDebugTokens::PassedValue);
117
118 Tokens.Add(TEXT("dist2D"));
119 Flags.Add(EPathFollowingDebugTokens::ParamName);
120 Tokens.Add(FString::Printf(TEXT("%.0f"), CurrentDistance));
121 Flags.Add(bFailedDistance ? EPathFollowingDebugTokens::FailedValue : EPathFollowingDebugTokens::PassedValue);
122
123 Tokens.Add(TEXT("distZ"));
124 Flags.Add(EPathFollowingDebugTokens::ParamName);
125 Tokens.Add(FString::Printf(TEXT("%.0f"), CurrentHeight));
126 Flags.Add(bFailedHeight ? EPathFollowingDebugTokens::FailedValue : EPathFollowingDebugTokens::PassedValue);
127}
128
129void UVRPathFollowingComponent::PauseMove(FAIRequestID RequestID, EPathFollowingVelocityMode VelocityMode)
130{
131 //UE_VLOG(GetOwner(), LogPathFollowing, Log, TEXT("PauseMove: RequestID(%u)"), RequestID);
132 if (Status == EPathFollowingStatus::Paused)
133 {
134 return;
135 }
136
137 if (RequestID.IsEquivalent(GetCurrentRequestId()))
138 {
139 if ((VelocityMode == EPathFollowingVelocityMode::Reset) && MovementComp && HasMovementAuthority())
140 {
141 MovementComp->StopMovementKeepPathing();
142 }
143
144 LocationWhenPaused = MovementComp ? (VRMovementComp != nullptr ? VRMovementComp->GetActorFeetLocationVR() : MovementComp->GetActorFeetLocation()) : FVector::ZeroVector;
145 PathTimeWhenPaused = Path.IsValid() ? Path->GetTimeStamp() : 0.0f;
146 Status = EPathFollowingStatus::Paused;
147
148 UpdateMoveFocus();
149 }
150}
151
152
153
155{
156 bool bCheckPath = true;
157 if (MovementComp != NULL)
158 {
159 float AgentRadius = 0.0f, AgentHalfHeight = 0.0f;
160 MovementComp->GetOwner()->GetSimpleCollisionCylinder(AgentRadius, AgentHalfHeight);
161
162 const FVector CurrentLocation = (VRMovementComp != nullptr ? VRMovementComp->GetActorFeetLocation() : MovementComp->GetActorFeetLocation());
163 const float DeltaMove2DSq = (CurrentLocation - LocationWhenPaused).SizeSquared2D();
164 const float DeltaZ = FMath::Abs(CurrentLocation.Z - LocationWhenPaused.Z);
165 if (DeltaMove2DSq < FMath::Square(AgentRadius) && DeltaZ < (AgentHalfHeight * 0.5f))
166 {
167 bCheckPath = false;
168 }
169 }
170
171 return bCheckPath;
172}
173
174int32 UVRPathFollowingComponent::DetermineStartingPathPoint(const FNavigationPath* ConsideredPath) const
175{
176 int32 PickedPathPoint = INDEX_NONE;
177
178 if (ConsideredPath && ConsideredPath->IsValid())
179 {
180 // if we already have some info on where we were on previous path
181 // we can find out if there's a segment on new path we're currently at
182 if (MoveSegmentStartRef != INVALID_NAVNODEREF &&
183 MoveSegmentEndRef != INVALID_NAVNODEREF &&
184 ConsideredPath->GetNavigationDataUsed() != NULL)
185 {
186 // iterate every new path node and see if segment match
187 for (int32 PathPoint = 0; PathPoint < ConsideredPath->GetPathPoints().Num() - 1; ++PathPoint)
188 {
189 if (ConsideredPath->GetPathPoints()[PathPoint].NodeRef == MoveSegmentStartRef &&
190 ConsideredPath->GetPathPoints()[PathPoint + 1].NodeRef == MoveSegmentEndRef)
191 {
192 PickedPathPoint = PathPoint;
193 break;
194 }
195 }
196 }
197
198 if (MovementComp && PickedPathPoint == INDEX_NONE)
199 {
200 if (ConsideredPath->GetPathPoints().Num() > 2)
201 {
202 // check if is closer to first or second path point (don't assume AI's standing)
203 const FVector CurrentLocation = (VRMovementComp != nullptr ? VRMovementComp->GetActorFeetLocationVR() : MovementComp->GetActorFeetLocation());
204 const FVector PathPt0 = *ConsideredPath->GetPathPointLocation(0);
205 const FVector PathPt1 = *ConsideredPath->GetPathPointLocation(1);
206 // making this test in 2d to avoid situation where agent's Z location not being in "navmesh plane"
207 // would influence the result
208 const float SqDistToFirstPoint = (CurrentLocation - PathPt0).SizeSquared2D();
209 const float SqDistToSecondPoint = (CurrentLocation - PathPt1).SizeSquared2D();
210 PickedPathPoint = FMath::IsNearlyEqual(SqDistToFirstPoint, SqDistToSecondPoint) ?
211 ((FMath::Abs(CurrentLocation.Z - PathPt0.Z) < FMath::Abs(CurrentLocation.Z - PathPt1.Z)) ? 0 : 1) :
212 ((SqDistToFirstPoint < SqDistToSecondPoint) ? 0 : 1);
213 }
214 else
215 {
216 // If there are only two point we probably should start from the beginning
217 PickedPathPoint = 0;
218 }
219 }
220 }
221
222 return PickedPathPoint;
223}
224
226{
227 const float GameTime = GetWorld()->GetTimeSeconds();
228 if (bUseBlockDetection &&
229 MovementComp &&
230 GameTime > (LastSampleTime + BlockDetectionInterval) &&
231 BlockDetectionSampleCount > 0)
232 {
233 LastSampleTime = GameTime;
234
235 if (LocationSamples.Num() == NextSampleIdx)
236 {
237 LocationSamples.AddZeroed(1);
238 }
239
240 LocationSamples[NextSampleIdx] = (VRMovementComp != nullptr ? VRMovementComp->GetActorFeetLocationBased() : MovementComp->GetActorFeetLocationBased());
241 NextSampleIdx = (NextSampleIdx + 1) % BlockDetectionSampleCount;
242 return true;
243 }
244
245 return false;
246}
247
249{
250#if !UE_BUILD_SHIPPING
251 DEBUG_bMovingDirectlyToGoal = false;
252#endif // !UE_BUILD_SHIPPING
253
254 if ((Path.IsValid() == false) || (MovementComp == nullptr))
255 {
256 //UE_CVLOG(Path.IsValid() == false, this, LogPathFollowing, Log, TEXT("Aborting move due to not having a valid path object"));
257 OnPathFinished(EPathFollowingResult::Aborted, FPathFollowingResultFlags::InvalidPath);
258 return;
259 }
260
261 if (!Path->IsValid())
262 {
263 if (!Path->IsWaitingForRepath())
264 {
265 //UE_VLOG(this, LogPathFollowing, Log, TEXT("Aborting move due to path being invelid and not waiting for repath"));
266 OnPathFinished(EPathFollowingResult::Aborted, FPathFollowingResultFlags::InvalidPath);
267 }
268
269 return;
270 }
271
272 FMetaNavMeshPath* MetaNavPath = bIsUsingMetaPath ? Path->CastPath<FMetaNavMeshPath>() : nullptr;
273
274 // if agent has control over its movement, check finish conditions
275 const FVector CurrentLocation = (VRMovementComp != nullptr ? VRMovementComp->GetActorFeetLocationVR() : MovementComp->GetActorFeetLocation());
276 const bool bCanUpdateState = HasMovementAuthority();
277 if (bCanUpdateState && Status == EPathFollowingStatus::Moving)
278 {
279 const int32 LastSegmentEndIndex = Path->GetPathPoints().Num() - 1;
280 const bool bFollowingLastSegment = (MoveSegmentEndIndex >= LastSegmentEndIndex);
281 const bool bLastPathChunk = (MetaNavPath == nullptr || MetaNavPath->IsLastSection());
282
283 if (bCollidedWithGoal)
284 {
285 // check if collided with goal actor
286 OnSegmentFinished();
287 OnPathFinished(EPathFollowingResult::Success, FPathFollowingResultFlags::None);
288 }
289 else if (HasReachedDestination(CurrentLocation))
290 {
291 // always check for destination, acceptance radius may cause it to pass before reaching last segment
292 OnSegmentFinished();
293 OnPathFinished(EPathFollowingResult::Success, FPathFollowingResultFlags::None);
294 }
295 else if (bFollowingLastSegment && bMoveToGoalOnLastSegment && bLastPathChunk)
296 {
297 // use goal actor for end of last path segment
298 // UNLESS it's partial path (can't reach goal)
299 if (DestinationActor.IsValid() && Path->IsPartial() == false)
300 {
301 const FVector AgentLocation = DestinationAgent ? DestinationAgent->GetNavAgentLocation() : DestinationActor->GetActorLocation();
302 // note that the condition below requires GoalLocation to be in world space.
303 const FVector GoalLocation = FQuatRotationTranslationMatrix(DestinationActor->GetActorQuat(), AgentLocation).TransformPosition(MoveOffset);
304
305 CurrentDestination.Set(NULL, GoalLocation);
306
307 //UE_VLOG(this, LogPathFollowing, Log, TEXT("Moving directly to move goal rather than following last path segment"));
308 //UE_VLOG_LOCATION(this, LogPathFollowing, VeryVerbose, GoalLocation, 30, FColor::Green, TEXT("Last-segment-to-actor"));
309 //UE_VLOG_SEGMENT(this, LogPathFollowing, VeryVerbose, CurrentLocation, GoalLocation, FColor::Green, TEXT_EMPTY);
310 }
311
312 UpdateMoveFocus();
313
314#if !UE_BUILD_SHIPPING
315 DEBUG_bMovingDirectlyToGoal = true;
316#endif // !UE_BUILD_SHIPPING
317 }
318 // check if current move segment is finished
319 else if (HasReachedCurrentTarget(CurrentLocation))
320 {
321 OnSegmentFinished();
322 SetNextMoveSegment();
323 }
324 }
325
326 if (bCanUpdateState && Status == EPathFollowingStatus::Moving)
327 {
328 // check waypoint switch condition in meta paths
329 if (MetaNavPath && Status == EPathFollowingStatus::Moving)
330 {
331 MetaNavPath->ConditionalMoveToNextSection(CurrentLocation, EMetaPathUpdateReason::MoveTick);
332 }
333
334 // gather location samples to detect if moving agent is blocked
335 const bool bHasNewSample = UpdateBlockDetection();
336 if (bHasNewSample && IsBlocked())
337 {
338 if (Path->GetPathPoints().IsValidIndex(MoveSegmentEndIndex) && Path->GetPathPoints().IsValidIndex(MoveSegmentStartIndex))
339 {
340 //LogBlockHelper(GetOwner(), MovementComp, MinAgentRadiusPct, MinAgentHalfHeightPct,
341 //*Path->GetPathPointLocation(MoveSegmentStartIndex),
342 //*Path->GetPathPointLocation(MoveSegmentEndIndex));
343 }
344 else
345 {
346 if ((GetOwner() != NULL) && (MovementComp != NULL))
347 {
348 // UE_VLOG(GetOwner(), LogPathFollowing, Verbose, TEXT("Path blocked, but move segment indices are not valid: start %d, end %d of %d"), MoveSegmentStartIndex, MoveSegmentEndIndex, Path->GetPathPoints().Num());
349 }
350 }
351 OnPathFinished(EPathFollowingResult::Blocked, FPathFollowingResultFlags::None);
352 }
353 }
354}
355
357{
358 if (!Path.IsValid() || MovementComp == nullptr)
359 {
360 return;
361 }
362
363 const FVector CurrentLocation = (VRMovementComp != nullptr ? VRMovementComp->GetActorFeetLocationVR() : MovementComp->GetActorFeetLocation());
364 const FVector CurrentTarget = GetCurrentTargetLocation();
365
366 // set to false by default, we will set set this back to true if appropriate
367 bIsDecelerating = false;
368
369 const bool bAccelerationBased = MovementComp->UseAccelerationForPathFollowing();
370 if (bAccelerationBased)
371 {
372 CurrentMoveInput = (CurrentTarget - CurrentLocation).GetSafeNormal();
373
374 if (MoveSegmentStartIndex >= DecelerationSegmentIndex)
375 {
376 const FVector PathEnd = Path->GetEndLocation();
377 const float DistToEndSq = FVector::DistSquared(CurrentLocation, PathEnd);
378 const bool bShouldDecelerate = DistToEndSq < FMath::Square(CachedBrakingDistance);
379 if (bShouldDecelerate)
380 {
381 bIsDecelerating = true;
382
383 const float SpeedPct = FMath::Clamp(FMath::Sqrt(DistToEndSq) / CachedBrakingDistance, 0.0f, 1.0f);
384 CurrentMoveInput *= SpeedPct;
385 }
386 }
387
388 PostProcessMove.ExecuteIfBound(this, CurrentMoveInput);
389 MovementComp->RequestPathMove(CurrentMoveInput);
390 }
391 else
392 {
393 FVector MoveVelocity = (CurrentTarget - CurrentLocation) / DeltaTime;
394
395 const int32 LastSegmentStartIndex = Path->GetPathPoints().Num() - 2;
396 const bool bNotFollowingLastSegment = (MoveSegmentStartIndex < LastSegmentStartIndex);
397
398 PostProcessMove.ExecuteIfBound(this, MoveVelocity);
399 MovementComp->RequestDirectMove(MoveVelocity, bNotFollowingLastSegment);
400 }
401}
402
403bool UVRPathFollowingComponent::HasReachedCurrentTarget(const FVector& CurrentLocation) const
404{
405 if (MovementComp == NULL)
406 {
407 return false;
408 }
409
410 const FVector CurrentTarget = GetCurrentTargetLocation();
411 const FVector CurrentDirection = GetCurrentDirection();
412
413 // check if moved too far
414 const FVector ToTarget = (CurrentTarget - (VRMovementComp != nullptr ? VRMovementComp->GetActorFeetLocationVR() : MovementComp->GetActorFeetLocation()));
415 const float SegmentDot = FVector::DotProduct(ToTarget, CurrentDirection);
416 if (SegmentDot < 0.0)
417 {
418 return true;
419 }
420
421 // or standing at target position
422 // don't use acceptance radius here, it has to be exact for moving near corners (2D test < 5% of agent radius)
423 const float GoalRadius = 0.0f;
424 const float GoalHalfHeight = 0.0f;
425
426 return HasReachedInternal(CurrentTarget, GoalRadius, GoalHalfHeight, CurrentLocation, CurrentAcceptanceRadius, 0.05f);
427}
428
429void UVRPathFollowingComponent::DebugReachTest(float& CurrentDot, float& CurrentDistance, float& CurrentHeight, uint8& bDotFailed, uint8& bDistanceFailed, uint8& bHeightFailed) const
430{
431 if (!Path.IsValid() || MovementComp == NULL)
432 {
433 return;
434 }
435
436 const int32 LastSegmentEndIndex = Path->GetPathPoints().Num() - 1;
437 const bool bFollowingLastSegment = (MoveSegmentEndIndex >= LastSegmentEndIndex);
438
439 float GoalRadius = 0.0f;
440 float GoalHalfHeight = 0.0f;
441 float RadiusThreshold = 0.0f;
442 float AgentRadiusPct = 0.05f;
443
444 FVector AgentLocation = (VRMovementComp != nullptr ? VRMovementComp->GetActorFeetLocationVR() : MovementComp->GetActorFeetLocation());
445 FVector GoalLocation = GetCurrentTargetLocation();
446 RadiusThreshold = CurrentAcceptanceRadius;
447
448 if (bFollowingLastSegment)
449 {
450 GoalLocation = *Path->GetPathPointLocation(Path->GetPathPoints().Num() - 1);
451 AgentRadiusPct = MinAgentRadiusPct;
452
453 // take goal's current location, unless path is partial or last segment doesn't reach goal actor (used by tethered AI)
454 if (DestinationActor.IsValid() && !Path->IsPartial() && bMoveToGoalOnLastSegment)
455 {
456 if (DestinationAgent)
457 {
458 FVector GoalOffset;
459
460 const AActor* OwnerActor = GetOwner();
461 DestinationAgent->GetMoveGoalReachTest(OwnerActor, MoveOffset, GoalOffset, GoalRadius, GoalHalfHeight);
462 GoalLocation = FQuatRotationTranslationMatrix(DestinationActor->GetActorQuat(), DestinationAgent->GetNavAgentLocation()).TransformPosition(GoalOffset);
463 }
464 else
465 {
466 GoalLocation = DestinationActor->GetActorLocation();
467 }
468 }
469 }
470
471 const FVector ToGoal = (GoalLocation - AgentLocation);
472 const FVector CurrentDirection = GetCurrentDirection();
473 CurrentDot = FVector::DotProduct(ToGoal.GetSafeNormal(), CurrentDirection);
474 bDotFailed = (CurrentDot < 0.0f) ? 1 : 0;
475
476 // get cylinder of moving agent
477 float AgentRadius = 0.0f;
478 float AgentHalfHeight = 0.0f;
479 AActor* MovingAgent = MovementComp->GetOwner();
480 MovingAgent->GetSimpleCollisionCylinder(AgentRadius, AgentHalfHeight);
481
482 CurrentDistance = ToGoal.Size2D();
483 const float UseRadius = FMath::Max(RadiusThreshold, GoalRadius + (AgentRadius * AgentRadiusPct));
484 bDistanceFailed = (CurrentDistance > UseRadius) ? 1 : 0;
485
486 CurrentHeight = FMath::Abs(ToGoal.Z);
487 const float UseHeight = GoalHalfHeight + (AgentHalfHeight * MinAgentHalfHeightPct);
488 bHeightFailed = (CurrentHeight > UseHeight) ? 1 : 0;
489}
DEFINE_LOG_CATEGORY(LogPathFollowingVR)
virtual void OnMoveCompleted(FAIRequestID RequestID, const FPathFollowingResult &Result)
void FollowPathSegment(float DeltaTime) override
int32 DetermineStartingPathPoint(const FNavigationPath *ConsideredPath) const override
bool ShouldCheckPathOnResume() const override
UVRBaseCharacterMovementComponent * VRMovementComp
UPROPERTY(transient)
void PauseMove(FAIRequestID RequestID=FAIRequestID::CurrentRequest, EPathFollowingVelocityMode VelocityMode=EPathFollowingVelocityMode::Reset) override
void DebugReachTest(float &CurrentDot, float &CurrentDistance, float &CurrentHeight, uint8 &bDotFailed, uint8 &bDistanceFailed, uint8 &bHeightFailed) const
bool HasReachedCurrentTarget(const FVector &CurrentLocation) const
bool HasReached(const FVector &TestPoint, EPathFollowingReachMode ReachMode, float AcceptanceRadius=UPathFollowingComponent::DefaultAcceptanceRadius) const
void SetMovementComponent(UNavMovementComponent *MoveComp) override
virtual void GetDebugStringTokens(TArray< FString > &Tokens, TArray< EPathFollowingDebugTokens::Type > &Flags) const override