Behavior tree appears to call function after unpossess and destroy of AI

The stack and break messages show that the crash is due to a C++ AI function being called by the blackboard/behavior tree blueprint.

It would be very helpful if you attached log or callstack to your question.

–mieszko

Hi All

I’m using standard unpossess and destroy actor nodes in blueprints to remove ai control from a character that I’m about to possess with the player controller. The editor crashes very soon after those nodes have successfully completed (when I say that, I don’t know if they worked, but they ran, with breakpoints, and no error messages). The stack and break messages show that the crash is due to a C++ AI function being called by the blackboard/behavior tree blueprint.

I have initialized the blackboard and behavior tree in the possess function of the AI class, in C++. Should I have a custom unpossess function that deactivates these things? Any ideas about what I’ve done wrong here would be massively appreciated!

Thanks!

UE4Editor-RRDevelop-Win64-DebugGame.dll!TArray<FQueryKVPair * ptr64,FDefaultAllocator>::FindByPredicate<bool (FQueryKVPair *) >(ARRAIController::IsPlayerInRange::l7::bool (FQueryKVPair *) Pred) Line 1047 C++
UE4Editor-RRDevelop-Win64-DebugGame.dll!ARRAIController::IsPlayerInRange() Line 171 C++
UE4Editor-RRDevelop-Win64-DebugGame.dll!ARRAIController::execIsPlayerInRange(FFrame & Stack, void * const Result) Line 18 C++
UE4Editor-CoreUObject.dll!UFunction::Invoke(UObject * Obj, FFrame & Stack, void * const Result) Line 3699 C++
UE4Editor-CoreUObject.dll!UObject::CallFunction(FFrame & Stack, void * const Result, UFunction * Function) Line 395 C++
UE4Editor-CoreUObject.dll!UObject::ProcessContextOpcode(FFrame & Stack, void * const Result, bool bCanFailSilently) Line 1451 C++
UE4Editor-CoreUObject.dll!UObject::execLetBool(FFrame & Stack, void * const Result) Line 1370 C++
UE4Editor-CoreUObject.dll!UObject::ProcessInternal(FFrame & Stack, void * const Result) Line 602 C++
UE4Editor-CoreUObject.dll!UObject::CallFunction(FFrame & Stack, void * const Result, UFunction * Function) Line 516 C++
UE4Editor-CoreUObject.dll!UObject::execVirtualFunction(FFrame & Stack, void * const Result) Line 1530 C++
UE4Editor-CoreUObject.dll!UObject::ProcessInternal(FFrame & Stack, void * const Result) Line 602 C++
UE4Editor-CoreUObject.dll!UFunction::Invoke(UObject * Obj, FFrame & Stack, void * const Result) Line 3699 C++
UE4Editor-CoreUObject.dll!UObject::ProcessEvent(UFunction * Function, void * Parms) Line 924 C++
UE4Editor-AIModule.dll!UBTService_BlueprintBase::ReceiveTick(AActor * OwnerActor, float DeltaSeconds) Line 703 C++
UE4Editor-AIModule.dll!UBTCompositeNode::OnNodeActivation(FBehaviorTreeSearchData & SearchData) Line 129 C++
UE4Editor-AIModule.dll!UBehaviorTreeComponent::ProcessExecutionRequest() Line 879 C++
UE4Editor-AIModule.dll!UBehaviorTreeComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction * ThisTickFunction) Line 792 C++
UE4Editor-Engine.dll!UActorComponent::ConditionalTickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction & ThisTickFunction) Line 906 C++
UE4Editor-Engine.dll!FActorComponentTickFunction::ExecuteTick(float DeltaTime, ELevelTick TickType, ENamedThreads::Type CurrentThread, const TRefCountPtr & MyCompletionGraphEvent) Line 470 C++
UE4Editor-Engine.dll!FTickTaskSequencer::FTickFunctionTask::DoTask(ENamedThreads::Type CurrentThread, const TRefCountPtr & MyCompletionGraphEvent) Line 322 C++
UE4Editor-Engine.dll!TGraphTaskFTickTaskSequencer::FTickFunctionTask::ExecuteTask(TArray<FBaseGraphTask *,FDefaultAllocator> & NewTasks, ENamedThreads::Type CurrentThread) Line 671 C++
UE4Editor-Core.dll!FTaskThread::ProcessTasks(int QueueIndex, bool bAllowStall) Line 428 C++
UE4Editor-Core.dll!FTaskThread::ProcessTasksUntilQuit(int QueueIndex) Line 271 C++
UE4Editor-Core.dll!FTaskGraphImplementation::WaitUntilTasksComplete(const TArray<TRefCountPtr,TInlineAllocator<4,FDefaultAllocator> > & Tasks, ENamedThreads::Type CurrentThreadIfKnown) Line 984 C++
UE4Editor-Engine.dll!FTaskGraphInterface::WaitUntilTaskCompletes(const TRefCountPtr & Task, ENamedThreads::Type CurrentThreadIfKnown) Line 188 C++
UE4Editor-Engine.dll!FTickTaskSequencer::ReleaseTickGroup(ETickingGroup WorldTickGroup, bool bBlockTillComplete) Line 187 C++
UE4Editor-Engine.dll!FTickTaskManager::RunTickGroup(ETickingGroup Group, bool bBlockTillComplete) Line 722 C++
UE4Editor-Engine.dll!UWorld::RunTickGroup(ETickingGroup Group, bool bBlockTillComplete) Line 693 C++
UE4Editor-Engine.dll!UWorld::Tick(ELevelTick TickType, float DeltaSeconds) Line 1116 C++
UE4Editor-UnrealEd.dll!UEditorEngine::Tick(float DeltaSeconds, bool bIdleMode) Line 1227 C++
UE4Editor-UnrealEd.dll!UUnrealEdEngine::Tick(float DeltaSeconds, bool bIdleMode) Line 307 C++
UE4Editor.exe!FEngineLoop::Tick() Line 2214 C++
UE4Editor.exe!GuardedMain(const wchar_t * CmdLine, HINSTANCE
* hInInstance, HINSTANCE
* hPrevInstance, int nCmdShow) Line 131 C++
UE4Editor.exe!WinMain(HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, char * __formal, int nCmdShow) Line 191 C++
[External Code]

