Hope following codes can help somebody
MoveActorToLocationAndWait.h
#pragma once
#include "CoreMinimal.h"
#include "GameplayTask.h"
#include "Navigation/PathFollowingComponent.h"
#include "AbilitySystemComponent.h"
#include "MoveActorToLocationAndWait.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FMoveActorToLocationDelegate);
UCLASS()
class CRISISINKARNO_API UMoveActorToLocationAndWait : public UGameplayTask
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, meta = (BlueprintInternalUseOnly = "TRUE"))
static UMoveActorToLocationAndWait* moveActorToLocationAndWait(UAbilitySystemComponent* abilitySystemComponent, AController* controller,
const FVector& goalLocation, float AgentRadiusMultiplier = 1, float AgentHalfHeightMultiplier = 1.1);
virtual void Activate() override;
virtual void ExternalCancel() override;
virtual void OnDestroy(bool AbilityEnded) override;
private:
virtual void OnRequestFinished(FAIRequestID RequestID, const FPathFollowingResult& Result);
UPathFollowingComponent* initNavigationController(AController& Controller);
public:
UPROPERTY(BlueprintAssignable)
FMoveActorToLocationDelegate onCompleted;
UPROPERTY(BlueprintAssignable)
FMoveActorToLocationDelegate onFailed;
private:
AController* _controller = nullptr;
FVector _goalLocation;
float _agentRadiusMultiplier;
float _agentHalfHeightMultiplier;
UPathFollowingComponent* _pathFollowingComponent = nullptr;
FDelegateHandle _callbackHandle;
};
MoveActorToLocationAndWait.cpp
#include "MoveActorToLocationAndWait.h"
#include "NavigationPath.h"
#include "NavigationData.h"
#include "NavigationSystem.h"
#include "NavMesh/NavMeshPath.h"
#include "Navigation/PathFollowingComponent.h"
#include "AIController.h"
UMoveActorToLocationAndWait* UMoveActorToLocationAndWait::moveActorToLocationAndWait(UAbilitySystemComponent* abilitySystemComponent, AController* controller, const FVector& goalLocation,
float AgentRadiusMultiplier, float AgentHalfHeightMultiplier)
{
UMoveActorToLocationAndWait* ret = NewObject<UMoveActorToLocationAndWait>();
ret->_controller = controller;
ret->_goalLocation = goalLocation;
ret->_agentHalfHeightMultiplier = AgentHalfHeightMultiplier;
ret->_agentRadiusMultiplier = AgentRadiusMultiplier;
ret->InitTask(*abilitySystemComponent, abilitySystemComponent->GetGameplayTaskDefaultPriority());
return ret;
}
UPathFollowingComponent* UMoveActorToLocationAndWait::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;
}
void UMoveActorToLocationAndWait::Activate()
{
UNavigationSystemV1* NavSys = _controller ? FNavigationSystem::GetCurrent<UNavigationSystemV1>(_controller->GetWorld()) : nullptr;
if (NavSys == nullptr || _controller == nullptr || _controller->GetPawn() == nullptr)
{
EndTask();
onFailed.Broadcast();
return;
}
if (_pathFollowingComponent == nullptr)
{
_pathFollowingComponent = initNavigationController(*_controller);
if (_pathFollowingComponent == nullptr)
{
EndTask();
onFailed.Broadcast();
return;
}
}
if (!_pathFollowingComponent->IsPathFollowingAllowed())
{
EndTask();
onFailed.Broadcast();
return;
}
const bool bAlreadyAtGoal = _pathFollowingComponent->HasReached(_goalLocation, EPathFollowingReachMode::OverlapGoal);
// script source, keep only one move request at time
if (_pathFollowingComponent->GetStatus() != EPathFollowingStatus::Idle)
{
_pathFollowingComponent->AbortMove(*NavSys, FPathFollowingResultFlags::ForcedScript | FPathFollowingResultFlags::NewRequest
, FAIRequestID::AnyRequest, bAlreadyAtGoal ? EPathFollowingVelocityMode::Reset : EPathFollowingVelocityMode::Keep);
}
// script source, keep only one move request at time
if (_pathFollowingComponent->GetStatus() != EPathFollowingStatus::Idle)
{
_pathFollowingComponent->AbortMove(*NavSys, FPathFollowingResultFlags::ForcedScript | FPathFollowingResultFlags::NewRequest);
}
do
{
if (bAlreadyAtGoal)
{
_callbackHandle = _pathFollowingComponent->OnRequestFinished.AddUObject(this, &UMoveActorToLocationAndWait::OnRequestFinished);
_pathFollowingComponent->RequestMoveWithImmediateFinish(EPathFollowingResult::Success);
}
else
{
const FVector AgentNavLocation = _controller->GetNavAgentLocation();
const ANavigationData* NavData = NavSys->GetNavDataForProps(_controller->GetNavAgentPropertiesRef(), AgentNavLocation);
if (!NavData)
break;
FPathFindingQuery Query(_controller, *NavData, AgentNavLocation, _goalLocation);
FPathFindingResult Result = NavSys->FindPathSync(Query);
if (Result.IsSuccessful())
{
//Result.Path->DebugDraw(NavData, FColor(255, 0, 0, 255), nullptr, true, 10, 0); //debug
_callbackHandle = _pathFollowingComponent->OnRequestFinished.AddUObject(this, &UMoveActorToLocationAndWait::OnRequestFinished);
FAIMoveRequest requestData(_goalLocation);
_pathFollowingComponent->SetPreciseReachThreshold(_agentRadiusMultiplier, _agentHalfHeightMultiplier);
_pathFollowingComponent->RequestMove(requestData, Result.Path);
}
else if(_pathFollowingComponent->GetStatus() != EPathFollowingStatus::Idle)
{
_callbackHandle = _pathFollowingComponent->OnRequestFinished.AddUObject(this, &UMoveActorToLocationAndWait::OnRequestFinished);
_pathFollowingComponent->RequestMoveWithImmediateFinish(EPathFollowingResult::Invalid);
}
else {
break;
}
}
return;
} while (false);
EndTask();
onFailed.Broadcast();
}
void UMoveActorToLocationAndWait::ExternalCancel()
{
}
void UMoveActorToLocationAndWait::OnDestroy(bool AbilityEnded)
{
if (_pathFollowingComponent && _callbackHandle.IsValid())
_pathFollowingComponent->OnRequestFinished.Remove(_callbackHandle);
Super::OnDestroy(AbilityEnded);
}
void UMoveActorToLocationAndWait::OnRequestFinished(FAIRequestID requestID, const FPathFollowingResult& result)
{
EndTask();
onCompleted.Broadcast();
}