Announcement

Collapse
No announcement yet.

BTTask_MoveToward - Asynchronous "Move To" Behavior Tree task

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    BTTask_MoveToward - Asynchronous "Move To" Behavior Tree task

    Out of the box, UE4 provides a MoveTo task that moves the character toward a location or actor. However, that node doesn't return until the character has reached the specified location. This causes the behavior tree to freeze until it reaches the final location. This can cause jerky movement.

    Almost every Behavior Tree tutorial I've seen seems to have to either work around this limitation, or just writes their own move to task. It seems silly for everybody to keep writing the same logic over and over, so I created a new task that works exactly like the built-in MoveTo task, only it returns immediately. Essentially, it makes the behavior asynchronous, since the tree continues execution immediately, which is the behavior people (and by "people", I mean "me" ) seem to need more often.

    Here is the code. Feel free to use it, modify it, or do anything you want to it without limitation. Improvements and comments welcome.

    You will have to make two changes to use this in your project. You'll have to add your project-specific API macro in the header file, and your project-specific project header file in the implementation file. Just look for the brackets and copy the values from another class in your project.

    BTW: Is there any way to make UE4 classes like this generic so they can just be dropped in to a project?

    BTTask_MoveToward.h
    Code:
    #pragma once
    
    #include "BehaviorTree/Tasks/BTTask_BlackboardBase.h"
    #include "BTTask_MoveToward.generated.h"
    
    /**
     * 
     */
    UCLASS(config=Game)
    class [YOURPROJECT_API_MACRO] UBTTask_MoveToward : public UBTTask_BlackboardBase
    {
    	GENERATED_UCLASS_BODY()
        
        UPROPERTY(config, Category=Node, EditAnywhere, meta=(ClampMin = "0.0"))
        float AcceptableRadius;
    
        UPROPERTY(Category=Node, EditAnywhere)
        uint32 bAllowStrafe : 1;
        
        UPROPERTY(Category=Node, EditAnywhere)
        TSubclassOf<class UNavigationQueryFilter> FilterClass;
        
    
        virtual EBTNodeResult::Type ExecuteTask(class UBehaviorTreeComponent* OwnerComp, uint8* NodeMemory) override;
        virtual FString GetStaticDescription() const override;
        
    #if WITH_EDITOR
        virtual FName GetNodeIconName() const override;
    #endif // WITH_EDITOR
    };

    BTTask_MoveToward.cpp
    Code:
    #include "[Your Project Include].h"
    #include "BTTask_MoveToward.h"
    #include "BehaviorTree/BehaviorTree.h"
    #include "BehaviorTree/BlackboardComponent.h"
    #include "BehaviorTree/Blackboard/BlackboardKeyType_Object.h"
    #include "BehaviorTree/Blackboard/BlackboardKeyType_Vector.h"
    #include "AIController.h"
    
    // This class is identical to UBTTask_MoveTo except that it returns immediately rather than waiting until the character has reached the specified destination
    //     This allows the behavior tree to continue executing and lets the character move smoothly without using a Simple Parallel node
    
    UBTTask_MoveToward::UBTTask_MoveToward(const class FPostConstructInitializeProperties& PCIP)
    	: Super(PCIP)
        , AcceptableRadius(50.f)
        , bAllowStrafe(false)
    {
        NodeName = "Move Toward";
        BlackboardKey.AddObjectFilter(this, AActor::StaticClass());
        BlackboardKey.AddVectorFilter(this);
    }
    
    EBTNodeResult::Type UBTTask_MoveToward::ExecuteTask(class UBehaviorTreeComponent* OwnerComp, uint8* NodeMemory)
    {
        AAIController* MyController = OwnerComp ? Cast<AAIController>(OwnerComp->GetOwner()) : NULL;
        const UBlackboardComponent* MyBlackboard = OwnerComp->GetBlackboardComponent();
    
        EPathFollowingRequestResult::Type RequestResult = EPathFollowingRequestResult::Failed;
    
        if (BlackboardKey.SelectedKeyType == UBlackboardKeyType_Object::StaticClass())
        {
            UObject* KeyValue = MyBlackboard->GetValueAsObject(BlackboardKey.GetSelectedKeyID());
            AActor* TargetActor = Cast<AActor>(KeyValue);
            if (TargetActor)
            {
                RequestResult = MyController->MoveToActor(TargetActor, AcceptableRadius, true, true, bAllowStrafe, FilterClass);
            }
        }
        else if (BlackboardKey.SelectedKeyType == UBlackboardKeyType_Vector::StaticClass())
        {
            const FVector TargetLocation = MyBlackboard->GetValueAsVector(BlackboardKey.GetSelectedKeyID());
            RequestResult = MyController->MoveToLocation(TargetLocation, AcceptableRadius, true, true, false, bAllowStrafe, FilterClass);
        }
    
    
        if (RequestResult == EPathFollowingRequestResult::Failed)
        {
            return EBTNodeResult::Failed;
        }
    
        return EBTNodeResult::Succeeded;
    
    }
    
    FString UBTTask_MoveToward::GetStaticDescription() const
    {
        return "Moves the character toward the specified point, returning immediately.";
    }
    
    #if WITH_EDITOR
    
    FName UBTTask_MoveToward::GetNodeIconName() const
    {
        return FName("BTEditor.Graph.BTNode.Task.MoveTo.Icon");
    }
    
    #endif	// WITH_EDITOR
Working...
X