How to create a UBTTAsk sub task in C++?

I have a melee character with some range, stored in MeleeComponent. I now want to create a task in behaviour tree (using blueprints) that would move to enemy with accepted radius of melee range.
Unfortunately, I cannot set accepted radius for MoveTo (UBTTask_MoveTo) task in the blueprints.

So, instead, I want to create my own task MoveToMelee,in C++, that would create a UBTTask_MoveTo, set the AcceptedRadius with my melee range, and launch it as a subtask: so that when the subtask finishes, it’s result is automatically propagated as a result of my MoveToMelee task. Is this possible?

the UBTNode::NewBTAITask function looks like what I need, but it works with AI tasks, not UBTTasks, and it seems that they can’t be used as UBTTasks.
There ai version of moveTo BTTask - AITask_MoveTo, but I don’t see how to use it as a subtask.

P.S blueprint solutions are also welcome.
P.S. one possible solution to this is to subclass MoveToMelee task from UBTTask_MoveTo directly, but this is ugly.

Seems like you don’t do that. Not with UBTTask at least. They were designed to only be executed internally by the behavior tree framework.

So if in your UBTTask you need to use the functionality of some other UBTTask, the only way would be to subclass from it - but this is very, very bad from design perspective, as this kind of thing needs composition, not inheritance.

But, in the case of MoveTo task - it actually uses AITask, which is more low level task that allowes for composition. It is very clumbersome to use it directly, but luckily our AIController handles that for us.

My solution was to use AIController::MoveTo() and AIController::StopMovement() inside my task.

This is probably a little late for people, but i had this exact issue myself, i want my AI to move to an attack range based on a blackboard key, in the end i subclassed the move to node bheaviour task, see below:

H:

class XXX_API UHFBTTask_MoveToRange : public UBTTask_MoveTo
{
	GENERATED_BODY()

public:

    UHFBTTask_MoveToRange();

    // The key for the range we will pass in as our acceptable radius (overrides the acceptable radius value!!)
    UPROPERTY(EditAnywhere, Category = "Node|HF")
    FBlackboardKeySelector AcceptableRangeKey;
	// The minimum additional offset to the range, positive numbers add to AcceptableRangeKey, negative deduct from AcceptableRangeKey
    UPROPERTY(EditAnywhere, Category = "Node|HF")
	float MinRangeOffset = 10.0f;
	// The Maximum additional offset to the range, positive numbers add to AcceptableRangeKey, negative deduct from AcceptableRangeKey
	UPROPERTY(EditAnywhere, Category = "Node|HF")
	float MaxRangeOffset = 30.0f;

protected:

	UPROPERTY()
    bool bIsInitialized = false;



    virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory) override;
};

Cpp:

UHFBTTask_MoveToRange::UHFBTTask_MoveToRange()
{
    NodeName = "Move To Range";
    AcceptableRangeKey.AddFloatFilter(this, GET_MEMBER_NAME_CHECKED(UHFBTTask_MoveToRange, AcceptableRangeKey));

	// Ensure the task node uses instance memory (means each node uses its own properties
	bCreateNodeInstance = true;
}
EBTNodeResult::Type UHFBTTask_MoveToRange::ExecuteTask(UBehaviorTreeComponent& OwnerComp, uint8* NodeMemory)
{
    AAIController* AIController = OwnerComp.GetAIOwner();
    UBlackboardComponent* BlackboardComp = OwnerComp.GetBlackboardComponent();
  
    if (AIController == nullptr || BlackboardComp == nullptr)
    {
        return EBTNodeResult::Failed;
    }

	if (!bIsInitialized)
	{
		AcceptableRadius += (BlackboardComp->GetValueAsFloat(AcceptableRangeKey.SelectedKeyName) + FMath::RandRange(MinRangeOffset, MaxRangeOffset));
		//GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::White, FString::Printf(TEXT("AcceptableRadius: %f"), AcceptableRadius));
		bIsInitialized = true;
	}

    return Super::ExecuteTask(OwnerComp, NodeMemory);
}