Hi Mieszko, I have added to question, thanks…

Great, thanks for the swift response…

Oops. No dice. I removed the destroy node, and I’m still getting the same issue.

I’ll report this as a bug, as you suggest, but I can’t believe it is. There must be loads of people doing this and not getting crashes…

Ok, I see what’s going on. So you’ve encountered a deeper engine level issue (meaning it’s not AI-specific) of ticking components of actors that have been already destroyed. This should not be happening.

However, there’s an easy remedy for you. You should not be calling Destroy on actors, ever (unless you really, really know what your doing). Yeah, I know it’s available in blueprints, and should be safe. I probably should start an internal thread regarding this as well.

Regardless, when dealing with AI, unpossesing should be enough. Once an AIController is no longer being used it should get automatically garbage-collected at some point. If it doesn’t then that’s a bug and please report it.

Cheers,

–mieszko

If it’s any help I specifically stop my brain component in the controller before destroying.

can you elaborate on why you should never call Destroy on an actor? What if I want to remove them from the world? This seems to work well for me. I also don’t see anywhere in the engine where an AIController with no pawn gets cleaned up, a controller is just an AActor so I’d like to know where/how that’s supposed to automatically get collected? Thanks.

Hi . I have no brain component- if by that you mean a sensing component or something like that. I’m referring to my project here, obvs :wink:

The AI is using arrays in the character class to know if something is in range. If you do something to shut down the behavior tree, I’d love to know what that is- that was kind of what I was thinking needed to be done.

Ahhhh. StopTree(). I hope this is the solution. If I don’t comment again then it is. Thanks!

@robbiecooper
UBehaviorTreeComponent is a BrainComponent, and that’s what’s in your callstack.

@
Regarding destroying actors, all actors get garbage-collected when there are no references to them, or their owner got/is going to be destroyed. Calling destroy directly is not a good practice since it may bypass multiple mechanisms under the hood that observe different state changes of an actor. As a generic rule destroying actors should be left to GC, "unless you know what you’re doing"™ :wink:

@MieszoZ I do see where a controller attaches itself to the pawn’s root component, but I don’t see who owns the controller.

What happens when you spawn an actor (SpawnActor) with a null owner? Will that actor get reclaimed automatically? How do you spawn temp actors that don’t get GCed?

I’ve dug into the code more on this: ULevel collects references to all spawned actors in the reference collector path, see Line 281 of Level.cpp. SpawnActor adds the AActor to this array. Currently setting an Owner to null doesn’t affect GC that I can see.

I posted a similar problem here:

In Blueprints, how do you remove the AI from the world without calling destroy actor? In my case it seems like the AI is in the middle of execution of a tick event when destroy actor is called, and then the tick event in the task reports an error when accessing its controlled pawn.