Behavior Tree (BT) services added to root nodes call the initial tick twice
A BT Service with the setting "Call Tick on Search Start" enabled will execute the initial tick twice when added to the root composite of a BT.
The Node is notified about the activation twice, first when the behavior tree is executed (from UBehaviorTreeComponent::PushInstance) and then when the root composite is activated (from UBTCompositeNode::OnNodeActivation).
The issue happens specifically with services that are running on the root composite, because the BT Component only notifies the root services when it pushes a new BT Instance.
I've attached 4 call stacks:
Call stacks #1 and #2 happen on the same frame, for the reason described. Call stacks #3 and #4 happen once every Tick, following the normal flow of execution.
Steps to Reproduce
Since this is a Behavior Tree (BT) issue, a basic setup is needed and a repro case is provided (refer to TestProject.zip on linked UDN)
An AIController is needed, as well as a character to be possessed by the AI. In the example, a PlayerController and a GameMode are used as well but not necessary.
Create 2 behavior trees
Add a C++ BTService class
In BT#2 add a root composite (e.g. Selector)
In BT#2, add the created BTService to the root composite with the option "Call Tick on Search Start" enabled
From BT#1, execute BT#2 using the RunBehavior node
Notice how the TickNode runs twice on the service added to the root node in BT#2, both times as the result of the "Call Tick on Search Start" setting
Call stack #1:
> UnrealEditor-TestProject.dll!UBTS_TestService::TickNode(UBehaviorTreeComponent & OwnerComp, unsigned char * NodeMemory, float DeltaSeconds) Line 10 C++
UnrealEditor-AIModule.dll!UBTService::NotifyParentActivation(FBehaviorTreeSearchData & SearchData) Line 55 C++
UnrealEditor-AIModule.dll!UBehaviorTreeComponent::PushInstance(UBehaviorTree & TreeAsset) Line 2666 C++
UnrealEditor-AIModule.dll!UBehaviorTreeComponent::ProcessPendingInitialize() Line 274 C++
UnrealEditor-AIModule.dll!UBehaviorTreeComponent::StartTree(UBehaviorTree & Asset, EBTExecutionMode::Type ExecuteMode) Line 239 C++
UnrealEditor-AIModule.dll!AAIController::RunBehaviorTree(UBehaviorTree * BTAsset) Line 963 C++
UnrealEditor-AIModule.dll!AAIController::execRunBehaviorTree(UObject * Context, FFrame & Stack, void * const Z_Param__Result) Line 207 C++
UnrealEditor-CoreUObject.dll!UFunction::Invoke(UObject * Obj, FFrame & Stack, void * const Z_Param__Result) Line 6665 C++
UnrealEditor-CoreUObject.dll!UObject::CallFunction(FFrame & Stack, void * const Z_Param__Result, UFunction * Function) Line 1139 C++
[Inline Frame] UnrealEditor-CoreUObject.dll!FFrame::Step(UObject *) Line 477 C++
UnrealEditor-CoreUObject.dll!UObject::ProcessContextOpcode(FFrame & Stack, void * const Z_Param__Result, bool bCanFailSilently) Line 3094 C++
[Inline Frame] UnrealEditor-CoreUObject.dll!FFrame::Step(UObject *) Line 477 C++
UnrealEditor-CoreUObject.dll!UObject::execLetBool(UObject * Context, FFrame & Stack, void * const Z_Param__Result) Line 2985 C++
[Inline Frame] UnrealEditor-CoreUObject.dll!FFrame::Step(UObject *) Line 477 C++
UnrealEditor-CoreUObject.dll!ProcessLocalScriptFunction(UObject * Context, FFrame & Stack, void * const Z_Param__Result) Line 1209 C++
UnrealEditor-CoreUObject.dll!ProcessScriptFunction<void (_cdecl*)(UObject *,FFrame &,void *)>(UObject * Context, UFunction * Function, FFrame & Stack, void * const Z_Param_Result, void[Image Removed](UObject *, FFrame &, void *) ExecFtor) Line 1039 C++
UnrealEditor-CoreUObject.dll!ProcessLocalFunction(UObject * Context, UFunction * Fn, FFrame & Stack, void * const Z_Param__Result) Line 1279 C++
[Inline Frame] UnrealEditor-CoreUObject.dll!FFrame::Step(UObject *) Line 477 C++
UnrealEditor-CoreUObject.dll!ProcessLocalScriptFunction(UObject * Context, FFrame & Stack, void * const Z_Param__Result) Line 1209 C++
UnrealEditor-CoreUObject.dll!UObject::ProcessInternal(UObject * Context, FFrame & Stack, void * const Z_Param__Result) Line 1306 C++
UnrealEditor-CoreUObject.dll!UFunction::Invoke(UObject * Obj, FFrame & Stack, void * const Z_Param__Result) Line 6665 C++
UnrealEditor-CoreUObject.dll!UObject::ProcessEvent(UFunction * Function, void * Parms) Line 2145 C++
UnrealEditor-Engine.dll!AActor::ProcessEvent(UFunction * Function, void * Parameters) Line 1122 C++
UnrealEditor-EnhancedInput.dll!TScriptDelegate<FNotThreadSafeDelegateMode>::ProcessDelegate<UObject>(void * Parameters) Line 448 C++
UnrealEditor-EnhancedInput.dll!FInputDebugKeyHandlerDynamicSignature_DelegateWrapper(const TScriptDelegate<FNotThreadSafeDelegateMode> & InputDebugKeyHandlerDynamicSignature, FKey Key, FInputActionValue ActionValue) Line 72 C++
UnrealEditor-EnhancedInput.dll!FInputDebugKeyHandlerDynamicSignature::Execute(FKey InParam1, FInputActionValue InParam2) Line 17 C++
UnrealEditor-EnhancedInput.dll!TEnhancedInputUnifiedDelegate<FInputDebugKeyHandlerDynamicSignature>::Execute<FKey,FInputActionValue>(FKey <Args_0>, FInputActionValue <Args_1>) Line 110 C++
UnrealEditor-EnhancedInput.dll!FInputDebugKeyDelegateBinding<FInputDebugKeyHandlerDynamicSignature>::Execute(const FInputActionValue & ActionValue) Line 279 C++
UnrealEditor-EnhancedInput.dll!UEnhancedPlayerInput::EvaluateInputDelegates(const TArray<UInputComponent *,TSizedDefaultAllocator<32>> & InputComponentStack, const float DeltaTime, const bool bGamePaused, const TArray<TTuple<FKey,FKeyState *>,TSizedDefaultAllocator<32>> & KeysWithEvents) Line 677 C++
UnrealEditor-Engine.dll!UPlayerInput::ProcessInputStack(const TArray<UInputComponent *,TSizedDefaultAllocator<32>> & InputComponentStack, const float DeltaTime, const bool bGamePaused) Line 1125 C++
UnrealEditor-Engine.dll!APlayerController::ProcessPlayerInput(const float DeltaTime, const bool bGamePaused) Line 2687 C++
UnrealEditor-Engine.dll!APlayerController::TickPlayerInput(const float DeltaSeconds, const bool bGamePaused) Line 4966 C++
UnrealEditor-Engine.dll!APlayerController::PlayerTick(float DeltaTime) Line 2291 C++
UnrealEditor-Engine.dll!APlayerController::TickActor(float DeltaSeconds, ELevelTick TickType, FActorTickFunction & ThisTickFunction) Line 5123 C++
UnrealEditor-Engine.dll!FActorTickFunction::ExecuteTick(float DeltaTime, ELevelTick TickType, ENamedThreads::Type CurrentThread, const TRefCountPtr<FGraphEvent> & MyCompletionGraphEvent) Line 251 C++
UnrealEditor-Engine.dll!FTickFunctionTask::DoTask(ENamedThreads::Type CurrentThread, const TRefCountPtr<FGraphEvent> & MyCompletionGraphEvent) Line 278 C++
UnrealEditor-Engine.dll!TGraphTask<FTickFunctionTask>::ExecuteTask(TArray<FBaseGraphTask *,TSizedDefaultAllocator<32>> & NewTasks, ENamedThreads::Type CurrentThread, bool bDeleteOnCompletion) Line 1265 C++
[Inline Frame] UnrealEditor-Core.dll!FBaseGraphTask::Execute(TArray<FBaseGraphTask *,TSizedDefaultAllocator<32>> & CurrentThread, ENamedThreads::Type) Line 866 C++
UnrealEditor-Core.dll!FNamedTaskThread::ProcessTasksNamedThread(int QueueIndex, bool bAllowStall) Line 758 C++
UnrealEditor-Core.dll!FNamedTaskThread::ProcessTasksUntilQuit(int QueueIndex) Line 649 C++
[Inline Frame] UnrealEditor-Core.dll!FTaskGraphCompatibilityImplementation::ProcessThreadUntilRequestReturn(ENamedThreads::Type CurrentThread) Line 2071 C++
UnrealEditor-Core.dll!FTaskGraphCompatibilityImplementation::WaitUntilTasksComplete(const TArray<TRefCountPtr<FGraphEvent>,TSizedInlineAllocator<4,32,TSizedDefaultAllocator<32>>> & Tasks, ENamedThreads::Type CurrentThreadIfKnown) Line 2125 C++
UnrealEditor-Engine.dll!FTickTaskSequencer::ReleaseTickGroup(ETickingGroup WorldTickGroup, bool bBlockTillComplete) Line 556 C++
UnrealEditor-Engine.dll!FTickTaskManager::RunTickGroup(ETickingGroup Group, bool bBlockTillComplete) Line 1583 C++
UnrealEditor-Engine.dll!UWorld::RunTickGroup(ETickingGroup Group, bool bBlockTillComplete) Line 771 C++
UnrealEditor-Engine.dll!UWorld::Tick(ELevelTick TickType, float DeltaSeconds) Line 1515 C++
UnrealEditor-UnrealEd.dll!UEditorEngine::Tick(float DeltaSeconds, bool bIdleMode) Line 1924 C++
UnrealEditor-UnrealEd.dll!UUnrealEdEngine::Tick(float DeltaSeconds, bool bIdleMode) Line 531 C++
UnrealEditor.exe!FEngineLoop::Tick() Line 5825 C++
[Inline Frame] UnrealEditor.exe!EngineTick() Line 61 C++
UnrealEditor.exe!GuardedMain(const wchar_t * CmdLine) Line 188 C++
UnrealEditor.exe!LaunchWindowsStartup(HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, char * __formal, int nCmdShow, const wchar_t * CmdLine) Line 247 C++
UnrealEditor.exe!WinMain(HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, char * pCmdLine, int nCmdShow) Line 298 C++
[External Code]
------------------------------------------
Call stack #2
> UnrealEditor-TestProject.dll!UBTS_TestService::TickNode(UBehaviorTreeComponent & OwnerComp, unsigned char * NodeMemory, float DeltaSeconds) Line 10 C++
UnrealEditor-AIModule.dll!UBTService::NotifyParentActivation(FBehaviorTreeSearchData & SearchData) Line 55 C++
UnrealEditor-AIModule.dll!UBTCompositeNode::OnNodeActivation(FBehaviorTreeSearchData & SearchData) Line 173 C++
UnrealEditor-AIModule.dll!UBehaviorTreeComponent::ProcessExecutionRequest() Line 1939 C++
UnrealEditor-AIModule.dll!UBehaviorTreeComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction * ThisTickFunction) Line 1703 C++
[Inline Frame] UnrealEditor-Engine.dll!FActorComponentTickFunction::ExecuteTick::__l2::<lambda_1>::operator()(float) Line 1090 C++
UnrealEditor-Engine.dll!FActorComponentTickFunction::ExecuteTickHelper<`FActorComponentTickFunction::ExecuteTick'::`2'::<lambda_1>>(UActorComponent * Target, bool bTickInEditor, float DeltaTime, ELevelTick TickType, const FActorComponentTickFunction::ExecuteTick::__l2::<lambda_1> & ExecuteTickFunc) Line 4490 C++
UnrealEditor-Engine.dll!FActorComponentTickFunction::ExecuteTick(float DeltaTime, ELevelTick TickType, ENamedThreads::Type CurrentThread, const TRefCountPtr<FGraphEvent> & MyCompletionGraphEvent) Line 1088 C++
UnrealEditor-Engine.dll!FTickFunctionTask::DoTask(ENamedThreads::Type CurrentThread, const TRefCountPtr<FGraphEvent> & MyCompletionGraphEvent) Line 278 C++
UnrealEditor-Engine.dll!TGraphTask<FTickFunctionTask>::ExecuteTask(TArray<FBaseGraphTask *,TSizedDefaultAllocator<32>> & NewTasks, ENamedThreads::Type CurrentThread, bool bDeleteOnCompletion) Line 1265 C++
[Inline Frame] UnrealEditor-Core.dll!FBaseGraphTask::Execute(TArray<FBaseGraphTask *,TSizedDefaultAllocator<32>> & CurrentThread, ENamedThreads::Type) Line 866 C++
UnrealEditor-Core.dll!FNamedTaskThread::ProcessTasksNamedThread(int QueueIndex, bool bAllowStall) Line 758 C++
UnrealEditor-Core.dll!FNamedTaskThread::ProcessTasksUntilIdle(int QueueIndex) Line 660 C++
UnrealEditor-Engine.dll!FTickTaskSequencer::ReleaseTickGroup(ETickingGroup WorldTickGroup, bool bBlockTillComplete) Line 573 C++
UnrealEditor-Engine.dll!FTickTaskManager::RunTickGroup(ETickingGroup Group, bool bBlockTillComplete) Line 1583 C++
UnrealEditor-Engine.dll!UWorld::RunTickGroup(ETickingGroup Group, bool bBlockTillComplete) Line 771 C++
UnrealEditor-Engine.dll!UWorld::Tick(ELevelTick TickType, float DeltaSeconds) Line 1530 C++
UnrealEditor-UnrealEd.dll!UEditorEngine::Tick(float DeltaSeconds, bool bIdleMode) Line 1924 C++
UnrealEditor-UnrealEd.dll!UUnrealEdEngine::Tick(float DeltaSeconds, bool bIdleMode) Line 531 C++
UnrealEditor.exe!FEngineLoop::Tick() Line 5825 C++
[Inline Frame] UnrealEditor.exe!EngineTick() Line 61 C++
UnrealEditor.exe!GuardedMain(const wchar_t * CmdLine) Line 188 C++
UnrealEditor.exe!LaunchWindowsStartup(HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, char * __formal, int nCmdShow, const wchar_t * CmdLine) Line 247 C++
UnrealEditor.exe!WinMain(HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, char * pCmdLine, int nCmdShow) Line 298 C++
[External Code]
------------------------------------------
Call stack #3
> UnrealEditor-TestProject.dll!UBTS_TestService::TickNode(UBehaviorTreeComponent & OwnerComp, unsigned char * NodeMemory, float DeltaSeconds) Line 10 C++
UnrealEditor-AIModule.dll!UBTAuxiliaryNode::WrappedTickNode(UBehaviorTreeComponent & OwnerComp, unsigned char * NodeMemory, float DeltaSeconds, float & NextNeededDeltaTime) Line 71 C++
UnrealEditor-AIModule.dll!UBehaviorTreeComponent::TickComponent::__l38::<lambda_1>::operator()(const UBTAuxiliaryNode & AuxNode) Line 1667 C++
[Inline Frame] UnrealEditor-AIModule.dll!UE::Core::Private::Function::TFunctionRefBase<UE::Core::Private::Function::FFunctionRefStoragePolicy,void __cdecl(UBTAuxiliaryNode const &)>::operator()(const UBTAuxiliaryNode &) Line 629 C++
UnrealEditor-AIModule.dll!FBehaviorTreeInstance::ExecuteOnEachAuxNode(TFunctionRef<void __cdecl(UBTAuxiliaryNode const &)> ExecFunc) Line 326 C++
UnrealEditor-AIModule.dll!UBehaviorTreeComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction * ThisTickFunction) Line 1660 C++
[Inline Frame] UnrealEditor-Engine.dll!FActorComponentTickFunction::ExecuteTick::__l2::<lambda_1>::operator()(float) Line 1090 C++
UnrealEditor-Engine.dll!FActorComponentTickFunction::ExecuteTickHelper<`FActorComponentTickFunction::ExecuteTick'::`2'::<lambda_1>>(UActorComponent * Target, bool bTickInEditor, float DeltaTime, ELevelTick TickType, const FActorComponentTickFunction::ExecuteTick::__l2::<lambda_1> & ExecuteTickFunc) Line 4490 C++
UnrealEditor-Engine.dll!FActorComponentTickFunction::ExecuteTick(float DeltaTime, ELevelTick TickType, ENamedThreads::Type CurrentThread, const TRefCountPtr<FGraphEvent> & MyCompletionGraphEvent) Line 1088 C++
UnrealEditor-Engine.dll!FTickFunctionTask::DoTask(ENamedThreads::Type CurrentThread, const TRefCountPtr<FGraphEvent> & MyCompletionGraphEvent) Line 278 C++
UnrealEditor-Engine.dll!TGraphTask<FTickFunctionTask>::ExecuteTask(TArray<FBaseGraphTask *,TSizedDefaultAllocator<32>> & NewTasks, ENamedThreads::Type CurrentThread, bool bDeleteOnCompletion) Line 1265 C++
[Inline Frame] UnrealEditor-Core.dll!FBaseGraphTask::Execute(TArray<FBaseGraphTask *,TSizedDefaultAllocator<32>> & CurrentThread, ENamedThreads::Type) Line 866 C++
UnrealEditor-Core.dll!FNamedTaskThread::ProcessTasksNamedThread(int QueueIndex, bool bAllowStall) Line 758 C++
UnrealEditor-Core.dll!FNamedTaskThread::ProcessTasksUntilIdle(int QueueIndex) Line 660 C++
UnrealEditor-Engine.dll!FTickTaskSequencer::ReleaseTickGroup(ETickingGroup WorldTickGroup, bool bBlockTillComplete) Line 573 C++
UnrealEditor-Engine.dll!FTickTaskManager::RunTickGroup(ETickingGroup Group, bool bBlockTillComplete) Line 1583 C++
UnrealEditor-Engine.dll!UWorld::RunTickGroup(ETickingGroup Group, bool bBlockTillComplete) Line 771 C++
UnrealEditor-Engine.dll!UWorld::Tick(ELevelTick TickType, float DeltaSeconds) Line 1530 C++
UnrealEditor-UnrealEd.dll!UEditorEngine::Tick(float DeltaSeconds, bool bIdleMode) Line 1924 C++
UnrealEditor-UnrealEd.dll!UUnrealEdEngine::Tick(float DeltaSeconds, bool bIdleMode) Line 531 C++
UnrealEditor.exe!FEngineLoop::Tick() Line 5825 C++
[Inline Frame] UnrealEditor.exe!EngineTick() Line 61 C++
UnrealEditor.exe!GuardedMain(const wchar_t * CmdLine) Line 188 C++
UnrealEditor.exe!LaunchWindowsStartup(HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, char * __formal, int nCmdShow, const wchar_t * CmdLine) Line 247 C++
UnrealEditor.exe!WinMain(HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, char * pCmdLine, int nCmdShow) Line 298 C++
[External Code]
------------------------------------------
Call stack #4
> UnrealEditor-TestProject.dll!UBTS_TestService::TickNode(UBehaviorTreeComponent & OwnerComp, unsigned char * NodeMemory, float DeltaSeconds) Line 10 C++
UnrealEditor-AIModule.dll!UBTAuxiliaryNode::WrappedTickNode(UBehaviorTreeComponent & OwnerComp, unsigned char * NodeMemory, float DeltaSeconds, float & NextNeededDeltaTime) Line 71 C++
UnrealEditor-AIModule.dll!UBehaviorTreeComponent::TickComponent::__l38::<lambda_1>::operator()(const UBTAuxiliaryNode & AuxNode) Line 1667 C++
[Inline Frame] UnrealEditor-AIModule.dll!UE::Core::Private::Function::TFunctionRefBase<UE::Core::Private::Function::FFunctionRefStoragePolicy,void __cdecl(UBTAuxiliaryNode const &)>::operator()(const UBTAuxiliaryNode &) Line 629 C++
UnrealEditor-AIModule.dll!FBehaviorTreeInstance::ExecuteOnEachAuxNode(TFunctionRef<void __cdecl(UBTAuxiliaryNode const &)> ExecFunc) Line 326 C++
UnrealEditor-AIModule.dll!UBehaviorTreeComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction * ThisTickFunction) Line 1660 C++
[Inline Frame] UnrealEditor-Engine.dll!FActorComponentTickFunction::ExecuteTick::__l2::<lambda_1>::operator()(float) Line 1090 C++
UnrealEditor-Engine.dll!FActorComponentTickFunction::ExecuteTickHelper<`FActorComponentTickFunction::ExecuteTick'::`2'::<lambda_1>>(UActorComponent * Target, bool bTickInEditor, float DeltaTime, ELevelTick TickType, const FActorComponentTickFunction::ExecuteTick::__l2::<lambda_1> & ExecuteTickFunc) Line 4490 C++
UnrealEditor-Engine.dll!FActorComponentTickFunction::ExecuteTick(float DeltaTime, ELevelTick TickType, ENamedThreads::Type CurrentThread, const TRefCountPtr<FGraphEvent> & MyCompletionGraphEvent) Line 1088 C++
UnrealEditor-Engine.dll!FTickFunctionTask::DoTask(ENamedThreads::Type CurrentThread, const TRefCountPtr<FGraphEvent> & MyCompletionGraphEvent) Line 278 C++
UnrealEditor-Engine.dll!TGraphTask<FTickFunctionTask>::ExecuteTask(TArray<FBaseGraphTask *,TSizedDefaultAllocator<32>> & NewTasks, ENamedThreads::Type CurrentThread, bool bDeleteOnCompletion) Line 1265 C++
[Inline Frame] UnrealEditor-Core.dll!FBaseGraphTask::Execute(TArray<FBaseGraphTask *,TSizedDefaultAllocator<32>> & CurrentThread, ENamedThreads::Type) Line 866 C++
UnrealEditor-Core.dll!FNamedTaskThread::ProcessTasksNamedThread(int QueueIndex, bool bAllowStall) Line 758 C++
UnrealEditor-Core.dll!FNamedTaskThread::ProcessTasksUntilIdle(int QueueIndex) Line 660 C++
UnrealEditor-Engine.dll!FTickTaskSequencer::ReleaseTickGroup(ETickingGroup WorldTickGroup, bool bBlockTillComplete) Line 573 C++
UnrealEditor-Engine.dll!FTickTaskManager::RunTickGroup(ETickingGroup Group, bool bBlockTillComplete) Line 1583 C++
UnrealEditor-Engine.dll!UWorld::RunTickGroup(ETickingGroup Group, bool bBlockTillComplete) Line 771 C++
UnrealEditor-Engine.dll!UWorld::Tick(ELevelTick TickType, float DeltaSeconds) Line 1530 C++
UnrealEditor-UnrealEd.dll!UEditorEngine::Tick(float DeltaSeconds, bool bIdleMode) Line 1924 C++
UnrealEditor-UnrealEd.dll!UUnrealEdEngine::Tick(float DeltaSeconds, bool bIdleMode) Line 531 C++
UnrealEditor.exe!FEngineLoop::Tick() Line 5825 C++
[Inline Frame] UnrealEditor.exe!EngineTick() Line 61 C++
UnrealEditor.exe!GuardedMain(const wchar_t * CmdLine) Line 188 C++
UnrealEditor.exe!LaunchWindowsStartup(HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, char * __formal, int nCmdShow, const wchar_t * CmdLine) Line 247 C++
UnrealEditor.exe!WinMain(HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, char * pCmdLine, int nCmdShow) Line 298 C++
[External Code]
There's no existing public thread on this issue, so head over to Questions & Answers just mention UE-230986 in the post.