Behaviour Tree: Conditional loop on sequence unexpected behavior

I have an issue with using a conditional loop decorator on a sequence composite node: When a task that is a child of the sequence fails, while the loop’s condition is still true, the sequence is immediately executed again. I would expect that a failure of a child task that normally causes the sequence to fail as well would break the loop no matter what the condition is.

Is this the expected behaviour? If so, how am I supposed to break the loop in this case? The task that fails is not directly related to the condition of the loop and I want to avoid adding a coupling between the task and the loop condition.

Adding pictures help a lot to understand the problem :wink:

Yes, it is the expected behavior since the sequence is inside a loop. The sequence will be always executed if the loop is executed.
The only thing that comes to my mind is to add an additional boolean decorator after the loop. By this way, every time the loop is executed (any iteration) it will check this condition and see if the loop has to abort or not. It is like a loop with an if and a break.

Hope this helps,

Update: To illustrate the issue for future reference: First, a custom task. It is pretty useless, but enough to demonstrate the issue.

Second, the behaviour tree. BoolKey is set to true in the AI controller before the tree is run for the first time.

When the tree is run, MyTask gets executed first and immidiately fails, causing the parent sequence to fail as well. However, execution is not passed to the selector but instead, the sequence is run again in an infinite loop.

To get the desired behaviour, I made my own decorator based on the BTDecorator_ConditionalLoop class and overrode OnNodeDeactivation as follows:

void UBTDecorator_ConditionalLoopBreak::OnNodeDeactivation(FBehaviorTreeSearchData& SearchData, EBTNodeResult::Type NodeResult)
{
	if (NodeResult != EBTNodeResult::Aborted && NodeResult != EBTNodeResult::Failed)
	{
		const UBlackboardComponent* BlackboardComp = SearchData.OwnerComp.GetBlackboardComponent();
		const bool bEvalResult = BlackboardComp && EvaluateOnBlackboard(*BlackboardComp);

		if (bEvalResult != IsInversed())
		{
			GetParentNode()->SetChildOverride(SearchData, GetChildIndex());
		}
	}
}

The only change to the implementation in the base class is in the if (NodeResult)… condition where I also check that the Result is not Failed. This causes the loop to break when the node fails. This also allows to get back to the original behavior by adding a Force Success decorator before the conditional loop, which is also the way I would expect it to work.