移动流程概述
拉起一个UAITask_MoveTo,设置好InMoveRequest后,调用UAITask_MoveTo::PerformMove
,内部转到AAIController::MoveTo
。
判断是否到达,如果没有到达,就转到AAIController::RequestMove
开始请求移动,内部创建一条路径后,传给UPathFollowingComponent::RequestMove
,由UPathFollowingComponent
完成一条路径的移动。
RequestMove
调用SetMoveSegment
处理出移动线段的信息。最后在Tick内通过这些线段完成移动。
void UGameplayTask::PerformActivation()
void UAITask_MoveTo::Activate()
void UAITask_MoveTo::ConditionalPerformMove()
void UAITask_MoveTo::PerformMove()
FPathFollowingRequestResult AAIController::MoveTo(const FAIMoveRequest& MoveRequest, FNavPathSharedPtr* OutPath)
FAIRequestID AAIController::RequestMove(const FAIMoveRequest& MoveRequest, FNavPathSharedPtr Path)
FAIRequestID UPathFollowingComponent::RequestMove(const FAIMoveRequest& RequestData, FNavPathSharedPtr InPath)
Path = InPath;
void UPathFollowingComponent::SetMoveSegment(int32 SegmentStartIndex)
void UPathFollowingComponent::UpdateMoveFocus()
FVector UPathFollowingComponent::GetMoveFocus(bool bAllowStrafe) const
void AAIController::SetFocalPoint(FVector NewFocus, EAIFocusPriority::Type InPriority)
在Tick内,调用FollowPathSegment,借助移动组件来完成Pawn的移动
void UPathFollowingComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
void UPathFollowingComponent::FollowPathSegment(float DeltaTime)
MovementComp->RequestPathMove(CurrentMoveInput);
MovementComp->RequestDirectMove(MoveVelocity, bNotFollowingLastSegment);
如果使用了UCrowdFollowingComponent,则调用链则是:
void UCrowdManager::Tick(float DeltaTime)
void UCrowdManager::ApplyVelocity(UCrowdFollowingComponent* AgentComponent, int32 AgentIndex) const
void UCrowdFollowingComponent::ApplyCrowdAgentVelocity(const FVector& NewVelocity, const FVector& DestPathCorner, bool bTraversingLink, bool bIsNearEndOfPath)
MovementComp->RequestPathMove(CurrentMoveInput);
MovementComp->RequestDirectMove(MoveVelocity, bNotFollowingLastSegment);
调整方向
在过程中,会持续调用UpdateMoveFocus
,进行任务朝向的设置
void UPathFollowingComponent::UpdateMoveFocus()
void AAIController::SetFocalPoint(FVector NewFocus, EAIFocusPriority::Type InPriority)
AAIController在Tick时进行旋转
void AAIController::Tick(float DeltaTime)
void AAIController::UpdateControlRotation(float DeltaTime, bool bUpdatePawn)
关于路径
初始路径在AAIController::MoveTo
内确定,调用了找路径函数:
FPathFollowingRequestResult AAIController::MoveTo(const FAIMoveRequest& MoveRequest, FNavPathSharedPtr* OutPath)
void AAIController::FindPathForMoveRequest(const FAIMoveRequest& MoveRequest, FPathFindingQuery& Query, FNavPathSharedPtr& OutPath) const
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(GetWorld());
if (NavSys)
{
FPathFindingResult PathResult = NavSys->FindPathSync(Query);
找到路径后调用AAIController::RequestMove
,转到UPathFollowingComponent::RequestMove
const FAIRequestID RequestID = Path.IsValid() ? RequestMove(MoveRequest, Path) : FAIRequestID::InvalidRequest;
PathFollowingComponent->RequestMove(MoveRequest, Path);
UPathFollowingComponent使用这个路径,完成实际移动逻辑的控制
UPathFollowingComponent
收到移动请求
FAIRequestID UPathFollowingComponent::RequestMove(const FAIMoveRequest& RequestData, FNavPathSharedPtr InPath)
// 那条线段可能开始减速
UpdateDecelerationData();
// 细节优化,前面两个点选哪个作为起点
const uint32 CurrentSegment = DetermineStartingPathPoint(InPath.Get());
// 设置线段信息为起点
SetMoveSegment(CurrentSegment);
设置线段信息
void UPathFollowingComponent::SetMoveSegment(int32 SegmentStartIndex)
// 获取起点和终点
const FVector SegmentStart = *PathInstance->GetPathPointLocation(MoveSegmentStartIndex);
FVector SegmentEnd = *CurrentDestination;
// 移动方向
MoveSegmentDirection = (SegmentEnd - SegmentStart).GetSafeNormal();
// 判断是否使用了NavLink
if (PathPt0.CustomLinkId)
{
UNavigationSystemV1* NavSys = FNavigationSystem::GetCurrent<UNavigationSystemV1>(GetWorld());
INavLinkCustomInterface* CustomNavLink = NavSys->GetCustomLink(PathPt0.CustomLinkId);
StartUsingCustomLink(CustomNavLink, SegmentEnd);
}
// 跟新移动焦点
UpdateMoveFocus();
跟新移动焦点
void UPathFollowingComponent::UpdateMoveFocus()
// 计算焦点
const FVector MoveFocus = GetMoveFocus(AIOwner->bAllowStrafe);
if (bAllowStrafe && DestinationActor.IsValid())
{
MoveFocus = DestinationActor->GetActorLocation();
}
else
{
const FVector CurrentMoveDirection = GetCurrentDirection();
// 会往前挪5米
MoveFocus = *CurrentDestination + (CurrentMoveDirection * FAIConfig::Navigation::FocalPointDistance);
}
// 设置到AIController上
AIOwner->SetFocalPoint(MoveFocus, EAIFocusPriority::Move);
更新路径
void UPathFollowingComponent::UpdatePathSegment()
//路径失效的处理
if (!Path->IsValid())
// 到达:碰撞
if (bCollidedWithGoal)
// 到达:到达检查可接受半径的线段
else if (MoveSegmentEndIndex > PreciseAcceptanceRadiusCheckStartNodeIndex && HasReachedDestination(CurrentLocation))
// 最后一段,且是MoveToGoal,直接走过去
else if (bFollowingLastSegment && bMoveToGoalOnLastSegment && bLastPathChunk)
CurrentDestination.Set(NULL, GoalLocation);
UpdateMoveFocus();
// 到达
else if (HasReachedCurrentTarget(CurrentLocation))
// 阻塞检测
const bool bHasNewSample = UpdateBlockDetection();
if (bHasNewSample && IsBlocked())
OnPathFinished(EPathFollowingResult::Blocked, FPathFollowingResultFlags::None);
执行移动
void UPathFollowingComponent::FollowPathSegment(float DeltaTime)
// 判断是否需要减速
const bool bAccelerationBased = MovementComp->UseAccelerationForPathFollowing();
if (bAccelerationBased)
if (MoveSegmentStartIndex >= DecelerationSegmentIndex)
if (bShouldDecelerate)
const float SpeedPct = FMath::Clamp(FMath::Sqrt(DistToEndSq) / CachedBrakingDistance, 0.0f, 1.0f);
CurrentMoveInput *= SpeedPct;
MovementComp->RequestPathMove(CurrentMoveInput);
// 直接移动
FVector MoveVelocity = (CurrentTarget - CurrentLocation) / DeltaTime;
PostProcessMove.ExecuteIfBound(this, MoveVelocity);
MovementComp->RequestDirectMove(MoveVelocity, bNotFollowingLastSegment);
观察目标
void AAIController::FindPathForMoveRequest(const FAIMoveRequest& MoveRequest, FPathFindingQuery& Query, FNavPathSharedPtr& OutPath) const
if (MoveRequest.IsMoveToActorRequest())
PathResult.Path->SetGoalActorObservation(*MoveRequest.GetGoalActor(), 100.0f);
NavigationDataUsed->RegisterObservedPath(AsShared());
NextObservedPathsTickInSeconds = ObservedPathsTickInterval;
ObservedPaths.Add(SharedPath);
void ANavigationData::TickActor(float DeltaTime, enum ELevelTick TickType, FActorTickFunction& ThisTickFunction)
EPathObservationResult::Type Result = Path->TickPathObservation();
const FVector GoalLocation = GoalActorAsNavAgent != NULL ? GoalActorAsNavAgent->GetNavAgentLocation() : GoalActor->GetActorLocation();
return FVector::DistSquared(GoalLocation, GoalActorLastLocation) <= GoalActorLocationTetherDistanceSq ? EPathObservationResult::NoChange : EPathObservationResult::RequestRepath;
TArray<FNavPathRecalculationRequest> WorkQueue(RepathRequests);
FNavPathRecalculationRequest& RecalcRequest = WorkQueue[Idx];
FNavPathSharedPtr PinnedPath = RecalcRequest.Path.Pin();
FPathFindingQuery Query(PinnedPath.ToSharedRef());
const FPathFindingResult Result = FindPath(Query.NavAgentProperties, Query.SetPathInstanceToUpdate(PinnedPath));