From 3066dbd7e151f6191d224000b0f7c6c429e1d0b6 Mon Sep 17 00:00:00 2001 From: BrendanLoBuglio Date: Tue, 15 Jan 2019 15:54:56 -0800 Subject: [PATCH 1/2] First pass on BTTask_FlyTo that accepts Actor parameters --- .../Classes/BehaviorTree/BTTask_FlyTo.h | 7 +- .../Private/BehaviorTree/BTTask_FlyTo.cpp | 74 ++++++++++++++----- 2 files changed, 63 insertions(+), 18 deletions(-) diff --git a/Source/DonAINavigation/Classes/BehaviorTree/BTTask_FlyTo.h b/Source/DonAINavigation/Classes/BehaviorTree/BTTask_FlyTo.h index 78fcabd..48d2ea2 100644 --- a/Source/DonAINavigation/Classes/BehaviorTree/BTTask_FlyTo.h +++ b/Source/DonAINavigation/Classes/BehaviorTree/BTTask_FlyTo.h @@ -61,6 +61,8 @@ struct FBT_FlyToTarget FVector TargetLocation; + AActor* TargetActor; + FDelegateHandle BBObserverDelegateHandle; uint32 bTargetLocationChanged : 1; @@ -73,6 +75,7 @@ struct FBT_FlyToTarget Metadata = FBT_FlyToTarget_Metadata(); bSolutionInvalidatedByDynamicObstacle = false; bTargetLocationChanged = false; + TargetActor = nullptr; } }; @@ -105,7 +108,7 @@ class UBTTask_FlyTo : public UBTTaskNode // Behavior Tree Input: UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "DoN Navigation") - FBlackboardKeySelector FlightLocationKey; + FBlackboardKeySelector FlightGoalKey; /* Optional: Useful in somecases where you want failure or success of a task to automatically update a particular blackboard key*/ UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "DoN Navigation") @@ -165,6 +168,8 @@ class UBTTask_FlyTo : public UBTTaskNode void TickPathNavigation(UBehaviorTreeComponent& OwnerComp, FBT_FlyToTarget* MyMemory, float DeltaSeconds); + bool CheckTargetMoved(FBT_FlyToTarget* MyMemory); + virtual void OnTaskFinished(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, EBTNodeResult::Type TaskResult) override; virtual bool TeleportAndExit(UBehaviorTreeComponent& OwnerComp, bool bWrapUpLatentTask = true); diff --git a/Source/DonAINavigation/Private/BehaviorTree/BTTask_FlyTo.cpp b/Source/DonAINavigation/Private/BehaviorTree/BTTask_FlyTo.cpp index fd1e5a8..8018989 100644 --- a/Source/DonAINavigation/Private/BehaviorTree/BTTask_FlyTo.cpp +++ b/Source/DonAINavigation/Private/BehaviorTree/BTTask_FlyTo.cpp @@ -33,11 +33,12 @@ UBTTask_FlyTo::UBTTask_FlyTo(const FObjectInitializer& ObjectInitializer) NodeName = "Fly To"; bNotifyTick = true; - FlightLocationKey.AddVectorFilter(this, GET_MEMBER_NAME_CHECKED(UBTTask_FlyTo, FlightLocationKey)); - FlightResultKey.AddBoolFilter(this, GET_MEMBER_NAME_CHECKED(UBTTask_FlyTo, FlightResultKey)); + FlightGoalKey.AddObjectFilter(this, GET_MEMBER_NAME_CHECKED(UBTTask_FlyTo, FlightGoalKey), AActor::StaticClass()); + FlightGoalKey.AddVectorFilter(this, GET_MEMBER_NAME_CHECKED(UBTTask_FlyTo, FlightGoalKey)); + FlightResultKey.AddBoolFilter(this, GET_MEMBER_NAME_CHECKED(UBTTask_FlyTo, FlightResultKey)); KeyToFlipFlopWhenTaskExits.AddBoolFilter(this, GET_MEMBER_NAME_CHECKED(UBTTask_FlyTo, KeyToFlipFlopWhenTaskExits)); - FlightLocationKey.AllowNoneAsValue(true); + FlightGoalKey.AllowNoneAsValue(true); FlightResultKey.AllowNoneAsValue(true); KeyToFlipFlopWhenTaskExits.AllowNoneAsValue(true); } @@ -50,7 +51,7 @@ void UBTTask_FlyTo::InitializeFromAsset(UBehaviorTree& Asset) if (!blackboard) return; - FlightLocationKey.ResolveSelectedKey(*blackboard); + FlightGoalKey.ResolveSelectedKey(*blackboard); } EBTNodeResult::Type UBTTask_FlyTo::ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) @@ -66,9 +67,9 @@ EBTNodeResult::Type UBTTask_FlyTo::ExecuteTask(UBehaviorTreeComponent& OwnerComp if (myMemory->BBObserverDelegateHandle.IsValid()) { UE_VLOG(OwnerComp.GetAIOwner(), LogBehaviorTree, Warning, TEXT("UBTTask_MoveTo::ExecuteTask \'%s\' Old BBObserverDelegateHandle is still valid! Removing old Observer."), *GetNodeName()); - BlackboardComp->UnregisterObserver(FlightLocationKey.GetSelectedKeyID(), myMemory->BBObserverDelegateHandle); + BlackboardComp->UnregisterObserver(FlightGoalKey.GetSelectedKeyID(), myMemory->BBObserverDelegateHandle); } - myMemory->BBObserverDelegateHandle = BlackboardComp->RegisterObserver(FlightLocationKey.GetSelectedKeyID(), this, FOnBlackboardChangeNotification::CreateUObject(this, &UBTTask_FlyTo::OnBlackboardValueChange)); + myMemory->BBObserverDelegateHandle = BlackboardComp->RegisterObserver(FlightGoalKey.GetSelectedKeyID(), this, FOnBlackboardChangeNotification::CreateUObject(this, &UBTTask_FlyTo::OnBlackboardValueChange)); } } @@ -101,12 +102,12 @@ EBTNodeResult::Type UBTTask_FlyTo::SchedulePathfindingRequest(UBehaviorTreeCompo } // Validate blackboard key data: - if(FlightLocationKey.SelectedKeyType != UBlackboardKeyType_Vector::StaticClass()) + if(FlightGoalKey.SelectedKeyType != UBlackboardKeyType_Vector::StaticClass() && FlightGoalKey.SelectedKeyType != UBlackboardKeyType_Object::StaticClass()) { - UE_LOG(DoNNavigationLog, Log, TEXT("Invalid FlightLocationKey. Expected Vector type, found %s"), *(FlightLocationKey.SelectedKeyType ? FlightLocationKey.SelectedKeyType->GetName() : FString("?"))); + UE_LOG(DoNNavigationLog, Log, TEXT("Invalid FlightGoalKey. Expected Vector or Actor Object type, found %s"), *(FlightGoalKey.SelectedKeyType ? FlightGoalKey.SelectedKeyType->GetName() : FString("?"))); return HandleTaskFailure(OwnerComp, NodeMemory, blackboard); } - + // Prepare input: myMemory->Reset(); myMemory->Metadata.ActiveInstanceIdx = OwnerComp.GetActiveInstanceIdx(); @@ -115,8 +116,30 @@ EBTNodeResult::Type UBTTask_FlyTo::SchedulePathfindingRequest(UBehaviorTreeCompo myMemory->QueryParams.CustomDelegatePayload = &myMemory->Metadata; myMemory->bIsANavigator = pawn->GetClass()->ImplementsInterface(UDonNavigator::StaticClass()); - FVector flightDestination = blackboard->GetValueAsVector(FlightLocationKey.SelectedKeyName); - myMemory->TargetLocation = flightDestination; + FVector flightDestination; + // Prepare location as Vector: + if (FlightGoalKey.SelectedKeyType == UBlackboardKeyType_Vector::StaticClass()) + { + flightDestination = blackboard->GetValueAsVector(FlightGoalKey.SelectedKeyName); + myMemory->TargetLocation = flightDestination; + } + // Prepare location as Actor: + else + { + auto keyValue = blackboard->GetValueAsObject(FlightGoalKey.SelectedKeyName); + auto targetActor = Cast(keyValue); + if (targetActor) + { + flightDestination = targetActor->GetActorLocation(); + myMemory->TargetLocation = flightDestination; + myMemory->TargetActor = targetActor; + } + else + { + UE_LOG(DoNNavigationLog, Log, TEXT("Invalid FlightGoalKey tried to target an actor, but BB key %s was empty or not an actor"), *FlightGoalKey.SelectedKeyName.ToString()); + return HandleTaskFailure(OwnerComp, NodeMemory, blackboard); + } + } // Bind result notification delegate: FDoNNavigationResultHandler resultHandler; @@ -243,9 +266,19 @@ void UBTTask_FlyTo::TickTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemor APawn* pawn = OwnerComp.GetAIOwner()->GetPawn(); NavigationManager = UDonNavigationHelper::DonNavigationManagerForActor(pawn); + // If I'm still waiting to get a path to my target, just return: if (EDonNavigationQueryStatus::InProgress == myMemory->QueryResults.QueryStatus) return; + // If my actor target has moved beyond the threshold, I should recalculate my path towards it: + if (this->CheckTargetMoved(myMemory)) + { + auto ownerComp = myMemory->Metadata.OwnerComp.Get(); + AbortPathfindingRequest(*ownerComp, (uint8*)myMemory); + SchedulePathfindingRequest(*ownerComp, (uint8*)myMemory); + return; + } + switch (myMemory->QueryResults.QueryStatus) { @@ -294,6 +327,12 @@ void UBTTask_FlyTo::TickTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemor } } +bool UBTTask_FlyTo::CheckTargetMoved(FBT_FlyToTarget* MyMemory) +{ + // If my target is an actor actor and has moved out of the path tolerance, I should recalculate a new path towards it: + return MyMemory->TargetActor != nullptr && FVector::DistSquared(MyMemory->TargetLocation, MyMemory->TargetActor->GetActorLocation()) >= FMath::Square(this->RecalculatePathTolerance); +} + void UBTTask_FlyTo::TickPathNavigation(UBehaviorTreeComponent& OwnerComp, FBT_FlyToTarget* MyMemory, float DeltaSeconds) { const auto& queryResults = MyMemory->QueryResults; @@ -389,7 +428,7 @@ void UBTTask_FlyTo::OnTaskFinished(UBehaviorTreeComponent& OwnerComp, uint8* Nod UBlackboardComponent* BlackboardComp = OwnerComp.GetBlackboardComponent(); if (ensure(BlackboardComp) && myMemory->BBObserverDelegateHandle.IsValid()) - BlackboardComp->UnregisterObserver(FlightLocationKey.GetSelectedKeyID(), myMemory->BBObserverDelegateHandle); + BlackboardComp->UnregisterObserver(FlightGoalKey.GetSelectedKeyID(), myMemory->BBObserverDelegateHandle); myMemory->BBObserverDelegateHandle.Reset(); @@ -458,9 +497,10 @@ EBlackboardNotificationResult UBTTask_FlyTo::OnBlackboardValueChange(const UBlac return EBlackboardNotificationResult::RemoveObserver; } - if (myMemory != nullptr) + // If we're not pursuing an actor and the value of our our target location key changed, mark our location as having changed: + if (myMemory != nullptr && myMemory->TargetActor == nullptr) { - const FVector flightDestination = Blackboard.GetValueAsVector(FlightLocationKey.SelectedKeyName); + const FVector flightDestination = Blackboard.GetValueAsVector(FlightGoalKey.SelectedKeyName); if (!myMemory->TargetLocation.Equals(flightDestination, RecalculatePathTolerance)) myMemory->bTargetLocationChanged = true; @@ -489,7 +529,7 @@ FString UBTTask_FlyTo::GetStaticDescription() const { FString ReturnDesc = Super::GetStaticDescription(); - ReturnDesc += FString::Printf(TEXT("\n%s: %s \n"), *GET_MEMBER_NAME_CHECKED(UBTTask_FlyTo, FlightLocationKey).ToString(), *FlightLocationKey.SelectedKeyName.ToString()); + ReturnDesc += FString::Printf(TEXT("\n%s: %s \n"), *GET_MEMBER_NAME_CHECKED(UBTTask_FlyTo, FlightGoalKey).ToString(), *FlightGoalKey.SelectedKeyName.ToString()); ReturnDesc += FString("\nDebug Visualization:"); ReturnDesc += FString::Printf(TEXT("Raw Path: %d \n"), DebugParams.VisualizeRawPath); ReturnDesc += FString::Printf(TEXT("Optimized Path: %d \n"), DebugParams.VisualizeOptimizedPath); @@ -524,13 +564,13 @@ bool UBTTask_FlyTo::TeleportAndExit(UBehaviorTreeComponent& OwnerComp, bool bWra if (blackboard) { - FVector flightDestination = blackboard->GetValueAsVector(FlightLocationKey.SelectedKeyName); + FVector flightDestination = blackboard->GetValueAsVector(FlightGoalKey.SelectedKeyName); NavigationManager = UDonNavigationHelper::DonNavigationManagerForActor(pawn); bool bLocationValid = !NavigationManager->IsLocationBeneathLandscape(flightDestination); if(bLocationValid) { - FVector flightDestination = blackboard->GetValueAsVector(FlightLocationKey.SelectedKeyName); + FVector flightDestination = blackboard->GetValueAsVector(FlightGoalKey.SelectedKeyName); pawn->SetActorLocation(flightDestination, false); GEngine->AddOnScreenDebugMessage(-1, 15.f, FColor::White, FString::Printf(TEXT("%s teleported, being unable to find pathfind aerially!"), pawn ? *pawn->GetName() : *FString(""))); bTeleportSuccess = true; From f215ac40d1bef8e879a41d4e61e0117cd166d80f Mon Sep 17 00:00:00 2001 From: BrendanLoBuglio Date: Tue, 15 Jan 2019 16:12:02 -0800 Subject: [PATCH 2/2] Fixed a bug where DoN pawns with actor targets would get stuck if they were rapidly recalculating their actor target --- .../Classes/BehaviorTree/BTTask_FlyTo.h | 6 +++++- .../Private/BehaviorTree/BTTask_FlyTo.cpp | 12 +++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/Source/DonAINavigation/Classes/BehaviorTree/BTTask_FlyTo.h b/Source/DonAINavigation/Classes/BehaviorTree/BTTask_FlyTo.h index 48d2ea2..c251ab6 100644 --- a/Source/DonAINavigation/Classes/BehaviorTree/BTTask_FlyTo.h +++ b/Source/DonAINavigation/Classes/BehaviorTree/BTTask_FlyTo.h @@ -63,12 +63,16 @@ struct FBT_FlyToTarget AActor* TargetActor; + /* Whether this is a repath due to an actor target having moved */ + bool isMovingTargetRepath = false; + FDelegateHandle BBObserverDelegateHandle; uint32 bTargetLocationChanged : 1; void Reset() { + isMovingTargetRepath = false; solutionTraversalIndex = 0; QueryResults = FDoNNavigationQueryData(); QueryParams = FDoNNavigationQueryParams(); @@ -162,7 +166,7 @@ class UBTTask_FlyTo : public UBTTaskNode FBT_FlyToTarget* TaskMemoryFromGenericPayload(void* GenericPayload); - EBTNodeResult::Type SchedulePathfindingRequest(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory); + EBTNodeResult::Type SchedulePathfindingRequest(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, bool isMovingTargetRepath = false); void AbortPathfindingRequest(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory); diff --git a/Source/DonAINavigation/Private/BehaviorTree/BTTask_FlyTo.cpp b/Source/DonAINavigation/Private/BehaviorTree/BTTask_FlyTo.cpp index 8018989..881da33 100644 --- a/Source/DonAINavigation/Private/BehaviorTree/BTTask_FlyTo.cpp +++ b/Source/DonAINavigation/Private/BehaviorTree/BTTask_FlyTo.cpp @@ -76,7 +76,7 @@ EBTNodeResult::Type UBTTask_FlyTo::ExecuteTask(UBehaviorTreeComponent& OwnerComp return NodeResult; } -EBTNodeResult::Type UBTTask_FlyTo::SchedulePathfindingRequest(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) +EBTNodeResult::Type UBTTask_FlyTo::SchedulePathfindingRequest(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory, bool isMovingTargetRepath) { auto pawn = OwnerComp.GetAIOwner()->GetPawn(); auto myMemory = (FBT_FlyToTarget*)NodeMemory; @@ -115,6 +115,7 @@ EBTNodeResult::Type UBTTask_FlyTo::SchedulePathfindingRequest(UBehaviorTreeCompo myMemory->QueryParams = QueryParams; myMemory->QueryParams.CustomDelegatePayload = &myMemory->Metadata; myMemory->bIsANavigator = pawn->GetClass()->ImplementsInterface(UDonNavigator::StaticClass()); + myMemory->isMovingTargetRepath = isMovingTargetRepath; FVector flightDestination; // Prepare location as Vector: @@ -233,6 +234,11 @@ void UBTTask_FlyTo::Pathfinding_OnFinish(const FDoNNavigationQueryData& Data) return; } + // If we're a moving target repath, then skip the first index because the first index will generally just be + // from the pawn start to the closest nav voxel and may cause us to move backwards. + if (myMemory->isMovingTargetRepath && Data.PathSolutionOptimized.Num() >= 2) + myMemory->solutionTraversalIndex = 1; + // Inform pawn owner that we're about to start locomotion! if (myMemory->bIsANavigator) { @@ -275,7 +281,7 @@ void UBTTask_FlyTo::TickTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemor { auto ownerComp = myMemory->Metadata.OwnerComp.Get(); AbortPathfindingRequest(*ownerComp, (uint8*)myMemory); - SchedulePathfindingRequest(*ownerComp, (uint8*)myMemory); + SchedulePathfindingRequest(*ownerComp, (uint8*)myMemory, true); return; } @@ -330,7 +336,7 @@ void UBTTask_FlyTo::TickTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemor bool UBTTask_FlyTo::CheckTargetMoved(FBT_FlyToTarget* MyMemory) { // If my target is an actor actor and has moved out of the path tolerance, I should recalculate a new path towards it: - return MyMemory->TargetActor != nullptr && FVector::DistSquared(MyMemory->TargetLocation, MyMemory->TargetActor->GetActorLocation()) >= FMath::Square(this->RecalculatePathTolerance); + return MyMemory->TargetActor != nullptr && bRecalcPathOnDestinationChanged && FVector::DistSquared(MyMemory->TargetLocation, MyMemory->TargetActor->GetActorLocation()) >= FMath::Square(this->RecalculatePathTolerance); } void UBTTask_FlyTo::TickPathNavigation(UBehaviorTreeComponent& OwnerComp, FBT_FlyToTarget* MyMemory, float DeltaSeconds)