void AAIController::FindPathForMoveRequest(const FAIMoveRequest& MoveRequest, FPathFindingQuery& Query, FNavPathSharedPtr& OutPath) const { SCOPE_CYCLE_COUNTER(STAT_AI_Overall); UNavigationSystem* NavSys = UNavigationSystem::GetCurrent(GetWorld()); if (NavSys) { FPathFindingResult PathResult = NavSys->FindPathSync(Query); if (PathResult.Result != ENavigationQueryResult::Error) { if (PathResult.IsSuccessful() && PathResult.Path.IsValid()) { if (MoveRequest.IsMoveToActorRequest()) { PathResult.Path->SetGoalActorObservation(*MoveRequest.GetGoalActor(), 100.0f); } PathResult.Path->EnableRecalculationOnInvalidation(true); OutPath = PathResult.Path; } } else { UE_VLOG(this, LogAINavigation, Error, TEXT("Trying to find path to %s resulted in Error") , MoveRequest.IsMoveToActorRequest() ? *GetNameSafe(MoveRequest.GetGoalActor()) : *MoveRequest.GetGoalLocation().ToString()); UE_VLOG_SEGMENT(this, LogAINavigation, Error, GetPawn() ? GetPawn()->GetActorLocation() : FAISystem::InvalidLocation , MoveRequest.GetGoalLocation(), FColor::Red, TEXT("Failed move to %s"), *GetNameSafe(MoveRequest.GetGoalActor())); } } }
bool AAIController::BuildPathfindingQuery(const FAIMoveRequest& MoveRequest, FPathFindingQuery& Query) const { bool bResult = false; UNavigationSystem* NavSys = UNavigationSystem::GetCurrent(GetWorld()); const ANavigationData* NavData = (NavSys == nullptr) ? nullptr : MoveRequest.IsUsingPathfinding() ? NavSys->GetNavDataForProps(GetNavAgentPropertiesRef()) : NavSys->GetAbstractNavData(); if (NavData) { FVector GoalLocation = MoveRequest.GetGoalLocation(); if (MoveRequest.IsMoveToActorRequest()) { const INavAgentInterface* NavGoal = Cast<const INavAgentInterface>(MoveRequest.GetGoalActor()); if (NavGoal) { const FVector Offset = NavGoal->GetMoveGoalOffset(this); GoalLocation = FQuatRotationTranslationMatrix(MoveRequest.GetGoalActor()->GetActorQuat(), NavGoal->GetNavAgentLocation()).TransformPosition(Offset); } else { GoalLocation = MoveRequest.GetGoalActor()->GetActorLocation(); } } FSharedConstNavQueryFilter NavFilter = UNavigationQueryFilter::GetQueryFilter(*NavData, this, MoveRequest.GetNavigationFilter()); Query = FPathFindingQuery(*this, *NavData, GetNavAgentLocation(), GoalLocation, NavFilter); Query.SetAllowPartialPaths(MoveRequest.IsUsingPartialPaths()); if (PathFollowingComponent) { PathFollowingComponent->OnPathfindingQuery(Query); } bResult = true; } else { UE_VLOG(this, LogAINavigation, Warning, TEXT("Unable to find NavigationData instance while calling AAIController::BuildPathfindingQuery")); } return bResult; }
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; }