FPathFollowingRequestResult AAIController::MoveTo(const FAIMoveRequest& MoveRequest, FNavPathSharedPtr* OutPath) { // both MoveToActor and MoveToLocation can be called from blueprints/script and should keep only single movement request at the same time. // this function is entry point of all movement mechanics - do NOT abort in here, since movement may be handled by AITasks, which support stacking SCOPE_CYCLE_COUNTER(STAT_MoveTo); UE_VLOG(this, LogAINavigation, Log, TEXT("MoveTo: %s"), *MoveRequest.ToString()); FPathFollowingRequestResult ResultData; ResultData.Code = EPathFollowingRequestResult::Failed; if (MoveRequest.IsValid() == false) { UE_VLOG(this, LogAINavigation, Error, TEXT("MoveTo request failed due MoveRequest not being valid. Most probably desireg Goal Actor not longer exists"), *MoveRequest.ToString()); return ResultData; } if (PathFollowingComponent == nullptr) { UE_VLOG(this, LogAINavigation, Error, TEXT("MoveTo request failed due missing PathFollowingComponent")); return ResultData; } ensure(MoveRequest.GetNavigationFilter() || !DefaultNavigationFilterClass); bool bCanRequestMove = true; bool bAlreadyAtGoal = false; if (!MoveRequest.IsMoveToActorRequest()) { if (MoveRequest.GetGoalLocation().ContainsNaN() || FAISystem::IsValidLocation(MoveRequest.GetGoalLocation()) == false) { UE_VLOG(this, LogAINavigation, Error, TEXT("AAIController::MoveTo: Destination is not valid! Goal(%s)"), TEXT_AI_LOCATION(MoveRequest.GetGoalLocation())); bCanRequestMove = false; } // fail if projection to navigation is required but it failed if (bCanRequestMove && MoveRequest.IsProjectingGoal()) { UNavigationSystem* NavSys = UNavigationSystem::GetCurrent(GetWorld()); const FNavAgentProperties& AgentProps = GetNavAgentPropertiesRef(); FNavLocation ProjectedLocation; if (NavSys && !NavSys->ProjectPointToNavigation(MoveRequest.GetGoalLocation(), ProjectedLocation, INVALID_NAVEXTENT, &AgentProps)) { UE_VLOG_LOCATION(this, LogAINavigation, Error, MoveRequest.GetGoalLocation(), 30.f, FColor::Red, TEXT("AAIController::MoveTo failed to project destination location to navmesh")); bCanRequestMove = false; } MoveRequest.UpdateGoalLocation(ProjectedLocation.Location); } bAlreadyAtGoal = bCanRequestMove && PathFollowingComponent->HasReached(MoveRequest); } else { bAlreadyAtGoal = bCanRequestMove && PathFollowingComponent->HasReached(MoveRequest); } if (bAlreadyAtGoal) { UE_VLOG(this, LogAINavigation, Log, TEXT("MoveTo: already at goal!")); ResultData.MoveId = PathFollowingComponent->RequestMoveWithImmediateFinish(EPathFollowingResult::Success); ResultData.Code = EPathFollowingRequestResult::AlreadyAtGoal; } else if (bCanRequestMove) { FPathFindingQuery PFQuery; const bool bValidQuery = BuildPathfindingQuery(MoveRequest, PFQuery); if (bValidQuery) { FNavPathSharedPtr Path; FindPathForMoveRequest(MoveRequest, PFQuery, Path); const FAIRequestID RequestID = Path.IsValid() ? RequestMove(MoveRequest, Path) : FAIRequestID::InvalidRequest; if (RequestID.IsValid()) { bAllowStrafe = MoveRequest.CanStrafe(); ResultData.MoveId = RequestID; ResultData.Code = EPathFollowingRequestResult::RequestSuccessful; if (OutPath) { *OutPath = Path; } } } } if (ResultData.Code == EPathFollowingRequestResult::Failed) { ResultData.MoveId = PathFollowingComponent->RequestMoveWithImmediateFinish(EPathFollowingResult::Invalid); } return ResultData; }
EPathFollowingRequestResult::Type AAIController::MoveTo(const FAIMoveRequest& MoveRequest) { SCOPE_CYCLE_COUNTER(STAT_MoveTo); UE_VLOG(this, LogAINavigation, Log, TEXT("MoveTo: %s"), *MoveRequest.ToString()); EPathFollowingRequestResult::Type Result = EPathFollowingRequestResult::Failed; bool bCanRequestMove = true; bool bAlreadyAtGoal = false; if (!MoveRequest.HasGoalActor()) { if (MoveRequest.GetGoalLocation().ContainsNaN() || FAISystem::IsValidLocation(MoveRequest.GetGoalLocation()) == false) { UE_VLOG(this, LogAINavigation, Error, TEXT("AAIController::MoveTo: Destination is not valid! Goal(%s)"), TEXT_AI_LOCATION(MoveRequest.GetGoalLocation())); bCanRequestMove = false; } // fail if projection to navigation is required but it failed if (bCanRequestMove && MoveRequest.IsProjectingGoal()) { UNavigationSystem* NavSys = UNavigationSystem::GetCurrent(GetWorld()); const FNavAgentProperties& AgentProps = GetNavAgentPropertiesRef(); FNavLocation ProjectedLocation; if (NavSys && !NavSys->ProjectPointToNavigation(MoveRequest.GetGoalLocation(), ProjectedLocation, AgentProps.GetExtent(), &AgentProps)) { UE_VLOG_LOCATION(this, LogAINavigation, Error, MoveRequest.GetGoalLocation(), 30.f, FLinearColor::Red, TEXT("AAIController::MoveTo failed to project destination location to navmesh")); bCanRequestMove = false; } MoveRequest.UpdateGoalLocation(ProjectedLocation.Location); } bAlreadyAtGoal = bCanRequestMove && PathFollowingComponent && PathFollowingComponent->HasReached(MoveRequest.GetGoalLocation(), MoveRequest.GetAcceptanceRadius(), !MoveRequest.CanStopOnOverlap()); } else { bAlreadyAtGoal = bCanRequestMove && PathFollowingComponent && PathFollowingComponent->HasReached(*MoveRequest.GetGoalActor(), MoveRequest.GetAcceptanceRadius(), !MoveRequest.CanStopOnOverlap()); } if (bAlreadyAtGoal) { UE_VLOG(this, LogAINavigation, Log, TEXT("MoveToActor: already at goal!")); // make sure previous move request gets aborted PathFollowingComponent->AbortMove(TEXT("Aborting move due to new move request finishing with AlreadyAtGoal"), FAIRequestID::AnyRequest); PathFollowingComponent->SetLastMoveAtGoal(true); OnMoveCompleted(FAIRequestID::CurrentRequest, EPathFollowingResult::Success); Result = EPathFollowingRequestResult::AlreadyAtGoal; } else if (bCanRequestMove) { FPathFindingQuery Query; const bool bValidQuery = PreparePathfinding(MoveRequest, Query); const FAIRequestID RequestID = bValidQuery ? RequestPathAndMove(MoveRequest, Query) : FAIRequestID::InvalidRequest; if (RequestID.IsValid()) { bAllowStrafe = MoveRequest.CanStrafe(); Result = EPathFollowingRequestResult::RequestSuccessful; } } if (Result == EPathFollowingRequestResult::Failed) { if (PathFollowingComponent) { PathFollowingComponent->SetLastMoveAtGoal(false); } OnMoveCompleted(FAIRequestID::InvalidRequest, EPathFollowingResult::Invalid); } return Result; }
EPathFollowingRequestResult::Type AAIController::MoveToLocation(const FVector& Dest, float AcceptanceRadius, bool bStopOnOverlap, bool bUsePathfinding, bool bProjectDestinationToNavigation, bool bCanStrafe, TSubclassOf<UNavigationQueryFilter> FilterClass) { SCOPE_CYCLE_COUNTER(STAT_MoveToLocation); EPathFollowingRequestResult::Type Result = EPathFollowingRequestResult::Failed; bool bCanRequestMove = true; UE_VLOG(this, LogAINavigation, Log, TEXT("MoveToLocation: Goal(%s) AcceptRadius(%.1f%s) bUsePathfinding(%d) bCanStrafe(%d) Filter(%s)") , TEXT_AI_LOCATION(Dest), AcceptanceRadius, bStopOnOverlap ? TEXT(" + agent") : TEXT(""), bUsePathfinding, bCanStrafe, *GetNameSafe(FilterClass)); // Check input is valid if (Dest.ContainsNaN()) { UE_VLOG(this, LogAINavigation, Error, TEXT("AAIController::MoveToLocation: Destination is not valid! Goal(%s) AcceptRadius(%.1f%s) bUsePathfinding(%d) bCanStrafe(%d)") , TEXT_AI_LOCATION(Dest), AcceptanceRadius, bStopOnOverlap ? TEXT(" + agent") : TEXT(""), bUsePathfinding, bCanStrafe); ensure(!Dest.ContainsNaN()); bCanRequestMove = false; } FVector GoalLocation = Dest; // fail if projection to navigation is required but it failed if (bCanRequestMove && bProjectDestinationToNavigation) { UNavigationSystem* NavSys = UNavigationSystem::GetCurrent(GetWorld()); const FNavAgentProperties& AgentProps = GetNavAgentPropertiesRef(); FNavLocation ProjectedLocation; if (NavSys && !NavSys->ProjectPointToNavigation(Dest, ProjectedLocation, AgentProps.GetExtent(), &AgentProps)) { UE_VLOG_LOCATION(this, LogAINavigation, Error, Dest, 30.f, FLinearColor::Red, TEXT("AAIController::MoveToLocation failed to project destination location to navmesh")); bCanRequestMove = false; } GoalLocation = ProjectedLocation.Location; } if (bCanRequestMove && PathFollowingComponent && PathFollowingComponent->HasReached(GoalLocation, AcceptanceRadius, !bStopOnOverlap)) { UE_VLOG(this, LogAINavigation, Log, TEXT("MoveToLocation: already at goal!")); // make sure previous move request gets aborted PathFollowingComponent->AbortMove(TEXT("Aborting move due to new move request finishing with AlreadyAtGoal"), FAIRequestID::AnyRequest); PathFollowingComponent->SetLastMoveAtGoal(true); OnMoveCompleted(FAIRequestID::CurrentRequest, EPathFollowingResult::Success); Result = EPathFollowingRequestResult::AlreadyAtGoal; bCanRequestMove = false; } if (bCanRequestMove) { FPathFindingQuery Query; const bool bValidQuery = PreparePathfinding(Query, GoalLocation, NULL, bUsePathfinding, FilterClass); const FAIRequestID RequestID = bValidQuery ? RequestPathAndMove(Query, NULL, AcceptanceRadius, bStopOnOverlap, NULL) : FAIRequestID::InvalidRequest; if (RequestID.IsValid()) { bAllowStrafe = bCanStrafe; Result = EPathFollowingRequestResult::RequestSuccessful; } } if (Result == EPathFollowingRequestResult::Failed) { if (PathFollowingComponent) { PathFollowingComponent->SetLastMoveAtGoal(false); } OnMoveCompleted(FAIRequestID::InvalidRequest, EPathFollowingResult::Invalid); } return Result; }