UE4:AI‘s MoveTo——代码分析

20 篇文章 13 订阅

移动流程概述

拉起一个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));
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值