Created
February 25, 2023 12:32
-
-
Save olddeda/e2d816c316f664928b934f30498741ff to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include "AsyncMoveTo.h" | |
| #include "AIController.h" | |
| #include "AISystem.h" | |
| #include "NavigationSystem.h" | |
| #include "Logging/MessageLog.h" | |
| #include UE_INLINE_GENERATED_CPP_BY_NAME(AsyncMoveTo) | |
| DEFINE_LOG_CATEGORY_STATIC(AsyncMoveTo, Warning, All); | |
| #define LOCTEXT_NAMESPACE "AsyncMoveTo" | |
| namespace | |
| { | |
| UPathFollowingComponent* InitNavigationController(AController& Controller) | |
| { | |
| AAIController* AsAIController = Cast<AAIController>(&Controller); | |
| UPathFollowingComponent* PathFollowingComp = nullptr; | |
| if (AsAIController) | |
| { | |
| PathFollowingComp = AsAIController->GetPathFollowingComponent(); | |
| } | |
| else | |
| { | |
| PathFollowingComp = Controller.FindComponentByClass<UPathFollowingComponent>(); | |
| if (PathFollowingComp == nullptr) | |
| { | |
| PathFollowingComp = NewObject<UPathFollowingComponent>(&Controller); | |
| PathFollowingComp->RegisterComponentWithWorld(Controller.GetWorld()); | |
| PathFollowingComp->Initialize(); | |
| } | |
| } | |
| return PathFollowingComp; | |
| } | |
| } | |
| UAsyncMoveTo::UAsyncMoveTo(const FObjectInitializer& ObjectInitializer) | |
| : Super(ObjectInitializer) | |
| { | |
| if (HasAnyFlags(RF_ClassDefaultObject) == false) | |
| { | |
| AddToRoot(); | |
| } | |
| MoveRequestID = FAIRequestID::InvalidRequest; | |
| MoveRequest.SetAcceptanceRadius(GET_AI_CONFIG_VAR(AcceptanceRadius)); | |
| MoveRequest.SetReachTestIncludesAgentRadius(GET_AI_CONFIG_VAR(bFinishMoveOnGoalOverlap)); | |
| } | |
| UAsyncMoveTo* UAsyncMoveTo::AsyncMoveToLocationOrActor(AController* Controller, FVector InGoalLocation, AActor* InGoalActor, float AcceptanceRadius, bool StopOnOverlap) | |
| { | |
| UAsyncMoveTo* Task = NewObject<UAsyncMoveTo>(); | |
| Task->SetUp(Controller, InGoalLocation, InGoalActor, AcceptanceRadius, StopOnOverlap); | |
| return Task; | |
| } | |
| UAsyncMoveTo* UAsyncMoveTo::AsyncMoveToActor(AController* Controller, AActor* InGoalActor) | |
| { | |
| UAsyncMoveTo* Task = NewObject<UAsyncMoveTo>(); | |
| Task->SetUp(Controller, FVector::Zero(), InGoalActor); | |
| return Task; | |
| } | |
| UAsyncMoveTo* UAsyncMoveTo::AsyncMoveToLocation(AController* Controller, FVector InGoalLocation) | |
| { | |
| UAsyncMoveTo* Task = NewObject<UAsyncMoveTo>(); | |
| Task->SetUp(Controller, InGoalLocation, nullptr); | |
| return Task; | |
| } | |
| void UAsyncMoveTo::SetUp(AController* InController, FVector InGoalLocation, AActor* InGoalActor, float AcceptanceRadius, bool StopOnOverlap) | |
| { | |
| UE_LOG(AsyncMoveTo, Warning, TEXT("SetUp")); | |
| Controller = InController; | |
| GoalLocation = InGoalLocation; | |
| GoalActor = InGoalActor; | |
| if (InGoalActor) | |
| { | |
| MoveRequest.SetGoalActor(InGoalActor); | |
| } | |
| else | |
| { | |
| MoveRequest.SetGoalLocation(InGoalLocation); | |
| } | |
| MoveRequest.SetAcceptanceRadius(AcceptanceRadius); | |
| MoveRequest.SetReachTestIncludesAgentRadius(StopOnOverlap); | |
| NavSys = Controller ? FNavigationSystem::GetCurrent<UNavigationSystemV1>(Controller->GetWorld()) : nullptr; | |
| if (NavSys == nullptr || Controller == nullptr || Controller->GetPawn() == nullptr) | |
| { | |
| UE_LOG(LogNavigation, Warning, TEXT("UNavigationSystemV1::AsyncMoveTo called for NavSys:%s Controller:%s controlling Pawn:%s (if any of these is None then there's your problem"), | |
| *GetNameSafe(NavSys), *GetNameSafe(Controller), Controller ? *GetNameSafe(Controller->GetPawn()) : TEXT("NULL")); | |
| return; | |
| } | |
| PathFollowing = InitNavigationController(*Controller); | |
| if (PathFollowing == nullptr) | |
| { | |
| FMessageLog("PIE").Warning(FText::Format( | |
| LOCTEXT("AsyncMoveToErrorNoComp", "AsyncMoveTo failed for {0}: missing components"), | |
| FText::FromName(Controller->GetFName()) | |
| )); | |
| return; | |
| } | |
| if (!PathFollowing->IsPathFollowingAllowed()) | |
| { | |
| FMessageLog("PIE").Warning(FText::Format( | |
| LOCTEXT("AsyncMoveToErrorMovement", "AsyncMoveTo failed for {0}: movement not allowed"), | |
| FText::FromName(Controller->GetFName()) | |
| )); | |
| return; | |
| } | |
| Run(); | |
| } | |
| void UAsyncMoveTo::Run() | |
| { | |
| const bool bAlreadyAtGoal = (GoalActor != nullptr) ? PathFollowing->HasReached(*GoalActor, EPathFollowingReachMode::OverlapAgent) : PathFollowing->HasReached(GoalLocation, EPathFollowingReachMode::OverlapAgent); | |
| if (PathFollowing->GetStatus() != EPathFollowingStatus::Idle) | |
| { | |
| PathFollowing->AbortMove(*NavSys, FPathFollowingResultFlags::ForcedScript | FPathFollowingResultFlags::NewRequest, FAIRequestID::AnyRequest, bAlreadyAtGoal ? EPathFollowingVelocityMode::Reset : EPathFollowingVelocityMode::Keep); | |
| } | |
| if (PathFollowing->GetStatus() != EPathFollowingStatus::Idle) | |
| { | |
| PathFollowing->AbortMove(*NavSys, FPathFollowingResultFlags::ForcedScript | FPathFollowingResultFlags::NewRequest); | |
| } | |
| if (bAlreadyAtGoal) | |
| { | |
| PathFollowing->RequestMoveWithImmediateFinish(EPathFollowingResult::Success); | |
| OnSuccess.Broadcast(EPathFollowingResult::Success); | |
| return; | |
| } | |
| const FVector AgentNavLocation = Controller->GetNavAgentLocation(); | |
| const ANavigationData* NavData = NavSys->GetNavDataForProps(Controller->GetNavAgentPropertiesRef(), AgentNavLocation); | |
| if (NavData) | |
| { | |
| FPathFindingQuery Query(Controller, *NavData, AgentNavLocation, ((GoalActor != nullptr) ? GoalActor->GetActorLocation() : GoalLocation)); | |
| FPathFindingResult Result = NavSys->FindPathSync(Query); | |
| if (Result.IsSuccessful()) | |
| { | |
| if (GoalActor != nullptr) | |
| { | |
| Result.Path->SetGoalActorObservation(*GoalActor, 100.0f); | |
| } | |
| MoveRequestID = PathFollowing->RequestMove(MoveRequest, Result.Path); | |
| PathFollowing->OnRequestFinished.AddUObject(this, &UAsyncMoveTo::OnRequestFinished); | |
| } | |
| else if (PathFollowing->GetStatus() != EPathFollowingStatus::Idle) | |
| { | |
| PathFollowing->RequestMoveWithImmediateFinish(EPathFollowingResult::Invalid); | |
| OnFail.Broadcast(EPathFollowingResult::Invalid); | |
| } | |
| } | |
| } | |
| void UAsyncMoveTo::OnRequestFinished(FAIRequestID RequestID, const FPathFollowingResult& Result) | |
| { | |
| UE_LOG(AsyncMoveTo, Warning, TEXT("OnRequestFinished")); | |
| PathFollowing->OnRequestFinished.RemoveAll(this); | |
| if (RequestID == MoveRequestID) | |
| { | |
| if (Result.IsSuccess()) | |
| { | |
| OnSuccess.Broadcast(Result.Code); | |
| } | |
| else | |
| { | |
| OnFail.Broadcast(Result.Code); | |
| } | |
| } | |
| RemoveFromRoot(); | |
| } | |
| #undef LOCTEXT_NAMESPACE |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #pragma once | |
| #include "CoreMinimal.h" | |
| #include "UObject/ObjectMacros.h" | |
| #include "Engine/EngineTypes.h" | |
| #include "AITypes.h" | |
| #include "Navigation/PathFollowingComponent.h" | |
| #include "Kismet/BlueprintAsyncActionBase.h" | |
| #include "AsyncMoveTo.generated.h" | |
| class UNavigationSystemV1; | |
| DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FAsyncMoveToDelegate, TEnumAsByte<EPathFollowingResult::Type>, MovementResult); | |
| UCLASS() | |
| class OLDTALE_API UAsyncMoveTo : public UBlueprintAsyncActionBase | |
| { | |
| GENERATED_UCLASS_BODY() | |
| public: | |
| UFUNCTION(BlueprintCallable, Category = "AI|Navigation", meta = (AdvancedDisplay = "AcceptanceRadius,StopOnOverlap", DefaultToSelf = "Controller", BlueprintInternalUseOnly = "TRUE", DisplayName = "Async Move To")) | |
| static UAsyncMoveTo* AsyncMoveToLocationOrActor(AController* Controller, FVector Destination, AActor* TargetActor = nullptr, float AcceptanceRadius = 5.f, bool StopOnOverlap = false); | |
| UFUNCTION(BlueprintCallable, Category = "AI|Navigation", meta = (DefaultToSelf = "Controller", BlueprintInternalUseOnly = "TRUE", DisplayName = "Async Move To Actor")) | |
| static UAsyncMoveTo* AsyncMoveToActor(AController* Controller, AActor* TargetActor); | |
| UFUNCTION(BlueprintCallable, Category = "AI|Navigation", meta = (DefaultToSelf = "Controller", BlueprintInternalUseOnly = "TRUE", DisplayName = "Async Move To Location")) | |
| static UAsyncMoveTo* AsyncMoveToLocation(AController* Controller, FVector Destination); | |
| public: | |
| UPROPERTY(BlueprintAssignable) | |
| FAsyncMoveToDelegate OnSuccess; | |
| UPROPERTY(BlueprintAssignable) | |
| FAsyncMoveToDelegate OnFail; | |
| private: | |
| void SetUp(AController* Controller, FVector Destination, AActor* TargetActor = nullptr, float AcceptanceRadius = 5.f, bool StopOnOverlap = false); | |
| void Run(); | |
| protected: | |
| UPROPERTY() | |
| FAIMoveRequest MoveRequest; | |
| FAIRequestID MoveRequestID; | |
| AController* Controller; | |
| FVector GoalLocation; | |
| AActor* GoalActor; | |
| UNavigationSystemV1* NavSys; | |
| UPathFollowingComponent* PathFollowing; | |
| virtual void OnRequestFinished(FAIRequestID RequestID, const FPathFollowingResult& Result); | |
| }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment