I have a state tree task that works basically the same as Epics FStateTreeMoveToTask example, using an internally allocated and tracked UAITask_MoveTo.
Like FStateTreeMoveToTask, the ExitState function calls ExternalCancel on the move task if it isn’t already finished.
However, I noticed that my AI was doing some weird movement behavior, and on further debugging, I discovered that there were multiple UAITask_MoveTo in the TaskPriorityQueue in UGameplayTasksComponent coming from the state tree
I would not expect this to be possible, as I am not using any parallel move states in this situation, and the ExitState should be cleaning up the last allocated UAITask_MoveTo, as it does in FStateTreeMoveToTask.
After a bit more experimentation and debugging, I noticed that StateCompleted may be called on a state, and ExitState doesn’t, providing an opportunity for the state to “leak” data, in this case the UAITask_MoveTo gameplay task created to do work for the state.
I wanted to ask whether this was to be expected or not. This seems like a bug that even Epic isn’t accounting for in their example.
If this is to be expected, I guess any cleanup code such as this would generally need to be duplicated across StateExit and StateCompleted?
It looks like if the State::Tick fails(return EStateTreeRunStatus::Failed), StateCompleted will be called, and ExitState will not be called for the same state, missing any cleanup you might be expecting to do in the ExitState.
The callstack for the StateCompleted is
FStateTreeTasks_AIMoveTo::StateCompleted(FStateTreeExecutionContext &, EStateTreeRunStatus, const FStateTreeActiveStates &) StateTreeTasks_AIMoveTo.cpp:82 FStateTreeExecutionContext::StateCompleted() StateTreeExecutionContext.cpp:2077 FStateTreeExecutionContext::TickUpdateTasksInternal(const float) StateTreeExecutionContext.cpp:584 FStateTreeExecutionContext::Tick(const float) StateTreeExecutionContext.cpp:526 UStateTreeComponent::TickComponent(float, ELevelTick, FActorComponentTickFunction *) StateTreeComponent.cpp:109
Note: This is with bShouldStateChangeOnReselect=false, I notice that FStateTreeMoveToTask doesn’t disable this option.