Evaluation logic question!

RedStage

New member
Hello! I'm having trouble understanding the evaluation logic.

In my example tree in the attachments I have a Selector which has two children; a Sequence and a plain old Idle. Conditional1 and 2 are Compare SharedBool Conditionals that compare the variables Bool1 and Bool2 respectively, and they will return Success if the bool variable is true. Action1 and 2 are Log actions.If we start the game with both Bool1 and 2 as false, the Selector will first try the leftmost Sequence, which will return Failure because of Conditional1. The Selector will then run the Idle and, well, idle.

Now, if we at this point set Bool1 as true, Sequence will cause a Conditional Abort and the Sequence will start to run. The first log task, Action1, is run as well as the Wait in the middle. Then we get to Conditional2. It will check Bool2, which is still false and return Failure. After this the Selector will again go to Idle.

This much I understand. But I would've expected the Conditional1 to cause another Conditional abort on the next frame since Bool1 is still true and the Conditional1 is a Success. Instead we will not evaluate the Sequence at all anymore, but will stay in Idle, until either Bool1 or Bool2 value changes and one of the Conditionals returns something else than their previous value. Why is this?

There is a common need in our project to cancel out of lower priority branches that may have long-lasting actions or sequences, that we need to immediately come out of and back to a higher priority branch whenever the higher priority branch becomes available again (re-evaluation of some condition or conditions): conditional aborts initially SEEM to be just the solution to this, but just as in this case they will start to fail after the higher priority branch they abort into fails even once.

The abstract minimal repro case / explanation of the issue could be many things, but in our case the first gameplay use-cases where the Behavior Tree just seems to not work as expected and needed was when there's a high-priority sequence like "a condition for distance to target, an attack action that can hit or miss which, if successful, is followed by something triggered by the hit like damage or a Timeline", and a low-priority branch like idling or wandering about. If the attack sequence returns a failure (attack missing mid-sequence), the low-priority Idle branch goes on and the attack branch will only ever conditionally abort and resume if the distance-check condition changes values back to failure and success again; remaining close to the target will lock the BT into Idle, even though the distance-check condition would be successful and conditional abort could be reasonably expected to cause abort to retrying the attack as long as we stay in range.

Any insight would be highly appreciated!

1615918815521.png
 
But I would've expected the Conditional1 to cause another Conditional abort on the next frame since Bool1 is still true and the Conditional1 is a Success. Instead we will not evaluate the Sequence at all anymore, but will stay in Idle, until either Bool1 or Bool2 value changes and one of the Conditionals returns something else than their previous value. Why is this?
Can you post a screenshot of what the tree looks like when it is active and Conditional1 has returned success?
 
Thank you for answering! Here is a pic of the tree when it's running! At this point we are just running Idle since the Sequence has already failed at Conditional2, and will not reevaluate unless Conditional1 or Conditional2 return something else than their current TaskStatus. Our expectations were that at game start after the Sequence fails at Conditional2, if Conditional1 is still a Success, the branch should cause a Conditional Abort the next frame and Action1 and Wait should run again.

1615982797244.png
 
Thanks. The conditional aborts will only trigger after the conditional task has been registered. If Conditional1 returns false the very first frame it will register itself to be checked the next frame. At this time the system doesn't know anything about the Conditional2 task. The next frame if Conditional1 returns true then it will cause an abort and run until Conditional2. At this point the system knows of Conditional2 so the next time that changes status (from false to true in your case) it will trigger an abort. During this time if Conditional1 changes back to false it will also trigger an abort.
 
After Conditional2 returns FAILURE, why do we go to Idle and stay there forever, regardless of Conditional1 returning SUCCESS every frame?
If this were an enemy AI
  • Conditional1 = Can I reach Player?
  • Conditional2 = Did attack hit (SUCCESS) or did player dodge the attack (FAILURE)
  • So when an attack misses (player sidesteps, but never leaves enemy's reach) the enemy would go Idle and would never attack again until the player goes far and returns. We NEED the conditional abort to fire us out of Idle and back to the left-side Sequence because the Conditional1 returns SUCCESS, without it being changed to false between re-evaluations: if this is a range or "can see player" type condition the AI would stay stuck in Idle even if the player is still in view and range.
The abstract/simple repro in the example tree behaves exactly as any of our real-world AI use-cases like this attack example, and there seems to be no answers that help with these two questions, kind of two different ways to phrase the same problem:
  • How to interrupt a sequence or a long action if a condition fails? Idle action needs to abort out back to Left-Side Sequence if Conditional1 re-evaluates as SUCCESS, but doesn't if Conditional2 has previously returned FAILURE.
  • How to return to a higher-priority Selector child-branch if a condition returns SUCCESS? Idle keeps playing forever if Conditional1 returns SUCCESS and Conditional2 returns FAILURE, even though we can witness by adding Debug.Log call to CompareBoolean.OnUpdate() that Conditional1 runs OnUpdate method and returns SUCCESS every frame while Idle is running.
 
Last edited:
After Conditional2 returns FAILURE, why do we go to Idle and stay there forever, regardless of Conditional1 returning SUCCESS every frame?
Conditional aborts trigger when the conditional task changes the TaskStatus. This means that it can trigger from success to failure or failure to success. Since Conditional2 continues to return failure the conditional abort never occurs. The top sequence task returns failure because of Conditional2 and the idle continues to play.

For more control over when a branch is aborted I recommend using the Interrupt/Perform Interruption tasks.
 
We used the Interrupt/Perform Interruption as a workaround with our problem. If we exit the Sequence because our Conditional2 fails, we have a lower priority extra Sequence. This Sequence has an identical conditional as Conditional1 in the first branch and if that first conditional is still true after we have exited the first branch, we perform an interruption which will force the Conditional1 in the first branch to return a success. And in my limited understanding of the inner works of the Behaviour Tree, it will cause the calling of Sequence.OnChildExecuted callback, which will update Sequence.executionStatus to Success, which will in turn make Sequence.CanExecute return true => Sequence starts again.

The downside of this is that this extra Sequence handling needs to be done after every Sequence with a similar situation. But it does work for now.

ExampleBehaviorScreenshot.png
 
Top