Getting AI to interrupt/abort Behavior Tree MoveTo Task

I’ve tried making sense of this with other similar topics with no luck.

How would I get an AI character to abort its current MoveTo Task? The goal is to differentiate between moving away from the player character or moving to a random waypoint on the map. While the conditions for the tree and tasks work, the task MoveTo, which is the default MoveTo task of BT, finishes executing its movement before running the tree again. What I want is for the AI to STOP its current MoveTo task and stop moving to its destination if a condition is satisfied and run the other MoveTo task that is in the tree. As in, if the player’s location is within an AI conecheck, the AI will run the moveTo task to move away from the player instead of continuing on its way to its initial waypoint.

Below is a pic for reference. I’ve read that decorators are only checked before a task is executed and after, not during. Would I need to make a custom moveTo Task for this instead? Time Limit was an attempt to interrupt but it doesn’t work the way I expected. Thanks

You will need a different way than a decorator to constantly check if the player is close to your AI. What you could try is:

1 - Turn your first MoveTo node into a Simple Parallel

2 - Make your MoveTo the main task of the parallel

3 - Make the background task of the parallel a selector with no children that loops forever and has a cone check that aborts both.

I don’t know if having no children under the selector would be problematic, but it should be easy to try.

Thanks for the advice, however, now it doesn’t work because apparently simple parallel won’t run the subtree assigned to it. Here is my new approach.

The subtree is meant to calculate the TargetPoint to moveTo through the selector, which won’t run the task of finding a random waypoint if the giant is close. However, the tree is stuck at MoveTo Task without running the subtree(at least it didn’t look like it while I was simulating it.) What else do you think could be the problem?

Also I tried doing your initial method of looping cone check over the selector in the subtree, but that ended up freezing the engine itself in an infinite loop

oh that proximity check was there because the cone check wasn’t working. Instead of a cone check, we tried a distance check but once the cone check worked we left it there. I took it out.

The Cone originates from the Pawn Location, with its mouth calculated from its Location + its Forward Vector at a half angle of 45 degrees.

Now, the cone check is there to check if the PAWN is FACING towards the giant in its FORWARD vector. If it did(which means the pawn saw the giant), store a value in the BB that moveTo will use to move away. The freezing is still there with the simple parallel AT the cone check conditional.

This BT is the one that is freezing the engine up with what I think is an infinite loop.

Overall, I don’t think this overall approach using simple parallel is the right one so far. Gonna be repetitive but the goal is to interrupt moveTo for moving to a waypoint(which is the 2nd subtree of the initial looped selector) with a moveTo for moving away from the giant when the pawn is facing it through its forward direction. After the 1st moveTo is done, if the giant isn’t in the view of the pawn, return to moving to waypoints.

Nope. Still freezes, and still didn’t try the subtree to the right. How does Simple Parallel operate anyway? From what I read, it runs the main task and if set to immediate, it’ll abort without running the subtree. I don’t really understand that the subtree runs under that setting.

I’m not sure that putting that parallel at the root is a good idea. I meant you should put it in place of your original first MoveTo node in your original tree structure. But I’m assuming you tried that and it didn’t work, which is odd.

Besides doing a cone check (I’ve never used that node), is there another event in your design that could be used to trigger the MoveTo abort? You seem to be checking for proximity to the giant, but why the cone check? Should it only abort if the villager is facing the giant, or can it just abort whenever the giant is within X units of the villager?

What if you put a Wait node as the child of the Selector that has the looping Cone Check, and put the wait time at like 0.5 seconds or something?

The editor is probably crashing because running the cone check infinitely every frame, but if you put a Wait node, technically it should only loop once the Wait is finished, which is like a ghetto Tick node.

It’s only supposed to abort once the main task has finished.

Well in this case let’s forget the parallel idea…

The only other solution I see right now is a little more involved, since it would require you making your own custom MoveTo Task. That Custom MoveTo node would need to use the AI Move To function, since that is a latent function that will only “finish” once you reach your goal (or fail to), ensuring that this task will only complete once youve finished moving.

When this custom MoveTo task is first triggered, you would need to save a reference to itself into your blackboard (as an object), so that you can use that reference to know which MoveTo task to abort.

Once you’ve created the custom MoveTo, change that Parallel node in your latest screenshot into a Selector, getting rid of the Loop/Cone Check child and only keeping the custom MoveTo as a child of that new selector, and then putting a new Service on the Selector.

The problem now is you need to do that cone check manually in the service, and I’m not sure how to do that.

But just to see if that solution is on the right track, you could forget the cone check for now and just have the service check if the giant is in range using a Sphere Overlap Actors on the Event Receive Tick in the service, and check if the giant is part of those overlapped actors.

