Import helper plugin
This commit is contained in:
@@ -0,0 +1,245 @@
|
||||
// Copyright (c) 2026 Unreal Directive. Licensed under the MIT License.
|
||||
|
||||
|
||||
#include "Tasks/DirectiveUtilTask_AsyncLoadAsset.h"
|
||||
#include "DirectiveUtilLogChannels.h"
|
||||
#include "Engine/AssetManager.h"
|
||||
#include "Engine/StreamableManager.h"
|
||||
|
||||
UDirectiveUtilTask_AsyncLoadAsset* UDirectiveUtilTask_AsyncLoadAsset::AsyncLoadAsset(UObject* WorldContextObject, const TSoftObjectPtr<UObject> Asset)
|
||||
{
|
||||
UDirectiveUtilTask_AsyncLoadAsset* Action = NewObject<UDirectiveUtilTask_AsyncLoadAsset>();
|
||||
Action->SoftAsset = Asset;
|
||||
|
||||
if (WorldContextObject)
|
||||
{
|
||||
Action->RegisterWithGameInstance(WorldContextObject);
|
||||
}
|
||||
|
||||
return Action;
|
||||
}
|
||||
|
||||
void UDirectiveUtilTask_AsyncLoadAsset::Activate()
|
||||
{
|
||||
if (SoftAsset.IsNull())
|
||||
{
|
||||
UE_LOG(LogDirectiveUtil, Warning, TEXT("Async Load Asset failed to activate. The soft object reference is null."));
|
||||
Failed.Broadcast(nullptr);
|
||||
SetReadyToDestroy();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!UAssetManager::GetIfInitialized())
|
||||
{
|
||||
UE_LOG(LogDirectiveUtil, Warning, TEXT("Async Load Asset failed to activate. The Asset Manager is not initialized."));
|
||||
Failed.Broadcast(nullptr);
|
||||
SetReadyToDestroy();
|
||||
return;
|
||||
}
|
||||
|
||||
StreamableHandle = UAssetManager::GetStreamableManager().RequestAsyncLoad(
|
||||
SoftAsset.ToSoftObjectPath(),
|
||||
FStreamableDelegate::CreateUObject(this, &UDirectiveUtilTask_AsyncLoadAsset::OnLoaded),
|
||||
FStreamableManager::DefaultAsyncLoadPriority);
|
||||
|
||||
if (!StreamableHandle.IsValid())
|
||||
{
|
||||
UE_LOG(LogDirectiveUtil, Warning, TEXT("Async Load Asset failed to start the load request."));
|
||||
Failed.Broadcast(nullptr);
|
||||
SetReadyToDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
void UDirectiveUtilTask_AsyncLoadAsset::OnLoaded()
|
||||
{
|
||||
UObject* LoadedAsset = StreamableHandle.IsValid() ? StreamableHandle->GetLoadedAsset() : nullptr;
|
||||
if (LoadedAsset)
|
||||
{
|
||||
Completed.Broadcast(LoadedAsset);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogDirectiveUtil, Warning, TEXT("Async Load Asset completed but the asset could not be resolved."));
|
||||
Failed.Broadcast(nullptr);
|
||||
}
|
||||
SetReadyToDestroy();
|
||||
}
|
||||
|
||||
void UDirectiveUtilTask_AsyncLoadAsset::Cancel()
|
||||
{
|
||||
if (StreamableHandle.IsValid() && StreamableHandle->IsActive())
|
||||
{
|
||||
StreamableHandle->CancelHandle();
|
||||
}
|
||||
SetReadyToDestroy();
|
||||
}
|
||||
|
||||
UDirectiveUtilTask_AsyncLoadClass* UDirectiveUtilTask_AsyncLoadClass::AsyncLoadClass(UObject* WorldContextObject, const TSoftClassPtr<UObject> AssetClass)
|
||||
{
|
||||
UDirectiveUtilTask_AsyncLoadClass* Action = NewObject<UDirectiveUtilTask_AsyncLoadClass>();
|
||||
Action->SoftClass = AssetClass;
|
||||
|
||||
if (WorldContextObject)
|
||||
{
|
||||
Action->RegisterWithGameInstance(WorldContextObject);
|
||||
}
|
||||
|
||||
return Action;
|
||||
}
|
||||
|
||||
void UDirectiveUtilTask_AsyncLoadClass::Activate()
|
||||
{
|
||||
if (SoftClass.IsNull())
|
||||
{
|
||||
UE_LOG(LogDirectiveUtil, Warning, TEXT("Async Load Class failed to activate. The soft class reference is null."));
|
||||
Failed.Broadcast(nullptr);
|
||||
SetReadyToDestroy();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!UAssetManager::GetIfInitialized())
|
||||
{
|
||||
UE_LOG(LogDirectiveUtil, Warning, TEXT("Async Load Class failed to activate. The Asset Manager is not initialized."));
|
||||
Failed.Broadcast(nullptr);
|
||||
SetReadyToDestroy();
|
||||
return;
|
||||
}
|
||||
|
||||
StreamableHandle = UAssetManager::GetStreamableManager().RequestAsyncLoad(
|
||||
SoftClass.ToSoftObjectPath(),
|
||||
FStreamableDelegate::CreateUObject(this, &UDirectiveUtilTask_AsyncLoadClass::OnLoaded),
|
||||
FStreamableManager::DefaultAsyncLoadPriority);
|
||||
|
||||
if (!StreamableHandle.IsValid())
|
||||
{
|
||||
UE_LOG(LogDirectiveUtil, Warning, TEXT("Async Load Class failed to start the load request."));
|
||||
Failed.Broadcast(nullptr);
|
||||
SetReadyToDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
void UDirectiveUtilTask_AsyncLoadClass::OnLoaded()
|
||||
{
|
||||
UClass* LoadedClass = StreamableHandle.IsValid() ? Cast<UClass>(StreamableHandle->GetLoadedAsset()) : nullptr;
|
||||
if (LoadedClass)
|
||||
{
|
||||
Completed.Broadcast(LoadedClass);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogDirectiveUtil, Warning, TEXT("Async Load Class completed but the class could not be resolved."));
|
||||
Failed.Broadcast(nullptr);
|
||||
}
|
||||
SetReadyToDestroy();
|
||||
}
|
||||
|
||||
void UDirectiveUtilTask_AsyncLoadClass::Cancel()
|
||||
{
|
||||
if (StreamableHandle.IsValid() && StreamableHandle->IsActive())
|
||||
{
|
||||
StreamableHandle->CancelHandle();
|
||||
}
|
||||
SetReadyToDestroy();
|
||||
}
|
||||
|
||||
UDirectiveUtilTask_AsyncLoadAssets* UDirectiveUtilTask_AsyncLoadAssets::AsyncLoadAssets(UObject* WorldContextObject, const TArray<TSoftObjectPtr<UObject>>& Assets)
|
||||
{
|
||||
UDirectiveUtilTask_AsyncLoadAssets* Action = NewObject<UDirectiveUtilTask_AsyncLoadAssets>();
|
||||
Action->SoftAssets = Assets;
|
||||
|
||||
if (WorldContextObject)
|
||||
{
|
||||
Action->RegisterWithGameInstance(WorldContextObject);
|
||||
}
|
||||
|
||||
return Action;
|
||||
}
|
||||
|
||||
void UDirectiveUtilTask_AsyncLoadAssets::Activate()
|
||||
{
|
||||
// Unset references are filtered out of the request but keep their null slots in the output;
|
||||
// duplicates are requested once and resolved per slot.
|
||||
TArray<FSoftObjectPath> PathsToLoad;
|
||||
for (const TSoftObjectPtr<UObject>& SoftAsset : SoftAssets)
|
||||
{
|
||||
if (!SoftAsset.IsNull())
|
||||
{
|
||||
PathsToLoad.AddUnique(SoftAsset.ToSoftObjectPath());
|
||||
}
|
||||
}
|
||||
|
||||
if (PathsToLoad.Num() == 0)
|
||||
{
|
||||
// Nothing to request; an empty request list is an error path in the streamable manager.
|
||||
OnLoaded();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!UAssetManager::GetIfInitialized())
|
||||
{
|
||||
UE_LOG(LogDirectiveUtil, Warning, TEXT("Async Load Assets failed to activate. The Asset Manager is not initialized."));
|
||||
OnLoaded();
|
||||
return;
|
||||
}
|
||||
|
||||
StreamableHandle = UAssetManager::GetStreamableManager().RequestAsyncLoad(
|
||||
MoveTemp(PathsToLoad),
|
||||
FStreamableDelegate::CreateUObject(this, &UDirectiveUtilTask_AsyncLoadAssets::OnLoaded),
|
||||
FStreamableManager::DefaultAsyncLoadPriority);
|
||||
|
||||
if (!StreamableHandle.IsValid())
|
||||
{
|
||||
UE_LOG(LogDirectiveUtil, Warning, TEXT("Async Load Assets failed to start the load request."));
|
||||
OnLoaded();
|
||||
return;
|
||||
}
|
||||
|
||||
// Binding fails when the load already finished (all assets were in memory); complete directly,
|
||||
// with the guard keeping the broadcast exactly once.
|
||||
if (!StreamableHandle->BindUpdateDelegate(FStreamableUpdateDelegate::CreateUObject(this, &UDirectiveUtilTask_AsyncLoadAssets::OnUpdate)))
|
||||
{
|
||||
OnLoaded();
|
||||
}
|
||||
}
|
||||
|
||||
void UDirectiveUtilTask_AsyncLoadAssets::OnLoaded()
|
||||
{
|
||||
if (bHasCompleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
bHasCompleted = true;
|
||||
|
||||
TArray<UObject*> LoadedAssets;
|
||||
LoadedAssets.Reserve(SoftAssets.Num());
|
||||
for (const TSoftObjectPtr<UObject>& SoftAsset : SoftAssets)
|
||||
{
|
||||
LoadedAssets.Add(SoftAsset.Get());
|
||||
}
|
||||
|
||||
Completed.Broadcast(LoadedAssets);
|
||||
SetReadyToDestroy();
|
||||
}
|
||||
|
||||
void UDirectiveUtilTask_AsyncLoadAssets::OnUpdate(TSharedRef<FStreamableHandle> Handle)
|
||||
{
|
||||
if (bHasCompleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int32 LoadedCount = 0;
|
||||
int32 RequestedCount = 0;
|
||||
Handle->GetLoadedCount(LoadedCount, RequestedCount);
|
||||
Progress.Broadcast(LoadedCount, RequestedCount);
|
||||
}
|
||||
|
||||
void UDirectiveUtilTask_AsyncLoadAssets::Cancel()
|
||||
{
|
||||
bHasCompleted = true;
|
||||
if (StreamableHandle.IsValid() && StreamableHandle->IsActive())
|
||||
{
|
||||
StreamableHandle->CancelHandle();
|
||||
}
|
||||
SetReadyToDestroy();
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
// Copyright (c) 2026 Unreal Directive. Licensed under the MIT License.
|
||||
|
||||
|
||||
#include "Tasks/DirectiveUtilTask_AsyncTrace.h"
|
||||
#include "DirectiveUtilLogChannels.h"
|
||||
#include "Engine/Engine.h"
|
||||
#include "Engine/World.h"
|
||||
#include "WorldCollision.h"
|
||||
#include "CollisionShape.h"
|
||||
#include "CollisionQueryParams.h"
|
||||
|
||||
UDirectiveUtilTask_AsyncTrace* UDirectiveUtilTask_AsyncTrace::AsyncLineTraceByChannel(UObject* WorldContextObject, const FVector Start, const FVector End, const ETraceTypeQuery TraceChannel, const bool bMultiTrace)
|
||||
{
|
||||
UDirectiveUtilTask_AsyncTrace* Action = NewObject<UDirectiveUtilTask_AsyncTrace>();
|
||||
Action->WorldContextObject = WorldContextObject;
|
||||
Action->Start = Start;
|
||||
Action->End = End;
|
||||
Action->TraceChannel = TraceChannel;
|
||||
Action->bMultiTrace = bMultiTrace;
|
||||
Action->Shape = EDirectiveUtilTraceShape::Line;
|
||||
|
||||
if (WorldContextObject)
|
||||
{
|
||||
Action->RegisterWithGameInstance(WorldContextObject);
|
||||
}
|
||||
|
||||
return Action;
|
||||
}
|
||||
|
||||
UDirectiveUtilTask_AsyncTrace* UDirectiveUtilTask_AsyncTrace::AsyncSphereTraceByChannel(UObject* WorldContextObject, const FVector Start, const FVector End, const float Radius, const ETraceTypeQuery TraceChannel, const bool bMultiTrace)
|
||||
{
|
||||
UDirectiveUtilTask_AsyncTrace* Action = NewObject<UDirectiveUtilTask_AsyncTrace>();
|
||||
Action->WorldContextObject = WorldContextObject;
|
||||
Action->Start = Start;
|
||||
Action->End = End;
|
||||
Action->Radius = Radius;
|
||||
Action->TraceChannel = TraceChannel;
|
||||
Action->bMultiTrace = bMultiTrace;
|
||||
Action->Shape = EDirectiveUtilTraceShape::Sphere;
|
||||
|
||||
if (WorldContextObject)
|
||||
{
|
||||
Action->RegisterWithGameInstance(WorldContextObject);
|
||||
}
|
||||
|
||||
return Action;
|
||||
}
|
||||
|
||||
UDirectiveUtilTask_AsyncTrace* UDirectiveUtilTask_AsyncTrace::AsyncBoxTraceByChannel(UObject* WorldContextObject, const FVector Start, const FVector End, const FVector HalfSize, const FRotator Orientation, const ETraceTypeQuery TraceChannel, const bool bMultiTrace)
|
||||
{
|
||||
UDirectiveUtilTask_AsyncTrace* Action = NewObject<UDirectiveUtilTask_AsyncTrace>();
|
||||
Action->WorldContextObject = WorldContextObject;
|
||||
Action->Start = Start;
|
||||
Action->End = End;
|
||||
Action->HalfSize = HalfSize;
|
||||
Action->Orientation = Orientation.Quaternion();
|
||||
Action->TraceChannel = TraceChannel;
|
||||
Action->bMultiTrace = bMultiTrace;
|
||||
Action->Shape = EDirectiveUtilTraceShape::Box;
|
||||
|
||||
if (WorldContextObject)
|
||||
{
|
||||
Action->RegisterWithGameInstance(WorldContextObject);
|
||||
}
|
||||
|
||||
return Action;
|
||||
}
|
||||
|
||||
UDirectiveUtilTask_AsyncTrace* UDirectiveUtilTask_AsyncTrace::AsyncCapsuleTraceByChannel(UObject* WorldContextObject, const FVector Start, const FVector End, const float Radius, const float HalfHeight, const ETraceTypeQuery TraceChannel, const bool bMultiTrace)
|
||||
{
|
||||
UDirectiveUtilTask_AsyncTrace* Action = NewObject<UDirectiveUtilTask_AsyncTrace>();
|
||||
Action->WorldContextObject = WorldContextObject;
|
||||
Action->Start = Start;
|
||||
Action->End = End;
|
||||
Action->Radius = Radius;
|
||||
Action->HalfHeight = HalfHeight;
|
||||
Action->TraceChannel = TraceChannel;
|
||||
Action->bMultiTrace = bMultiTrace;
|
||||
Action->Shape = EDirectiveUtilTraceShape::Capsule;
|
||||
|
||||
if (WorldContextObject)
|
||||
{
|
||||
Action->RegisterWithGameInstance(WorldContextObject);
|
||||
}
|
||||
|
||||
return Action;
|
||||
}
|
||||
|
||||
void UDirectiveUtilTask_AsyncTrace::Activate()
|
||||
{
|
||||
UWorld* World = WorldContextObject && GEngine
|
||||
? GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull)
|
||||
: nullptr;
|
||||
if (!World)
|
||||
{
|
||||
UE_LOG(LogDirectiveUtil, Warning, TEXT("Async Trace failed to activate. World is null."));
|
||||
Completed.Broadcast(TArray<FHitResult>());
|
||||
SetReadyToDestroy();
|
||||
return;
|
||||
}
|
||||
|
||||
const ECollisionChannel CollisionChannel = UEngineTypes::ConvertToCollisionChannel(TraceChannel);
|
||||
const EAsyncTraceType AsyncType = bMultiTrace ? EAsyncTraceType::Multi : EAsyncTraceType::Single;
|
||||
|
||||
FTraceDelegate TraceDelegate;
|
||||
TraceDelegate.BindUObject(this, &UDirectiveUtilTask_AsyncTrace::OnTraceComplete);
|
||||
|
||||
FCollisionQueryParams Params(FName(TEXT("DirectiveUtilAsyncTrace")), false);
|
||||
|
||||
if (Shape == EDirectiveUtilTraceShape::Line)
|
||||
{
|
||||
World->AsyncLineTraceByChannel(AsyncType, Start, End, CollisionChannel, Params, FCollisionResponseParams::DefaultResponseParam, &TraceDelegate);
|
||||
}
|
||||
else
|
||||
{
|
||||
FCollisionShape CollisionShape;
|
||||
switch (Shape)
|
||||
{
|
||||
case EDirectiveUtilTraceShape::Sphere:
|
||||
CollisionShape = FCollisionShape::MakeSphere(Radius);
|
||||
break;
|
||||
case EDirectiveUtilTraceShape::Box:
|
||||
CollisionShape = FCollisionShape::MakeBox(HalfSize);
|
||||
break;
|
||||
case EDirectiveUtilTraceShape::Capsule:
|
||||
CollisionShape = FCollisionShape::MakeCapsule(Radius, HalfHeight);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
World->AsyncSweepByChannel(AsyncType, Start, End, Orientation, CollisionChannel, CollisionShape, Params, FCollisionResponseParams::DefaultResponseParam, &TraceDelegate);
|
||||
}
|
||||
}
|
||||
|
||||
void UDirectiveUtilTask_AsyncTrace::OnTraceComplete(const FTraceHandle& Handle, FTraceDatum& Datum)
|
||||
{
|
||||
Completed.Broadcast(Datum.OutHits);
|
||||
SetReadyToDestroy();
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
// Copyright (c) 2026 Unreal Directive. Licensed under the MIT License.
|
||||
|
||||
|
||||
#include "Tasks/DirectiveUtilTask_Delay.h"
|
||||
#include "DirectiveUtilLogChannels.h"
|
||||
#include "Engine/Engine.h"
|
||||
#include "Engine/World.h"
|
||||
#include "TimerManager.h"
|
||||
|
||||
|
||||
UDirectiveUtilTask_Delay* UDirectiveUtilTask_Delay::CancellableDelay(UObject* WorldContextObject, const float Duration)
|
||||
{
|
||||
UDirectiveUtilTask_Delay* Action = NewObject<UDirectiveUtilTask_Delay>();
|
||||
Action->WorldContextObject = WorldContextObject;
|
||||
Action->Duration = Duration;
|
||||
|
||||
if (WorldContextObject)
|
||||
{
|
||||
Action->RegisterWithGameInstance(WorldContextObject);
|
||||
}
|
||||
|
||||
return Action;
|
||||
}
|
||||
|
||||
|
||||
void UDirectiveUtilTask_Delay::EndTask()
|
||||
{
|
||||
if (UWorld* World = GEngine ? GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::ReturnNull) : nullptr)
|
||||
{
|
||||
World->GetTimerManager().ClearTimer(TimerHandle);
|
||||
}
|
||||
SetReadyToDestroy();
|
||||
}
|
||||
|
||||
void UDirectiveUtilTask_Delay::Activate()
|
||||
{
|
||||
UWorld* World = WorldContextObject && GEngine
|
||||
? GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull)
|
||||
: nullptr;
|
||||
if (!World)
|
||||
{
|
||||
UE_LOG(LogDirectiveUtil, Warning, TEXT("Cancellable Delay failed to activate. World is null."));
|
||||
SetReadyToDestroy();
|
||||
return;
|
||||
}
|
||||
|
||||
const float ClampedDuration = FMath::Max(Duration, KINDA_SMALL_NUMBER);
|
||||
World->GetTimerManager().SetTimer(TimerHandle, this, &UDirectiveUtilTask_Delay::OnDelayComplete, ClampedDuration, false);
|
||||
UE_LOG(LogDirectiveUtil, Verbose, TEXT("Cancellable Delay started for %f seconds."), ClampedDuration);
|
||||
}
|
||||
|
||||
void UDirectiveUtilTask_Delay::OnDelayComplete()
|
||||
{
|
||||
UE_LOG(LogDirectiveUtil, Verbose, TEXT("Cancellable Delay completed."));
|
||||
Completed.Broadcast();
|
||||
SetReadyToDestroy();
|
||||
}
|
||||
@@ -0,0 +1,280 @@
|
||||
// Copyright (c) 2026 Unreal Directive. Licensed under the MIT License.
|
||||
|
||||
|
||||
#include "Tasks/DirectiveUtilTask_MoveToLocation.h"
|
||||
#include "DirectiveUtilLogChannels.h"
|
||||
#include "Blueprint/AIBlueprintHelperLibrary.h"
|
||||
#include "Engine/World.h"
|
||||
#include "DrawDebugHelpers.h"
|
||||
#include "Navigation/PathFollowingComponent.h"
|
||||
#include "TimerManager.h"
|
||||
|
||||
|
||||
UDirectiveUtilTask_MoveToLocation* UDirectiveUtilTask_MoveToLocation::MoveToLocation(
|
||||
UObject* WorldContextObject,
|
||||
AController* Controller,
|
||||
const FVector Destination,
|
||||
const float AcceptanceRadius,
|
||||
const bool bCheckStuckMovement,
|
||||
const float StuckThreshold,
|
||||
const bool bDebugLineTrace)
|
||||
{
|
||||
UDirectiveUtilTask_MoveToLocation* Action = NewObject<UDirectiveUtilTask_MoveToLocation>();
|
||||
Action->Controller = Controller;
|
||||
Action->Destination = Destination;
|
||||
Action->AcceptanceRadius = AcceptanceRadius;
|
||||
Action->bDebugLineTrace = bDebugLineTrace;
|
||||
Action->StuckThreshold = StuckThreshold;
|
||||
Action->bCheckStuckMovement = bCheckStuckMovement;
|
||||
|
||||
if (WorldContextObject)
|
||||
{
|
||||
Action->RegisterWithGameInstance(WorldContextObject);
|
||||
}
|
||||
|
||||
return Action;
|
||||
}
|
||||
|
||||
|
||||
void UDirectiveUtilTask_MoveToLocation::EndTask()
|
||||
{
|
||||
ExecuteCompleted(false);
|
||||
}
|
||||
|
||||
void UDirectiveUtilTask_MoveToLocation::Activate()
|
||||
{
|
||||
if(!Controller || !Controller->GetPawn())
|
||||
{
|
||||
ExecuteCompleted(false);
|
||||
UE_LOG(LogDirectiveUtil, Warning, TEXT("Controller or pawn has been destroyed while moving to location. Aborting."));
|
||||
return;
|
||||
}
|
||||
|
||||
StartLocation = Controller->GetPawn()->GetActorLocation();
|
||||
LastCheckedLocation = StartLocation;
|
||||
CurrentLocation = StartLocation;
|
||||
|
||||
Controller->GetWorld()->GetTimerManager().SetTimer(TimerHandle, this, &UDirectiveUtilTask_MoveToLocation::CheckMoveToLocation, 0.1f, true);
|
||||
|
||||
if (bCheckStuckMovement)
|
||||
{
|
||||
Controller->GetWorld()->GetTimerManager().SetTimer(StuckTimerHandle, this, &UDirectiveUtilTask_MoveToLocation::CheckStuckMovement, 3.f, true);
|
||||
}
|
||||
|
||||
UAIBlueprintHelperLibrary::SimpleMoveToLocation(Controller, Destination);
|
||||
UE_LOG(LogDirectiveUtil, Verbose, TEXT("Moving controller to location (%s)."), *Destination.ToString());
|
||||
|
||||
if (bDebugLineTrace)
|
||||
{
|
||||
DrawDebugLine(
|
||||
Controller->GetWorld(),
|
||||
Destination + FVector(0, 0, 100),
|
||||
Destination,
|
||||
FColor::Green,
|
||||
false,
|
||||
5.0f,
|
||||
0,
|
||||
1.0f
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void UDirectiveUtilTask_MoveToLocation::CheckMoveToLocation()
|
||||
{
|
||||
|
||||
if(!Controller || !Controller->GetPawn())
|
||||
{
|
||||
ExecuteCompleted(false);
|
||||
UE_LOG(LogDirectiveUtil, Warning, TEXT("Controller or pawn has been destroyed while moving to location. Aborting."));
|
||||
return;
|
||||
}
|
||||
|
||||
CurrentLocation = Controller->GetPawn()->GetActorLocation();
|
||||
UE_LOG(LogDirectiveUtil, Verbose, TEXT("Controller is moving to location (%s). Current distance: %f."), *Destination.ToString(), FVector::Dist(CurrentLocation, Destination));
|
||||
|
||||
if (FVector::Dist(CurrentLocation, Destination) < AcceptanceRadius)
|
||||
{
|
||||
UE_LOG(LogDirectiveUtil, Verbose, TEXT("Controller has moved to location."));
|
||||
ExecuteCompleted(true);
|
||||
return;
|
||||
}
|
||||
|
||||
const UPathFollowingComponent* PathFollowing = Controller->FindComponentByClass<UPathFollowingComponent>();
|
||||
if (!PathFollowing || PathFollowing->GetStatus() == EPathFollowingStatus::Idle)
|
||||
{
|
||||
UE_LOG(LogDirectiveUtil, Verbose, TEXT("Path following has stopped. Completing move to location."));
|
||||
ExecuteCompleted(FVector::Dist(CurrentLocation, Destination) < AcceptanceRadius);
|
||||
}
|
||||
}
|
||||
|
||||
void UDirectiveUtilTask_MoveToLocation::CheckStuckMovement()
|
||||
{
|
||||
if(!Controller || !Controller->GetPawn())
|
||||
{
|
||||
ExecuteCompleted(false);
|
||||
UE_LOG(LogDirectiveUtil, Warning, TEXT("Controller or pawn has been destroyed while moving to location. Aborting."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (FVector::Dist(CurrentLocation, LastCheckedLocation) < StuckThreshold)
|
||||
{
|
||||
UE_LOG(LogDirectiveUtil, Warning, TEXT("Controller is stuck while moving to location. Aborting"));
|
||||
ExecuteCompleted(false);
|
||||
}
|
||||
|
||||
LastCheckedLocation = CurrentLocation;
|
||||
}
|
||||
|
||||
void UDirectiveUtilTask_MoveToLocation::ExecuteCompleted(const bool bSuccess)
|
||||
{
|
||||
if (bHasCompleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
bHasCompleted = true;
|
||||
|
||||
UE_LOG(LogDirectiveUtil, Log, TEXT("Movement to location completed. Success: %s."), bSuccess ? TEXT("true") : TEXT("false"));
|
||||
|
||||
if (Controller)
|
||||
{
|
||||
Controller->GetWorld()->GetTimerManager().ClearTimer(TimerHandle);
|
||||
Controller->GetWorld()->GetTimerManager().ClearTimer(StuckTimerHandle);
|
||||
}
|
||||
|
||||
Completed.Broadcast(bSuccess);
|
||||
|
||||
Controller = nullptr;
|
||||
Destination = FVector::ZeroVector;
|
||||
|
||||
SetReadyToDestroy();
|
||||
}
|
||||
|
||||
UDirectiveUtilTask_MoveToActor* UDirectiveUtilTask_MoveToActor::MoveToActor(
|
||||
UObject* WorldContextObject,
|
||||
AController* Controller,
|
||||
AActor* Goal,
|
||||
const float AcceptanceRadius,
|
||||
const bool bCheckStuckMovement,
|
||||
const float StuckThreshold)
|
||||
{
|
||||
UDirectiveUtilTask_MoveToActor* Action = NewObject<UDirectiveUtilTask_MoveToActor>();
|
||||
Action->Controller = Controller;
|
||||
Action->Goal = Goal;
|
||||
Action->AcceptanceRadius = AcceptanceRadius;
|
||||
Action->StuckThreshold = StuckThreshold;
|
||||
Action->bCheckStuckMovement = bCheckStuckMovement;
|
||||
|
||||
if (WorldContextObject)
|
||||
{
|
||||
Action->RegisterWithGameInstance(WorldContextObject);
|
||||
}
|
||||
|
||||
return Action;
|
||||
}
|
||||
|
||||
void UDirectiveUtilTask_MoveToActor::EndTask()
|
||||
{
|
||||
ExecuteCompleted(false);
|
||||
}
|
||||
|
||||
void UDirectiveUtilTask_MoveToActor::Activate()
|
||||
{
|
||||
if(!Controller || !Controller->GetPawn() || !IsValid(Goal))
|
||||
{
|
||||
ExecuteCompleted(false);
|
||||
UE_LOG(LogDirectiveUtil, Warning, TEXT("Controller, pawn, or goal has been destroyed while moving to actor. Aborting."));
|
||||
return;
|
||||
}
|
||||
|
||||
StartLocation = Controller->GetPawn()->GetActorLocation();
|
||||
LastCheckedLocation = StartLocation;
|
||||
CurrentLocation = StartLocation;
|
||||
|
||||
Controller->GetWorld()->GetTimerManager().SetTimer(TimerHandle, this, &UDirectiveUtilTask_MoveToActor::CheckMoveToActor, 0.1f, true);
|
||||
|
||||
if (bCheckStuckMovement)
|
||||
{
|
||||
Controller->GetWorld()->GetTimerManager().SetTimer(StuckTimerHandle, this, &UDirectiveUtilTask_MoveToActor::CheckStuckMovement, 3.f, true);
|
||||
}
|
||||
|
||||
UAIBlueprintHelperLibrary::SimpleMoveToActor(Controller, Goal);
|
||||
UE_LOG(LogDirectiveUtil, Verbose, TEXT("Moving controller to actor (%s)."), *GetNameSafe(Goal));
|
||||
}
|
||||
|
||||
void UDirectiveUtilTask_MoveToActor::CheckMoveToActor()
|
||||
{
|
||||
if(!Controller || !Controller->GetPawn())
|
||||
{
|
||||
ExecuteCompleted(false);
|
||||
UE_LOG(LogDirectiveUtil, Warning, TEXT("Controller or pawn has been destroyed while moving to actor. Aborting."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsValid(Goal))
|
||||
{
|
||||
ExecuteCompleted(false);
|
||||
UE_LOG(LogDirectiveUtil, Warning, TEXT("Goal actor has been destroyed while moving to actor. Aborting."));
|
||||
return;
|
||||
}
|
||||
|
||||
// The goal can move, so its location is re-read every poll.
|
||||
const FVector GoalLocation = Goal->GetActorLocation();
|
||||
CurrentLocation = Controller->GetPawn()->GetActorLocation();
|
||||
UE_LOG(LogDirectiveUtil, Verbose, TEXT("Controller is moving to actor (%s). Current distance: %f."), *GetNameSafe(Goal), FVector::Dist(CurrentLocation, GoalLocation));
|
||||
|
||||
if (FVector::Dist(CurrentLocation, GoalLocation) < AcceptanceRadius)
|
||||
{
|
||||
UE_LOG(LogDirectiveUtil, Verbose, TEXT("Controller has moved to actor."));
|
||||
ExecuteCompleted(true);
|
||||
return;
|
||||
}
|
||||
|
||||
const UPathFollowingComponent* PathFollowing = Controller->FindComponentByClass<UPathFollowingComponent>();
|
||||
if (!PathFollowing || PathFollowing->GetStatus() == EPathFollowingStatus::Idle)
|
||||
{
|
||||
UE_LOG(LogDirectiveUtil, Verbose, TEXT("Path following has stopped. Completing move to actor."));
|
||||
ExecuteCompleted(FVector::Dist(CurrentLocation, GoalLocation) < AcceptanceRadius);
|
||||
}
|
||||
}
|
||||
|
||||
void UDirectiveUtilTask_MoveToActor::CheckStuckMovement()
|
||||
{
|
||||
if(!Controller || !Controller->GetPawn())
|
||||
{
|
||||
ExecuteCompleted(false);
|
||||
UE_LOG(LogDirectiveUtil, Warning, TEXT("Controller or pawn has been destroyed while moving to actor. Aborting."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (FVector::Dist(CurrentLocation, LastCheckedLocation) < StuckThreshold)
|
||||
{
|
||||
UE_LOG(LogDirectiveUtil, Warning, TEXT("Controller is stuck while moving to actor. Aborting"));
|
||||
ExecuteCompleted(false);
|
||||
}
|
||||
|
||||
LastCheckedLocation = CurrentLocation;
|
||||
}
|
||||
|
||||
void UDirectiveUtilTask_MoveToActor::ExecuteCompleted(const bool bSuccess)
|
||||
{
|
||||
if (bHasCompleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
bHasCompleted = true;
|
||||
|
||||
UE_LOG(LogDirectiveUtil, Log, TEXT("Movement to actor completed. Success: %s."), bSuccess ? TEXT("true") : TEXT("false"));
|
||||
|
||||
if (Controller)
|
||||
{
|
||||
Controller->GetWorld()->GetTimerManager().ClearTimer(TimerHandle);
|
||||
Controller->GetWorld()->GetTimerManager().ClearTimer(StuckTimerHandle);
|
||||
}
|
||||
|
||||
Completed.Broadcast(bSuccess);
|
||||
|
||||
Controller = nullptr;
|
||||
Goal = nullptr;
|
||||
|
||||
SetReadyToDestroy();
|
||||
}
|
||||
Reference in New Issue
Block a user