How to abort Behavior Tree task based on State change in C++?

I’m trying to solve this seemingly simple issue, which became a headache of mine. I check mostly all the forum question for this, but most of the time I found that people set the LockAILogic false in a blueprint. Now, this is not feasible for me, because I write the tasks in C++.

What I found out, is the MoveTo node in behaviour tree is not cancellable. Which is fine. So I made my own. But that is probably more complicated than it should be, and it still doesn’t produce the abort correctly.

The PedestrianBehaviorTree.

The decorator I use to check the different states.

State change when the NPC saw the player. BlackboardAccessor is an interface I made as wrapper so I don’t have to rely on string keys.

Custom MoveToLocation, where I save the movement result code and check it when the OnMoveComplete is finished (This is something I would really love to avoid). It can work perfectly well with a single Pedestrian, but cause a lot of other issues when multiple is presented, which makes me question that a Task is instanced to all running NPC-s or exists as one? Potentially keeping the task alive by keeping references are also not a good idea, but this is all I got. It is a mess right now, but this is also my 20th reiteration of the process, so right now I couldn’t care less. Still no abort, tho! I used StopMovement() which is worked, but only for the movement. It would not help if Pedestrian is in the Waiting state or something else. Without the StopMovement() the NPC goes to the location and after that it will trigger a new state.

I would like to find a solution where I set the BehaviorState and it triggers a re-evaluation of the tree aborting the currently running task, and find the next valid node, without constantly ticking around.

  • What am I missing?
  • My behaviour tree is badly structured and missing some service or something?
  • The custom move location is the problem? I would like to avoid the blueprints if that’s possible.
  • Is there a better way to handle custom move to? Nor MoveToLocation and MoveToActor worked as it should. I mean, it works because it moves to a place. But It needs to be completed somehow and send a Success from this task.
  • Is this problem even solvable with C++? (This last one is rhetoric… no need to answer that. )

Also before this approach I made an FSM completely from scratch from code, it worked fine, but was so complicated and huge, that it became a nightmare to maintain.

Thank you in advance!

So I solved half of the issues with the movement by using node instancing. Still no aborting.

UBTT_MoveToLocation::UBTT_MoveToLocation()

  • This is the constructor for the UBTT_MoveToLocation task. It sets the bCreateNodeInstance flag to true, ensuring that a unique instance of this task is created for each behavior tree execution.

UBTT_MoveToLocation::ExecuteTask

  • Initiates the task of moving an AI-controlled character to a specified location.
  • Retrieves the movement target location from the blackboard using an IBlackboardAccessor.
  • Sets up a move request with the desired goal location and movement settings (e.g., acceptance radius, strafing).
  • Registers a delegate to handle movement completion via OnMoveCompletedCallback.
  • Returns a result indicating whether the movement request failed, succeeded immediately, or is in progress

UBTT_MoveToLocation::OnMoveCompletedCallback

  • Called when the AI-controlled character completes its movement.
  • Validates the callback by ensuring the correct request ID and a valid memory reference.
  • Removes the movement completion delegate and resets the associated handle.
  • Determines the task result (Succeeded, Failed, or Aborted) based on the movement outcome and finishes the latent task in the behavior tree.

UBTT_MoveToLocation::GetInstanceMemorySize

  • Returns the size of the memory block required by this task for storing runtime data (FBTTMoveToLocationMemory).

UBTT_MoveToLocation::OnTaskFinished

  • Cleans up the task’s resources when it finishes, regardless of the result.
  • Ensures the movement completion delegate is removed and the handle is reset to avoid dangling references.
  • Calls the superclass implementation to handle any additional cleanup or logic.

At least this is working now…

(post deleted by author)

So, digging inside the UBehaviorTreeComponent code, I managed to find a function called RequestBranchEvaluation. Using it on my own wrapper, where I set the state, it aborts the current task. The only problem remained is that it does not abort immediately.

Ok, so it didn’t immediately abort, because I had a Wait task after Move to Location and in the RequestEvaluation I sent a Success, which triggered the Wait and after it continued the execution.

So the solution for me was:

The only downside of this is the abortable element needs to be the last task. So it probably has some limitations. Need to dig a bit deeper later on.