If he is, you then grab the reference to your MoveTo task from your blackboard (while in the service), and call Finish Abort on it.

This all seems overly complicated for a task that should be fairly simple. I’ve only been working with BTs for a week or two, but I too have been annoyed at the fact that the decorator observers don’t have the ability to run on some sort of tick.

Will try, but why the heck does this make sense?

No, it’s not. The BTTask_MoveTo is an integral part of BT workflow, while AIMoveTo was never intended to be used inside BT tasks.

I found it in another post related to stopping moveTo

Decorators check the validity in the beginning but it will get notified of the change and thus aborting if necessary.

Create a BlackBoard variable that tells your unit to execute the “run away from the giant” branch. This could be a boolean or maybe the object(Giant) to run away from. Then you can add a “Blackboard Based Condition” decorator on your Sequence, allow it down the branch only when the condition is met and let it abort on condition change.

You probably also need to create a Service to check if a giant exists nearby and alter the variable.

While that was something I initially thought up and attempted, it ultimately did not meet what we expect because it still has the issue of MoveTo MOVING to its Target Point completely to the end first before executing the tree(and service and condition checks) again.

My partner ended up with an idea to get an array of path points between the pawn location and the target to move to, but while its movement is rigid because its checking points to move to after a few steps, it works because the current moveTo of the pawn is interrupted in favor of the other. Thanks though, but not what we want.

That is odd, because I have this basic setup and my unit stops if it finds an enemy while moving to the destination.

Did your service by any chance change the blackboard key for destination?
Maybe instead of a boolean, I can try something like that

My service checks for possible target and sets the “Enemy” BB variable when one gets within the unit attack range. This is change, is noticed and notifies the decorator which cancels its own branch(self). I don’t change the Destination because I want the unit to, after finishing the Attack task and clearing the Enemy variable, to move from its current location to the Destination.

Also, on your Service you can just grab the distance vector(target location - self location) and do a “Vector Length Squared” then check if its less than you Max squared. It would simplify it a bit.

In your BT the condition should be set to abort “lower”, this way when the unit is in the right branch(the lower priority branch) and the variable get set, it will notify the decorator and abort. After this, it will restart from the root and this time it will pass the decorator, just remember to clear the variable when the giant is no longer an issue, so that he can resume going to the lower priority branch.

Somehow, even changing the abort condition to lower, won’t interrupt the pawn from running the lower branch’s moveTo completely before restarting the BT even when I follow closely to it as a pawn. Once it reaches its waypoint, the tree resets then goes for the left branch.

That is odd, have you confirmed in debug that the variable is set while in the lower branch? Try simplifying the tree by unlinking most nodes for testing purposes. I tried with the BT on the img and it worked. After the service updated the enemy, the lower branch was aborted.

I think ultimately, since we cannot get the desired movement we want with Behavior Trees, we will switch to implementing steering behaviors for movement. Thanks for the advice though

This maybe a bit specific to my project, but I have had a lot of success with using the AI Controller’s “Stop Movement” function. It seems to terminate Move-To, and I simply start whatever new actions I wanted to perform right after calling this function. You can find the function by pulli!ng from the controller pin in any AI specific event in Behaviour Trees. “Basic_Influ_Blue” is the AI controlled Character. This task occurs at a higher priority than the move to task, shutting down the Move-To while its decorator aborts lower, so that it doesn’t start again until bAllowedToMove is set to true.

The only caveat is that this function stops ALL movement, so you would need to be okay with a full stop before behaviour changes.

Cheers.

My solution:

  • You have to abort the MoveTo with StopMevement on the AIController.
  • You have to use Parallel Tasks during a MoveTo
  • I also did a charge up based aggro check. After 2 seconds of seeing the player the ‘charge’ hits 100% and starts the move.

I have a TargetActor (the player) and a TargetLocation (the last known location of the player)

Had to write some blueprint Tasks and Decorators

  • ClearTargetActor clears the blackboard TargetActor
  • TDLAddCharge adds charge time delta time to the NPC AI AggroCharge
  • TDL Is Charged is a decorator to tell if AggroCharge is greater than or equal to 100
  • Clear Goals is a Task to clear TargetActor and TargetLocation and DesiredHeaddingFlag
  • Anim sets a desired anomation in the Animation Blueprint

Rather small to read. More detail below.

How charge up is done.

Then you move to the TargetActor or TargetLocation. If EQS loses sight of the player (Survivor) then fall back to use the TargetLocation.

Details

Too many picture. The StopMveTo just calls StopMovement on the NPC’s AIController.