Hi,
We’ve been having problems with decorators not aborting lower priority tasks when they should. After an extensive debugging session we think we’ve found the issue, but are unsure of the solution. We’ll try to describe the the consequence and the source of the problem:
Problem:
In BehaviorTreeInstance::Initialize the line (BehaviorTreeTypes.cpp)
UBTDecorator* InstancedDecoratorOb = Cast(DecoratorOb->GetNodeInstance(OwnerComp, DecoratorMemory));
returns a wrong pointer when DecoratorOb is TimeLimit decorator. Subsequently the child index for this InstancedDecoratorOb is incorrectly overwritten on the next line: (BehaviorTreeTypes.cpp)
InstancedDecoratorOb->InitializeParentLink(DecoratorOb->GetChildIndex());
When the instanced decorator with the incorrect child index then triggers and requests execution the line
RequestedOn->DoDecoratorsAllowExecution(*this, InstanceIdx, RequestedByChildIndex);
in (BehaviorTreeComponent.cpp) then checks the decorators on a different node than the one triggering this check because RequestedByChildIndex is wrong.
Suspected cause
UBTNode::GetNodeInstance seems to make the assumption that if GetSpecialNodeMemory returns a valid pointer that the node itself is instanced. Looking at the implementation of UBTNode::GetSpecialMemorySize() this seems to be a valid assumption, but this method is overridden in UBTAuxiliaryNode in the following manner:
uint16 UBTAuxiliaryNode::GetSpecialMemorySize() const
{
return bTickIntervals ? sizeof(FBTAuxiliaryMemory) : Super::GetSpecialMemorySize();
}
which invalidates the assumption for AuxiliaryNodes that have bTickIntervals set to true (e.g. BTDecorator_TimeLimit).
Could you confirm this bug? And do you have a suggested fix for this? We are unsure if the caller of GetNodeInstance should be responsible for checking if the node is actually instanced, or that GetNodeInstance should just return nullptr if it is not instanced. Or perhaps we are misreading the intentions of this code?
Best regards,
Steijn