2022-05-31 04:51:18 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "StateTreeExecutionContext.h"
# include "StateTreeTaskBase.h"
# include "StateTreeEvaluatorBase.h"
# include "StateTreeConditionBase.h"
# include "Containers/StaticArray.h"
2023-03-14 13:35:46 -04:00
# include "Debugger/StateTreeTrace.h"
# include "Debugger/StateTreeTraceTypes.h"
2023-05-23 10:46:16 -04:00
# include "Misc/ScopeExit.h"
2022-05-31 04:51:18 -04:00
# include "VisualLogger/VisualLogger.h"
# include "ProfilingDebugging/CsvProfiler.h"
# include "Logging/LogScopedVerbosityOverride.h"
2023-06-06 11:25:12 -04:00
# define STATETREE_LOG(Verbosity, Format, ...) UE_VLOG_UELOG(GetOwner(), LogStateTree, Verbosity, TEXT("%s: ") Format, *GetInstanceDescription(), ##__VA_ARGS__)
# define STATETREE_CLOG(Condition, Verbosity, Format, ...) UE_CVLOG_UELOG((Condition), GetOwner(), LogStateTree, Verbosity, TEXT("%s: ") Format, *GetInstanceDescription(), ##__VA_ARGS__)
2022-05-31 04:51:18 -04:00
2023-09-22 09:53:34 -04:00
# define STATETREE_LOG_AND_TRACE(Verbosity, Format, ...) \
UE_VLOG_UELOG ( GetOwner ( ) , LogStateTree , Verbosity , TEXT ( " %s: " ) Format , * GetInstanceDescription ( ) , # # __VA_ARGS__ ) ; \
STATETREE_TRACE_LOG_EVENT ( Format , # # __VA_ARGS__ )
2023-04-19 13:26:23 -04:00
# if WITH_STATETREE_DEBUGGER
2023-05-29 10:13:21 -04:00
# define ID_NAME PREPROCESSOR_JOIN(InstanceId,__LINE__) \
# define STATETREE_TRACE_SCOPED_PHASE(Phase) \
FStateTreeInstanceDebugId ID_NAME = GetInstanceDebugId ( ) ; \
2023-08-04 14:55:23 -04:00
TRACE_STATETREE_PHASE_EVENT ( ID_NAME , Phase , EStateTreeTraceEventType : : Push , FStateTreeStateHandle : : Invalid ) \
ON_SCOPE_EXIT { TRACE_STATETREE_PHASE_EVENT ( ID_NAME , Phase , EStateTreeTraceEventType : : Pop , FStateTreeStateHandle : : Invalid ) }
2023-05-29 10:13:21 -04:00
2023-08-04 14:55:23 -04:00
# define STATETREE_TRACE_SCOPED_STATE(StateHandle) \
2023-05-29 10:13:21 -04:00
FStateTreeInstanceDebugId ID_NAME = GetInstanceDebugId ( ) ; \
2023-08-04 14:55:23 -04:00
TRACE_STATETREE_PHASE_EVENT ( ID_NAME , EStateTreeUpdatePhase : : Unset , EStateTreeTraceEventType : : Push , StateHandle ) \
ON_SCOPE_EXIT { TRACE_STATETREE_PHASE_EVENT ( ID_NAME , EStateTreeUpdatePhase : : Unset , EStateTreeTraceEventType : : Pop , StateHandle ) }
# define STATETREE_TRACE_SCOPED_STATE_PHASE(StateHandle, Phase) \
FStateTreeInstanceDebugId ID_NAME = GetInstanceDebugId ( ) ; \
TRACE_STATETREE_PHASE_EVENT ( ID_NAME , Phase , EStateTreeTraceEventType : : Push , StateHandle ) \
ON_SCOPE_EXIT { TRACE_STATETREE_PHASE_EVENT ( ID_NAME , Phase , EStateTreeTraceEventType : : Pop , StateHandle ) }
2023-05-29 10:13:21 -04:00
# define STATETREE_TRACE_INSTANCE_EVENT(EventType) TRACE_STATETREE_INSTANCE_EVENT(GetInstanceDebugId(), GetStateTree(), *GetInstanceDescription(), EventType);
# define STATETREE_TRACE_ACTIVE_STATES_EVENT(ActiveStates) TRACE_STATETREE_ACTIVE_STATES_EVENT(GetInstanceDebugId(), ActiveStates);
# define STATETREE_TRACE_LOG_EVENT(Format, ...) TRACE_STATETREE_LOG_EVENT(GetInstanceDebugId(), Format, ##__VA_ARGS__)
# define STATETREE_TRACE_STATE_EVENT(StateHandle, EventType) TRACE_STATETREE_STATE_EVENT(GetInstanceDebugId(), StateHandle, EventType, EStateTreeStateSelectionBehavior::None);
# define STATETREE_TRACE_TASK_EVENT(Index, DataView, EventType, Status) TRACE_STATETREE_TASK_EVENT(GetInstanceDebugId(), FStateTreeIndex16(Index), DataView, EventType, Status);
2023-08-07 18:06:40 -04:00
# define STATETREE_TRACE_EVALUATOR_EVENT(Index, DataView, EventType) TRACE_STATETREE_EVALUATOR_EVENT(GetInstanceDebugId(), FStateTreeIndex16(Index), DataView, EventType);
2023-05-29 10:13:21 -04:00
# define STATETREE_TRACE_CONDITION_EVENT(Index, DataViews, EventType) TRACE_STATETREE_CONDITION_EVENT(GetInstanceDebugId(), FStateTreeIndex16(Index), DataView, EventType);
2023-08-04 14:55:23 -04:00
# define STATETREE_TRACE_TRANSITION_EVENT(Source, EventType) TRACE_STATETREE_TRANSITION_EVENT(GetInstanceDebugId(), Source, EventType);
2023-04-19 13:26:23 -04:00
# else
2023-05-29 10:13:21 -04:00
# define STATETREE_TRACE_SCOPED_PHASE(Phase)
2023-08-04 14:55:23 -04:00
# define STATETREE_TRACE_SCOPED_STATE(StateHandle)
# define STATETREE_TRACE_SCOPED_STATE_PHASE(StateHandle, Phase)
2023-05-29 10:13:21 -04:00
# define STATETREE_TRACE_INSTANCE_EVENT(EventType)
# define STATETREE_TRACE_ACTIVE_STATES_EVENT(ActiveStates)
2023-04-19 13:26:23 -04:00
# define STATETREE_TRACE_LOG_EVENT(Format, ...)
2023-05-23 10:46:16 -04:00
# define STATETREE_TRACE_STATE_EVENT(StateHandle, EventType)
2023-04-19 13:26:23 -04:00
# define STATETREE_TRACE_TASK_EVENT(Index, DataView, EventType, Status)
2023-08-07 18:06:40 -04:00
# define STATETREE_TRACE_EVALUATOR_EVENT(Index, DataView, EventType)
2023-05-23 10:46:16 -04:00
# define STATETREE_TRACE_CONDITION_EVENT(Index, DataView, EventType)
2023-08-04 14:55:23 -04:00
# define STATETREE_TRACE_TRANSITION_EVENT(Source, EventType)
2023-04-19 13:26:23 -04:00
# endif // WITH_STATETREE_DEBUGGER
2022-05-31 04:51:18 -04:00
namespace UE : : StateTree
{
2022-09-01 09:06:53 -04:00
constexpr int32 DebugIndentSize = 2 ; // Debug printing indent for hierarchical data.
} ; // UE::StateTree
2022-05-31 04:51:18 -04:00
2022-09-23 20:02:42 -04:00
FStateTreeExecutionContext : : FStateTreeExecutionContext ( UObject & InOwner , const UStateTree & InStateTree , FStateTreeInstanceData & InInstanceData )
: Owner ( InOwner )
, StateTree ( InStateTree )
, InstanceData ( InInstanceData )
2022-05-31 04:51:18 -04:00
{
2022-09-23 20:02:42 -04:00
if ( InStateTree . IsReadyToRun ( ) )
{
// Initialize data views for all possible items.
DataViews . SetNum ( StateTree . GetNumDataViews ( ) ) ;
// Set data views associated to the parameters using the default values
SetDefaultParameters ( ) ;
2023-01-23 12:48:04 -05:00
SharedInstanceData = StateTree . GetSharedInstanceData ( ) ;
2022-09-23 20:02:42 -04:00
}
else
{
2023-04-12 07:59:16 -04:00
STATETREE_LOG ( Warning , TEXT ( " %hs: StateTree asset is not valid ('%s' using StateTree '%s') " ) ,
__FUNCTION__ , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & StateTree ) ) ;
2022-09-23 20:02:42 -04:00
}
2022-05-31 04:51:18 -04:00
}
FStateTreeExecutionContext : : ~ FStateTreeExecutionContext ( )
{
}
void FStateTreeExecutionContext : : SetDefaultParameters ( )
{
2022-09-23 20:02:42 -04:00
if ( DataViews . IsValidIndex ( StateTree . ParametersDataViewIndex . Get ( ) ) )
2022-05-31 04:51:18 -04:00
{
2023-01-10 15:44:28 -05:00
// @todo: Handle constness correctly.
const FConstStructView ConstParameters = StateTree . GetDefaultParameters ( ) . GetValue ( ) ;
DataViews [ StateTree . ParametersDataViewIndex . Get ( ) ] = FStateTreeDataView ( ConstParameters . GetScriptStruct ( ) , const_cast < uint8 * > ( ConstParameters . GetMemory ( ) ) ) ;
2022-05-31 04:51:18 -04:00
}
}
void FStateTreeExecutionContext : : SetParameters ( const FInstancedPropertyBag & Parameters )
{
2022-09-23 20:02:42 -04:00
if ( ensureMsgf ( StateTree . GetDefaultParameters ( ) . GetPropertyBagStruct ( ) = = Parameters . GetPropertyBagStruct ( ) ,
TEXT ( " Parameters must be of the same struct type. Make sure to migrate the provided parameters to the same type as the StateTree default parameters. " ) )
& & DataViews . IsValidIndex ( StateTree . ParametersDataViewIndex . Get ( ) ) )
2022-05-31 04:51:18 -04:00
{
2023-01-10 15:44:28 -05:00
// @todo: Handle constness correctly.
const FConstStructView ConstParameters = Parameters . GetValue ( ) ;
DataViews [ StateTree . ParametersDataViewIndex . Get ( ) ] = FStateTreeDataView ( ConstParameters . GetScriptStruct ( ) , const_cast < uint8 * > ( ConstParameters . GetMemory ( ) ) ) ;
2022-05-31 04:51:18 -04:00
}
}
2022-09-01 09:06:53 -04:00
bool FStateTreeExecutionContext : : AreExternalDataViewsValid ( ) const
{
2022-09-23 20:02:42 -04:00
if ( ! IsValid ( ) )
{
return false ;
}
2022-09-01 09:06:53 -04:00
bool bResult = true ;
2022-09-23 20:02:42 -04:00
for ( const FStateTreeExternalDataDesc & DataDesc : StateTree . ExternalDataDescs )
2022-09-01 09:06:53 -04:00
{
const FStateTreeDataView & DataView = DataViews [ DataDesc . Handle . DataViewIndex . Get ( ) ] ;
if ( DataDesc . Requirement = = EStateTreeExternalDataRequirement : : Required )
{
// Required items must have valid pointer of the expected type.
if ( ! DataView . IsValid ( ) | | ! DataView . GetStruct ( ) - > IsChildOf ( DataDesc . Struct ) )
{
bResult = false ;
break ;
}
}
else
{
// Optional items must have same type if they are set.
if ( DataView . IsValid ( ) & & ! DataView . GetStruct ( ) - > IsChildOf ( DataDesc . Struct ) )
{
bResult = false ;
break ;
}
}
}
2022-09-23 20:02:42 -04:00
for ( const FStateTreeExternalDataDesc & DataDesc : StateTree . GetContextDataDescs ( ) )
2022-09-01 09:06:53 -04:00
{
const FStateTreeDataView & DataView = DataViews [ DataDesc . Handle . DataViewIndex . Get ( ) ] ;
// Items must have valid pointer of the expected type.
if ( ! DataView . IsValid ( ) | | ! DataView . GetStruct ( ) - > IsChildOf ( DataDesc . Struct ) )
{
bResult = false ;
break ;
}
}
return bResult ;
}
2022-12-02 10:07:29 -05:00
void FStateTreeExecutionContext : : UpdateLinkedStateParameters ( const FCompactStateTreeState & State , const int32 ParameterInstanceIndex )
2022-05-31 04:51:18 -04:00
{
2023-05-23 10:46:16 -04:00
const FStateTreeDataView StateParamsInstance = InstanceData . GetMutableStruct ( ParameterInstanceIndex ) ;
2022-12-02 07:57:31 -05:00
FCompactStateTreeParameters & StateParams = StateParamsInstance . GetMutable < FCompactStateTreeParameters > ( ) ;
2022-05-31 04:51:18 -04:00
2022-09-23 19:58:36 -04:00
// Update parameters if the state has any.
if ( StateParams . Parameters . IsValid ( ) )
2022-05-31 04:51:18 -04:00
{
2022-09-23 19:58:36 -04:00
// Parameters property bag
const FStateTreeDataView ParametersView ( StateParams . Parameters . GetMutableValue ( ) ) ;
if ( StateParams . BindingsBatch . IsValid ( ) )
{
2022-09-23 20:02:42 -04:00
StateTree . PropertyBindings . CopyTo ( DataViews , StateParams . BindingsBatch , ParametersView ) ;
2022-09-23 19:58:36 -04:00
}
2022-05-31 04:51:18 -04:00
2022-09-23 19:58:36 -04:00
// Set the parameters as the input parameters for the linked state.
check ( State . LinkedState . IsValid ( ) ) ;
2022-09-23 20:02:42 -04:00
const FCompactStateTreeState & LinkedState = StateTree . States [ State . LinkedState . Index ] ;
2022-09-23 19:58:36 -04:00
check ( LinkedState . ParameterDataViewIndex . IsValid ( ) ) ;
DataViews [ LinkedState . ParameterDataViewIndex . Get ( ) ] = ParametersView ;
}
2022-05-31 04:51:18 -04:00
}
2022-09-23 20:02:42 -04:00
void FStateTreeExecutionContext : : UpdateSubtreeStateParameters ( const FCompactStateTreeState & State )
2022-05-31 04:51:18 -04:00
{
check ( State . ParameterInstanceIndex . IsValid ( ) ) ;
2022-09-23 19:58:36 -04:00
// Update parameters if the state has any.
if ( State . ParameterDataViewIndex . IsValid ( ) )
{
// Usually the subtree parameter view is set by the linked state. If it's not (i.e. transitioned into a parametrized subtree), we'll set the view default params.
if ( DataViews [ State . ParameterDataViewIndex . Get ( ) ] . IsValid ( ) )
{
return ;
}
// Set view to default parameters.
2022-11-30 07:17:26 -05:00
const FConstStructView ParamInstance = StateTree . DefaultInstanceData . GetStruct ( State . ParameterInstanceIndex . Get ( ) ) ; // These are used as const, so get them from the tree initial values.
const FStateTreeDataView ParamInstanceView ( ParamInstance . GetScriptStruct ( ) , const_cast < uint8 * > ( ParamInstance . GetMemory ( ) ) ) ;
2022-12-02 07:57:31 -05:00
FCompactStateTreeParameters & Params = ParamInstanceView . GetMutable < FCompactStateTreeParameters > ( ) ;
2022-09-23 19:58:36 -04:00
DataViews [ State . ParameterDataViewIndex . Get ( ) ] = FStateTreeDataView ( Params . Parameters . GetMutableValue ( ) ) ;
}
2022-05-31 04:51:18 -04:00
}
2022-09-23 20:02:42 -04:00
EStateTreeRunStatus FStateTreeExecutionContext : : Start ( )
2022-05-31 04:51:18 -04:00
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_Start ) ;
2022-09-23 20:02:42 -04:00
if ( ! IsValid ( ) )
2022-05-31 04:51:18 -04:00
{
2023-04-12 07:59:16 -04:00
STATETREE_LOG ( Warning , TEXT ( " %hs: StateTree context is not initialized properly ('%s' using StateTree '%s') " ) ,
__FUNCTION__ , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & StateTree ) ) ;
2022-05-31 04:51:18 -04:00
return EStateTreeRunStatus : : Failed ;
}
2023-09-22 09:53:34 -04:00
if ( InstanceData . IsValid ( ) )
{
const FStateTreeExecutionState & Exec = GetExecState ( ) ;
if ( ! ensureMsgf ( Exec . CurrentPhase = = EStateTreeUpdatePhase : : Unset , TEXT ( " %hs can't be called while already in %s ('%s' using StateTree '%s'). " ) ,
__FUNCTION__ , * UEnum : : GetDisplayValueAsText ( Exec . CurrentPhase ) . ToString ( ) , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & StateTree ) ) )
{
return EStateTreeRunStatus : : Failed ;
}
// Stop if still running previous state.
Stop ( ) ;
}
2022-11-23 09:22:14 -05:00
2023-01-10 15:44:28 -05:00
// Initialize instance data. No active states yet, so we'll initialize the evals and global tasks.
InstanceData . Reset ( ) ;
2023-05-23 10:46:16 -04:00
constexpr FStateTreeActiveStates Empty ;
2023-01-10 15:44:28 -05:00
UpdateInstanceData ( Empty , Empty ) ;
2022-05-31 04:51:18 -04:00
if ( ! InstanceData . IsValid ( ) )
{
2023-04-12 07:59:16 -04:00
STATETREE_LOG ( Warning , TEXT ( " %hs: Failed to initialize instance data on '%s' using StateTree '%s'. Try to recompile the StateTree asset. " ) ,
__FUNCTION__ , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & StateTree ) ) ;
2023-01-10 15:44:28 -05:00
return EStateTreeRunStatus : : Failed ;
2022-05-31 04:51:18 -04:00
}
2022-09-07 17:12:18 -04:00
2023-05-29 10:13:21 -04:00
// Must sent instance creation event first
STATETREE_TRACE_INSTANCE_EVENT ( EStateTreeTraceEventType : : Push ) ;
// Set scoped phase only for properly initialized context with valid Instance data
// since we need it to output the InstanceId
STATETREE_TRACE_SCOPED_PHASE ( EStateTreeUpdatePhase : : StartTree ) ;
2023-09-22 09:53:34 -04:00
FStateTreeExecutionState * Exec = & GetExecState ( ) ; // Using pointer as we will need to reacquire the exec later.
// From this point any calls to Stop should be deferred.
Exec - > CurrentPhase = EStateTreeUpdatePhase : : StartTree ;
2023-01-10 15:44:28 -05:00
// Start evaluators and global tasks. Fail the execution if any global task fails.
FStateTreeIndex16 LastInitializedTaskIndex ;
2023-06-05 06:33:07 -04:00
const EStateTreeRunStatus GlobalTasksRunStatus = StartEvaluatorsAndGlobalTasks ( LastInitializedTaskIndex ) ;
2023-09-22 09:53:34 -04:00
if ( GlobalTasksRunStatus = = EStateTreeRunStatus : : Running )
2023-01-10 15:44:28 -05:00
{
2023-09-22 09:53:34 -04:00
// First tick.
// Tasks are not ticked here, since their behavior is that EnterState() (called above) is treated as a tick.
TickEvaluatorsAndGlobalTasks ( 0.0f , /*bTickGlobalTasks*/ false ) ;
2022-05-31 04:51:18 -04:00
2023-09-22 09:53:34 -04:00
// Initialize to unset running state.
Exec - > TreeRunStatus = EStateTreeRunStatus : : Running ;
Exec - > ActiveStates . Reset ( ) ;
Exec - > LastTickStatus = EStateTreeRunStatus : : Unset ;
2022-05-31 04:51:18 -04:00
2023-09-22 09:53:34 -04:00
static const FStateTreeStateHandle RootState = FStateTreeStateHandle ( 0 ) ;
2022-05-31 04:51:18 -04:00
2023-09-22 09:53:34 -04:00
FStateTreeActiveStates NextActiveStates ;
FStateTreeActiveStates VisitedStates ;
if ( SelectState ( RootState , NextActiveStates , VisitedStates ) )
2022-05-31 04:51:18 -04:00
{
2023-09-22 09:53:34 -04:00
if ( NextActiveStates . Last ( ) . IsCompletionState ( ) )
2022-05-31 04:51:18 -04:00
{
2023-09-22 09:53:34 -04:00
// Transition to a terminal state (succeeded/failed), or default transition failed.
STATETREE_LOG ( Warning , TEXT ( " %hs: Tree %s at StateTree start on '%s' using StateTree '%s'. " ) ,
__FUNCTION__ , NextActiveStates . Last ( ) = = FStateTreeStateHandle : : Succeeded ? TEXT ( " succeeded " ) : TEXT ( " failed " ) , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & StateTree ) ) ;
Exec - > TreeRunStatus = NextActiveStates . Last ( ) . ToCompletionStatus ( ) ;
}
else
{
// Enter state tasks can fail/succeed, treat it same as tick.
FStateTreeTransitionResult Transition ;
Transition . TargetState = RootState ;
Transition . CurrentActiveStates = Exec - > ActiveStates ;
Transition . CurrentRunStatus = Exec - > LastTickStatus ;
Transition . NextActiveStates = NextActiveStates ; // Enter state will update Exec.ActiveStates.
const EStateTreeRunStatus LastTickStatus = EnterState ( Transition ) ;
// Need to reacquire the exec state as EnterState may alter the allocation.
Exec = & GetExecState ( ) ;
Exec - > LastTickStatus = LastTickStatus ;
STATETREE_TRACE_ACTIVE_STATES_EVENT ( Exec - > ActiveStates ) ;
// Report state completed immediately.
if ( Exec - > LastTickStatus ! = EStateTreeRunStatus : : Running )
{
StateCompleted ( ) ;
}
2022-05-31 04:51:18 -04:00
}
}
2023-09-22 09:53:34 -04:00
if ( Exec - > ActiveStates . IsEmpty ( ) )
{
// Should not happen. This may happen if initial state could not be selected.
STATETREE_LOG ( Error , TEXT ( " %hs: Failed to select initial state on '%s' using StateTree '%s'. This should not happen, check that the StateTree logic can always select a state at start. " ) ,
__FUNCTION__ , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & StateTree ) ) ;
Exec - > TreeRunStatus = EStateTreeRunStatus : : Failed ;
}
}
else
2022-05-31 04:51:18 -04:00
{
2023-09-22 09:53:34 -04:00
StopEvaluatorsAndGlobalTasks ( GlobalTasksRunStatus , LastInitializedTaskIndex ) ;
STATETREE_LOG ( VeryVerbose , TEXT ( " %hs: Global tasks completed the StateTree %s on start in status '%s'. " ) ,
__FUNCTION__ , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & StateTree ) , * UEnum : : GetDisplayValueAsText ( GlobalTasksRunStatus ) . ToString ( ) ) ;
// We are not considered as running yet so we only set the status without requiring a stop.
Exec - > TreeRunStatus = GlobalTasksRunStatus ;
2022-05-31 04:51:18 -04:00
}
2023-09-22 09:53:34 -04:00
// Reset phase since we are now safe to stop.
Exec - > CurrentPhase = EStateTreeUpdatePhase : : Unset ;
// Use local for resulting run state since Stop will reset the instance data.
EStateTreeRunStatus Result = Exec - > TreeRunStatus ;
if ( Exec - > RequestedStop ! = EStateTreeRunStatus : : Unset )
{
STATETREE_LOG_AND_TRACE ( VeryVerbose , TEXT ( " Processing Deferred Stop " ) ) ;
Result = Stop ( Exec - > RequestedStop ) ;
}
return Result ;
2022-05-31 04:51:18 -04:00
}
2023-09-22 09:53:34 -04:00
EStateTreeRunStatus FStateTreeExecutionContext : : Stop ( EStateTreeRunStatus CompletionStatus )
2022-05-31 04:51:18 -04:00
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_Stop ) ;
2022-09-23 20:02:42 -04:00
if ( ! IsValid ( ) )
2022-05-31 04:51:18 -04:00
{
2023-04-12 07:59:16 -04:00
STATETREE_LOG ( Warning , TEXT ( " %hs: StateTree context is not initialized properly ('%s' using StateTree '%s') " ) ,
__FUNCTION__ , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & StateTree ) ) ;
2022-05-31 04:51:18 -04:00
return EStateTreeRunStatus : : Failed ;
}
if ( ! InstanceData . IsValid ( ) )
{
return EStateTreeRunStatus : : Failed ;
}
2022-11-23 09:22:14 -05:00
2023-05-29 10:13:21 -04:00
// Set scoped phase only for properly initialized context with valid Instance data
// since we need it to output the InstanceId
STATETREE_TRACE_SCOPED_PHASE ( EStateTreeUpdatePhase : : StopTree ) ;
2023-09-22 09:53:34 -04:00
// Make sure that we return a valid completion status (i.e. Succeeded, Failed or Stopped)
if ( CompletionStatus = = EStateTreeRunStatus : : Unset
| | CompletionStatus = = EStateTreeRunStatus : : Running )
{
CompletionStatus = EStateTreeRunStatus : : Stopped ;
}
FStateTreeExecutionState & Exec = GetExecState ( ) ;
// A reentrant call to Stop or a call from Start or Tick must be deferred.
if ( Exec . CurrentPhase ! = EStateTreeUpdatePhase : : Unset )
{
STATETREE_LOG_AND_TRACE ( VeryVerbose , TEXT ( " Deferring Stop at end of %s " ) , * UEnum : : GetDisplayValueAsText ( Exec . CurrentPhase ) . ToString ( ) ) ;
Exec . RequestedStop = CompletionStatus ;
return EStateTreeRunStatus : : Running ;
}
// No need to clear on exit since we reset all the instance data before leaving the function.
Exec . CurrentPhase = EStateTreeUpdatePhase : : StopTree ;
2023-01-10 15:44:28 -05:00
EStateTreeRunStatus Result = Exec . TreeRunStatus ;
2022-09-01 09:06:53 -04:00
// Capture events added between ticks.
2022-11-30 07:17:26 -05:00
FStateTreeEventQueue & EventQueue = InstanceData . GetMutableEventQueue ( ) ;
2022-11-01 15:11:19 -04:00
EventsToProcess = EventQueue . GetEvents ( ) ;
EventQueue . Reset ( ) ;
2022-09-01 09:06:53 -04:00
2022-05-31 04:51:18 -04:00
// Exit states if still in some valid state.
2023-06-05 06:33:07 -04:00
if ( Exec . TreeRunStatus = = EStateTreeRunStatus : : Running )
2022-05-31 04:51:18 -04:00
{
// Transition to Succeeded state.
FStateTreeTransitionResult Transition ;
2023-06-05 06:33:07 -04:00
Transition . TargetState = FStateTreeStateHandle : : FromCompletionStatus ( CompletionStatus ) ;
2022-05-31 04:51:18 -04:00
Transition . CurrentActiveStates = Exec . ActiveStates ;
2023-06-05 06:33:07 -04:00
Transition . CurrentRunStatus = CompletionStatus ;
Transition . NextActiveStates = FStateTreeActiveStates ( Transition . TargetState ) ;
2022-05-31 04:51:18 -04:00
2023-06-05 06:33:07 -04:00
if ( ! Exec . ActiveStates . IsEmpty ( ) )
{
ExitState ( Transition ) ;
}
2022-05-31 04:51:18 -04:00
2023-06-05 06:33:07 -04:00
// Stop evaluators and global tasks.
StopEvaluatorsAndGlobalTasks ( CompletionStatus ) ;
Result = CompletionStatus ;
2022-05-31 04:51:18 -04:00
}
2023-03-14 13:35:46 -04:00
// Trace before resetting the instance data since it is required to provide all the event information
2023-05-29 10:13:21 -04:00
STATETREE_TRACE_ACTIVE_STATES_EVENT ( FStateTreeActiveStates ( ) ) ;
STATETREE_TRACE_INSTANCE_EVENT ( EStateTreeTraceEventType : : Pop ) ;
2023-03-14 13:35:46 -04:00
2022-05-31 04:51:18 -04:00
// Destruct all allocated instance data (does not shrink the buffer). This will invalidate Exec too.
InstanceData . Reset ( ) ;
return Result ;
}
2022-09-23 20:02:42 -04:00
EStateTreeRunStatus FStateTreeExecutionContext : : Tick ( const float DeltaTime )
2022-05-31 04:51:18 -04:00
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_Tick ) ;
2023-05-29 10:13:21 -04:00
STATETREE_TRACE_SCOPED_PHASE ( EStateTreeUpdatePhase : : TickStateTree ) ;
2022-05-31 04:51:18 -04:00
2022-09-23 20:02:42 -04:00
if ( ! IsValid ( ) )
2022-05-31 04:51:18 -04:00
{
2023-04-12 07:59:16 -04:00
STATETREE_LOG ( Warning , TEXT ( " %hs: StateTree context is not initialized properly ('%s' using StateTree '%s') " ) ,
__FUNCTION__ , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & StateTree ) ) ;
2022-05-31 04:51:18 -04:00
return EStateTreeRunStatus : : Failed ;
}
2022-09-23 20:02:42 -04:00
2022-05-31 04:51:18 -04:00
if ( ! InstanceData . IsValid ( ) )
{
2023-04-12 07:59:16 -04:00
STATETREE_LOG ( Error , TEXT ( " %hs: Tick called on %s using StateTree %s with invalid instance data. Start() must be called before Tick(). " ) ,
__FUNCTION__ , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & StateTree ) ) ;
2022-05-31 04:51:18 -04:00
return EStateTreeRunStatus : : Failed ;
}
2022-09-01 09:06:53 -04:00
2022-11-30 07:17:26 -05:00
FStateTreeEventQueue & EventQueue = InstanceData . GetMutableEventQueue ( ) ;
2022-09-23 20:02:42 -04:00
FStateTreeExecutionState * Exec = & GetExecState ( ) ;
2023-09-22 09:53:34 -04:00
// No ticking if the tree is done or stopped.
2022-05-31 04:51:18 -04:00
if ( Exec - > TreeRunStatus ! = EStateTreeRunStatus : : Running )
{
return Exec - > TreeRunStatus ;
}
2023-09-22 09:53:34 -04:00
if ( ! ensureMsgf ( Exec - > CurrentPhase = = EStateTreeUpdatePhase : : Unset , TEXT ( " %hs can't be called while already in %s ('%s' using StateTree '%s'). " ) ,
__FUNCTION__ , * UEnum : : GetDisplayValueAsText ( Exec - > CurrentPhase ) . ToString ( ) , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & StateTree ) ) )
{
return EStateTreeRunStatus : : Failed ;
}
// From this point any calls to Stop should be deferred.
Exec - > CurrentPhase = EStateTreeUpdatePhase : : TickStateTree ;
// Capture events added between ticks.
EventsToProcess = EventQueue . GetEvents ( ) ;
EventQueue . Reset ( ) ;
2022-12-02 07:57:31 -05:00
// Update the delayed transitions.
for ( FStateTreeTransitionDelayedState & DelayedState : Exec - > DelayedTransitions )
2022-05-31 04:51:18 -04:00
{
2022-12-02 07:57:31 -05:00
DelayedState . TimeLeft - = DeltaTime ;
2022-05-31 04:51:18 -04:00
}
2023-01-23 12:48:04 -05:00
2023-01-10 15:44:28 -05:00
// Tick global evaluators and tasks.
const EStateTreeRunStatus EvalAndGlobalTaskStatus = TickEvaluatorsAndGlobalTasks ( DeltaTime ) ;
2023-09-22 09:53:34 -04:00
if ( EvalAndGlobalTaskStatus = = EStateTreeRunStatus : : Running )
2023-01-10 15:44:28 -05:00
{
2023-09-22 09:53:34 -04:00
if ( Exec - > LastTickStatus = = EStateTreeRunStatus : : Running )
2022-05-31 04:51:18 -04:00
{
2023-09-22 09:53:34 -04:00
// Tick tasks on active states.
Exec - > LastTickStatus = TickTasks ( DeltaTime ) ;
2023-01-23 12:48:04 -05:00
2022-05-31 04:51:18 -04:00
// Report state completed immediately.
if ( Exec - > LastTickStatus ! = EStateTreeRunStatus : : Running )
{
2022-09-23 20:02:42 -04:00
StateCompleted ( ) ;
2022-05-31 04:51:18 -04:00
}
}
2023-01-23 12:48:04 -05:00
2023-09-22 09:53:34 -04:00
// The state selection is repeated up to MaxIteration time. This allows failed EnterState() to potentially find a new state immediately.
// This helps event driven StateTrees to not require another event/tick to find a suitable state.
static constexpr int32 MaxIterations = 5 ;
for ( int32 Iter = 0 ; Iter < MaxIterations ; Iter + + )
2022-05-31 04:51:18 -04:00
{
2023-09-22 09:53:34 -04:00
// Append events accumulated during the tick, so that transitions can immediately act on them.
// We'll consume the events only if they lead to state change below (EnterState is treated the same as Tick),
// or let them be processed next frame if no transition.
EventsToProcess . Append ( EventQueue . GetEvents ( ) ) ;
// Trigger conditional transitions or state succeed/failed transitions. First tick transition is handled here too.
if ( TriggerTransitions ( ) )
{
STATETREE_TRACE_SCOPED_PHASE ( EStateTreeUpdatePhase : : ApplyTransitions ) ;
STATETREE_TRACE_TRANSITION_EVENT ( NextTransitionSource , EStateTreeTraceEventType : : OnTransition ) ;
NextTransitionSource . Reset ( ) ;
// We have committed to state change, consume events that were accumulated during the tick above.
EventQueue . Reset ( ) ;
ExitState ( NextTransition ) ;
// Tree succeeded or failed.
if ( NextTransition . TargetState . IsCompletionState ( ) )
{
// Transition to a terminal state (succeeded/failed), or default transition failed.
Exec - > TreeRunStatus = NextTransition . TargetState . ToCompletionStatus ( ) ;
Exec - > ActiveStates . Reset ( ) ;
// Stop evaluators and global tasks.
StopEvaluatorsAndGlobalTasks ( Exec - > TreeRunStatus ) ;
break ;
}
// Append and consume the events accumulated during the state exit.
EventsToProcess . Append ( EventQueue . GetEvents ( ) ) ;
EventQueue . Reset ( ) ;
// Enter state tasks can fail/succeed, treat it same as tick.
const EStateTreeRunStatus LastTickStatus = EnterState ( NextTransition ) ;
NextTransition . Reset ( ) ;
// Need to reacquire the exec state as EnterState may alter the allocation.
Exec = & GetExecState ( ) ;
Exec - > LastTickStatus = LastTickStatus ;
STATETREE_TRACE_ACTIVE_STATES_EVENT ( Exec - > ActiveStates ) ;
// Consider events so far processed. Events sent during EnterState went into EventQueue, and are processed in next iteration.
EventsToProcess . Reset ( ) ;
// Report state completed immediately.
if ( Exec - > LastTickStatus ! = EStateTreeRunStatus : : Running )
{
StateCompleted ( ) ;
}
}
// Stop as soon as have found a running state.
if ( Exec - > LastTickStatus = = EStateTreeRunStatus : : Running )
{
break ;
}
2022-05-31 04:51:18 -04:00
}
}
2023-09-22 09:53:34 -04:00
else
2022-05-31 04:51:18 -04:00
{
2023-09-22 09:53:34 -04:00
// If global tasks succeed or fail, stop the tree.
Exec - > RequestedStop = EvalAndGlobalTaskStatus ;
2022-05-31 04:51:18 -04:00
}
2022-09-01 09:06:53 -04:00
EventsToProcess . Reset ( ) ;
2023-09-22 09:53:34 -04:00
// Reset phase since we are now safe to stop.
Exec - > CurrentPhase = EStateTreeUpdatePhase : : Unset ;
// Use local for resulting run state since Stop will reset the instance data.
EStateTreeRunStatus Result = Exec - > TreeRunStatus ;
if ( Exec - > RequestedStop ! = EStateTreeRunStatus : : Unset )
{
STATETREE_LOG_AND_TRACE ( VeryVerbose , TEXT ( " Processing Deferred Stop " ) ) ;
Result = Stop ( Exec - > RequestedStop ) ;
}
return Result ;
2022-05-31 04:51:18 -04:00
}
2022-12-16 05:45:13 -05:00
EStateTreeRunStatus FStateTreeExecutionContext : : GetStateTreeRunStatus ( ) const
{
if ( ! IsValid ( ) )
{
2023-04-12 07:59:16 -04:00
STATETREE_LOG ( Warning , TEXT ( " %hs: StateTree context is not initialized properly ('%s' using StateTree '%s') " ) ,
__FUNCTION__ , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & StateTree ) ) ;
2022-12-16 05:45:13 -05:00
return EStateTreeRunStatus : : Failed ;
}
2023-01-12 09:00:45 -05:00
if ( const FStateTreeExecutionState * Exec = InstanceData . GetExecutionState ( ) )
2022-12-16 05:45:13 -05:00
{
2023-01-12 09:00:45 -05:00
return Exec - > TreeRunStatus ;
2022-12-16 05:45:13 -05:00
}
2023-01-12 09:00:45 -05:00
return EStateTreeRunStatus : : Failed ;
2022-12-16 05:45:13 -05:00
}
2022-11-01 15:11:19 -04:00
void FStateTreeExecutionContext : : SendEvent ( const FStateTreeEvent & Event ) const
{
SendEvent ( Event . Tag , Event . Payload , Event . Origin ) ;
}
void FStateTreeExecutionContext : : SendEvent ( const FGameplayTag Tag , const FConstStructView Payload , const FName Origin ) const
2022-09-01 09:06:53 -04:00
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_SendEvent ) ;
2022-09-23 20:02:42 -04:00
if ( ! IsValid ( ) )
2022-09-01 09:06:53 -04:00
{
2023-04-12 07:59:16 -04:00
STATETREE_LOG ( Warning , TEXT ( " %hs: StateTree context is not initialized properly ('%s' using StateTree '%s') " ) ,
__FUNCTION__ , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & StateTree ) ) ;
2022-09-01 09:06:53 -04:00
return ;
}
2022-09-23 20:02:42 -04:00
2022-09-01 09:06:53 -04:00
if ( ! InstanceData . IsValid ( ) )
{
2023-04-12 07:59:16 -04:00
STATETREE_LOG ( Error , TEXT ( " %hs: SendEvent called on %s using StateTree %s with invalid instance data. Start() must be called before sending events. " ) ,
__FUNCTION__ , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & StateTree ) ) ;
2022-09-01 09:06:53 -04:00
return ;
}
2023-09-22 09:53:34 -04:00
STATETREE_LOG_AND_TRACE ( Verbose , TEXT ( " Send Event '%s' " ) , * Tag . ToString ( ) ) ;
2022-11-03 14:21:53 -04:00
2022-11-30 07:17:26 -05:00
FStateTreeEventQueue & EventQueue = InstanceData . GetMutableEventQueue ( ) ;
2022-11-01 15:11:19 -04:00
EventQueue . SendEvent ( & Owner , Tag , Payload , Origin ) ;
2022-09-01 09:06:53 -04:00
}
2023-01-23 12:48:04 -05:00
void FStateTreeExecutionContext : : RequestTransition ( const FStateTreeTransitionRequest & Request )
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_RequestTransition ) ;
if ( ! IsValid ( ) )
{
2023-04-12 07:59:16 -04:00
STATETREE_LOG ( Warning , TEXT ( " %hs: StateTree context is not initialized properly ('%s' using StateTree '%s') " ) ,
__FUNCTION__ , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & StateTree ) ) ;
2023-01-23 12:48:04 -05:00
return ;
}
if ( ! InstanceData . IsValid ( ) )
{
2023-04-12 07:59:16 -04:00
STATETREE_LOG ( Error , TEXT ( " %hs: RequestTransition called on %s using StateTree %s with invalid instance data. Start() must be called before requesting transition. " ) ,
__FUNCTION__ , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & StateTree ) ) ;
2023-01-23 12:48:04 -05:00
return ;
}
STATETREE_LOG ( Verbose , TEXT ( " Request transition to '%s' at priority %s " ) , * GetSafeStateName ( Request . TargetState ) , * UEnum : : GetDisplayValueAsText ( Request . Priority ) . ToString ( ) ) ;
if ( bAllowDirectTransitions )
{
2023-08-04 14:55:23 -04:00
if ( RequestTransition ( Request . TargetState , Request . Priority ) )
{
NextTransitionSource = FStateTreeTransitionSource ( EStateTreeTransitionSourceType : : ExternalRequest , Request . TargetState , Request . Priority ) ;
}
2023-01-23 12:48:04 -05:00
}
else
{
FStateTreeTransitionRequest RequestWithSource = Request ;
RequestWithSource . SourceState = CurrentlyProcessedState ;
InstanceData . AddTransitionRequest ( & Owner , RequestWithSource ) ;
}
}
2023-03-14 13:35:46 -04:00
# if WITH_STATETREE_DEBUGGER
FStateTreeInstanceDebugId FStateTreeExecutionContext : : GetInstanceDebugId ( ) const
{
FStateTreeInstanceDebugId & InstanceDebugId = GetExecState ( ) . InstanceDebugId ;
if ( ! InstanceDebugId . IsValid ( ) )
{
2023-03-16 11:43:35 -04:00
static std : : atomic < uint32 > SerialNumber = 0 ;
InstanceDebugId = FStateTreeInstanceDebugId ( GetTypeHash ( GetInstanceDescription ( ) ) , + + SerialNumber ) ;
2023-03-14 13:35:46 -04:00
}
return InstanceDebugId ;
}
# endif // WITH_STATETREE_DEBUGGER
2022-09-23 20:02:42 -04:00
void FStateTreeExecutionContext : : UpdateInstanceData ( const FStateTreeActiveStates & CurrentActiveStates , const FStateTreeActiveStates & NextActiveStates )
2022-09-01 09:06:53 -04:00
{
2022-05-31 04:51:18 -04:00
// Find common section of states at start.
int32 NumCommon = 0 ;
while ( NumCommon < CurrentActiveStates . Num ( ) & & NumCommon < NextActiveStates . Num ( ) )
{
if ( CurrentActiveStates [ NumCommon ] ! = NextActiveStates [ NumCommon ] )
{
break ;
}
NumCommon + + ;
}
// @todo: change this so that we only put the newly added structs and objects here.
TArray < FConstStructView > InstanceStructs ;
2022-11-30 07:17:26 -05:00
TArray < const UObject * > InstanceObjects ;
2022-05-31 04:51:18 -04:00
int32 NumCommonInstanceStructs = 0 ;
int32 NumCommonInstanceObjects = 0 ;
// Exec
2022-11-30 07:17:26 -05:00
InstanceStructs . Add ( StateTree . DefaultInstanceData . GetStruct ( 0 ) ) ;
2022-05-31 04:51:18 -04:00
// Evaluators
2022-09-23 20:02:42 -04:00
for ( int32 EvalIndex = StateTree . EvaluatorsBegin ; EvalIndex < ( StateTree . EvaluatorsBegin + StateTree . EvaluatorsNum ) ; EvalIndex + + )
2022-05-31 04:51:18 -04:00
{
2023-02-13 20:06:02 -05:00
const FStateTreeEvaluatorBase & Eval = StateTree . Nodes [ EvalIndex ] . Get < const FStateTreeEvaluatorBase > ( ) ;
2022-05-31 04:51:18 -04:00
if ( Eval . bInstanceIsObject )
{
2022-11-30 07:17:26 -05:00
InstanceObjects . Add ( StateTree . DefaultInstanceData . GetObject ( Eval . InstanceIndex . Get ( ) ) ) ;
2022-05-31 04:51:18 -04:00
}
else
{
2022-11-30 07:17:26 -05:00
InstanceStructs . Add ( StateTree . DefaultInstanceData . GetStruct ( Eval . InstanceIndex . Get ( ) ) ) ;
2022-05-31 04:51:18 -04:00
}
}
2023-01-10 15:44:28 -05:00
// Global tasks
for ( int32 TaskIndex = StateTree . GlobalTasksBegin ; TaskIndex < ( StateTree . GlobalTasksBegin + StateTree . GlobalTasksNum ) ; TaskIndex + + )
{
2023-02-13 20:06:02 -05:00
const FStateTreeTaskBase & Task = StateTree . Nodes [ TaskIndex ] . Get < const FStateTreeTaskBase > ( ) ;
2023-01-10 15:44:28 -05:00
if ( Task . bInstanceIsObject )
{
InstanceObjects . Add ( StateTree . DefaultInstanceData . GetObject ( Task . InstanceIndex . Get ( ) ) ) ;
}
else
{
InstanceStructs . Add ( StateTree . DefaultInstanceData . GetStruct ( Task . InstanceIndex . Get ( ) ) ) ;
}
}
2022-05-31 04:51:18 -04:00
// Expect initialized instance data to contain the common instances.
if ( InstanceData . IsValid ( ) )
{
NumCommonInstanceStructs = InstanceStructs . Num ( ) ;
NumCommonInstanceObjects = InstanceObjects . Num ( ) ;
}
// Tasks
const int32 FirstTaskStructIndex = InstanceStructs . Num ( ) ;
const int32 FirstTaskObjectIndex = InstanceObjects . Num ( ) ;
for ( int32 Index = 0 ; Index < NextActiveStates . Num ( ) ; Index + + )
{
const FStateTreeStateHandle CurrentHandle = NextActiveStates [ Index ] ;
2022-09-23 20:02:42 -04:00
const FCompactStateTreeState & State = StateTree . States [ CurrentHandle . Index ] ;
2022-05-31 04:51:18 -04:00
if ( State . Type = = EStateTreeStateType : : Linked )
{
check ( State . ParameterInstanceIndex . IsValid ( ) ) ;
2022-11-30 07:17:26 -05:00
InstanceStructs . Add ( StateTree . DefaultInstanceData . GetStruct ( State . ParameterInstanceIndex . Get ( ) ) ) ;
2022-05-31 04:51:18 -04:00
}
for ( int32 TaskIndex = State . TasksBegin ; TaskIndex < ( State . TasksBegin + State . TasksNum ) ; TaskIndex + + )
{
2023-02-13 20:06:02 -05:00
const FStateTreeTaskBase & Task = StateTree . Nodes [ TaskIndex ] . Get < const FStateTreeTaskBase > ( ) ;
2022-05-31 04:51:18 -04:00
if ( Task . bInstanceIsObject )
{
2022-11-30 07:17:26 -05:00
InstanceObjects . Add ( StateTree . DefaultInstanceData . GetObject ( Task . InstanceIndex . Get ( ) ) ) ;
2022-05-31 04:51:18 -04:00
}
else
{
2022-11-30 07:17:26 -05:00
InstanceStructs . Add ( StateTree . DefaultInstanceData . GetStruct ( Task . InstanceIndex . Get ( ) ) ) ;
2022-05-31 04:51:18 -04:00
}
}
if ( Index < NumCommon )
{
NumCommonInstanceStructs = InstanceStructs . Num ( ) ;
NumCommonInstanceObjects = InstanceObjects . Num ( ) ;
}
}
// Common section should match.
// @todo: put this behind a define when enough testing has been done.
for ( int32 Index = 0 ; Index < NumCommonInstanceStructs ; Index + + )
{
check ( Index < InstanceData . NumStructs ( ) ) ;
check ( InstanceStructs [ Index ] . GetScriptStruct ( ) = = InstanceData . GetStruct ( Index ) . GetScriptStruct ( ) ) ;
}
for ( int32 Index = 0 ; Index < NumCommonInstanceObjects ; Index + + )
{
check ( Index < InstanceData . NumObjects ( ) ) ;
check ( InstanceObjects [ Index ] ! = nullptr
& & InstanceData . GetObject ( Index ) ! = nullptr
& & InstanceObjects [ Index ] - > GetClass ( ) = = InstanceData . GetObject ( Index ) - > GetClass ( ) ) ;
}
// Remove instance data that was not common.
2022-11-30 07:17:26 -05:00
InstanceData . ShrinkTo ( NumCommonInstanceStructs , NumCommonInstanceObjects ) ;
2022-05-31 04:51:18 -04:00
// Add new instance data.
2022-09-23 20:02:42 -04:00
InstanceData . Append ( Owner ,
MakeArrayView ( InstanceStructs . GetData ( ) + NumCommonInstanceStructs , InstanceStructs . Num ( ) - NumCommonInstanceStructs ) ,
2022-05-31 04:51:18 -04:00
MakeArrayView ( InstanceObjects . GetData ( ) + NumCommonInstanceObjects , InstanceObjects . Num ( ) - NumCommonInstanceObjects ) ) ;
2022-09-23 20:02:42 -04:00
FStateTreeExecutionState & Exec = GetExecState ( ) ;
2022-05-31 04:51:18 -04:00
Exec . FirstTaskStructIndex = FStateTreeIndex16 ( FirstTaskStructIndex ) ;
Exec . FirstTaskObjectIndex = FStateTreeIndex16 ( FirstTaskObjectIndex ) ;
}
2022-09-23 20:02:42 -04:00
EStateTreeRunStatus FStateTreeExecutionContext : : EnterState ( const FStateTreeTransitionResult & Transition )
2022-05-31 04:51:18 -04:00
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_EnterState ) ;
if ( Transition . NextActiveStates . IsEmpty ( ) )
{
return EStateTreeRunStatus : : Failed ;
}
// Allocate new tasks.
2022-09-23 20:02:42 -04:00
UpdateInstanceData ( Transition . CurrentActiveStates , Transition . NextActiveStates ) ;
2022-05-31 04:51:18 -04:00
2022-09-23 20:02:42 -04:00
FStateTreeExecutionState & Exec = GetExecState ( ) ;
2022-05-31 04:51:18 -04:00
Exec . StateChangeCount + + ;
2022-11-22 08:13:54 -05:00
Exec . CompletedStateHandle = FStateTreeStateHandle : : Invalid ;
2023-02-09 04:20:59 -05:00
Exec . EnterStateFailedTaskIndex = FStateTreeIndex16 : : Invalid ; // This will make all tasks to be accepted.
Exec . ActiveStates . Reset ( ) ;
2022-05-31 04:51:18 -04:00
// On target branch means that the state is the target of current transition or child of it.
// States which were active before and will remain active, but are not on target branch will not get
// EnterState called. That is, a transition is handled as "replan from this state".
bool bOnTargetBranch = false ;
FStateTreeTransitionResult CurrentTransition = Transition ;
EStateTreeRunStatus Result = EStateTreeRunStatus : : Running ;
2023-02-09 04:20:59 -05:00
int32 InstanceStructIndex = 1 ; // Exec is at index 0
int32 InstanceObjectIndex = 0 ;
2022-05-31 04:51:18 -04:00
2023-02-09 04:20:59 -05:00
// Update data views for evaluators and global tasks as UpdateInstanceData() might have changed the location of the instance data.
// Evaluators
for ( int32 EvalIndex = StateTree . EvaluatorsBegin ; EvalIndex < ( StateTree . EvaluatorsBegin + StateTree . EvaluatorsNum ) ; EvalIndex + + )
{
2023-02-13 20:06:02 -05:00
const FStateTreeEvaluatorBase & Eval = StateTree . Nodes [ EvalIndex ] . Get < const FStateTreeEvaluatorBase > ( ) ;
2023-02-09 04:20:59 -05:00
SetNodeDataView ( Eval , InstanceStructIndex , InstanceObjectIndex ) ;
}
// Global tasks
for ( int32 TaskIndex = StateTree . GlobalTasksBegin ; TaskIndex < ( StateTree . GlobalTasksBegin + StateTree . GlobalTasksNum ) ; TaskIndex + + )
{
2023-02-13 20:06:02 -05:00
const FStateTreeTaskBase & Task = StateTree . Nodes [ TaskIndex ] . Get < const FStateTreeTaskBase > ( ) ;
2023-02-09 04:20:59 -05:00
SetNodeDataView ( Task , InstanceStructIndex , InstanceObjectIndex ) ;
}
2022-05-31 04:51:18 -04:00
2022-11-03 14:21:53 -04:00
STATETREE_LOG ( Log , TEXT ( " Enter state '%s' (%d) " ) , * DebugGetStatePath ( Transition . NextActiveStates ) , Exec . StateChangeCount ) ;
2023-08-04 14:55:23 -04:00
STATETREE_TRACE_SCOPED_PHASE ( EStateTreeUpdatePhase : : EnterStates ) ;
2022-11-03 14:21:53 -04:00
2022-05-31 04:51:18 -04:00
for ( int32 Index = 0 ; Index < Transition . NextActiveStates . Num ( ) & & Result ! = EStateTreeRunStatus : : Failed ; Index + + )
{
const FStateTreeStateHandle CurrentHandle = Transition . NextActiveStates [ Index ] ;
const FStateTreeStateHandle PreviousHandle = Transition . CurrentActiveStates . GetStateSafe ( Index ) ;
2022-09-23 20:02:42 -04:00
const FCompactStateTreeState & State = StateTree . States [ CurrentHandle . Index ] ;
2022-05-31 04:51:18 -04:00
2023-08-15 14:21:54 -04:00
// Add only enabled States to the list of active States
if ( State . bEnabled & & ! Exec . ActiveStates . Push ( CurrentHandle ) )
2022-05-31 04:51:18 -04:00
{
2023-04-12 07:59:16 -04:00
STATETREE_LOG ( Error , TEXT ( " %hs: Reached max execution depth when trying to enter state '%s'. '%s' using StateTree '%s'. " ) ,
__FUNCTION__ , * GetStateStatusString ( Exec ) , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & StateTree ) ) ;
2022-05-31 04:51:18 -04:00
break ;
}
2023-05-29 10:13:21 -04:00
2022-05-31 04:51:18 -04:00
if ( State . Type = = EStateTreeStateType : : Linked )
{
2022-09-23 20:02:42 -04:00
UpdateLinkedStateParameters ( State , InstanceStructIndex ) ;
2022-05-31 04:51:18 -04:00
InstanceStructIndex + + ;
}
else if ( State . Type = = EStateTreeStateType : : Subtree )
{
2022-09-23 20:02:42 -04:00
UpdateSubtreeStateParameters ( State ) ;
2022-05-31 04:51:18 -04:00
}
bOnTargetBranch = bOnTargetBranch | | CurrentHandle = = Transition . TargetState ;
const bool bWasActive = PreviousHandle = = CurrentHandle ;
2023-08-15 14:21:54 -04:00
// Do not enter a disabled State tasks but maintain property bindings
const bool bIsEnteringState = ( ! bWasActive | | bOnTargetBranch ) & & State . bEnabled ;
2022-05-31 04:51:18 -04:00
CurrentTransition . CurrentState = CurrentHandle ;
2022-09-28 09:55:53 -04:00
CurrentTransition . ChangeType = bWasActive ? EStateTreeStateChangeType : : Sustained : EStateTreeStateChangeType : : Changed ;
2022-05-31 04:51:18 -04:00
2023-06-09 15:27:31 -04:00
if ( bIsEnteringState )
{
2023-08-04 14:55:23 -04:00
STATETREE_TRACE_STATE_EVENT ( CurrentHandle , EStateTreeTraceEventType : : OnEntering ) ;
2023-06-09 15:27:31 -04:00
STATETREE_LOG ( Log , TEXT ( " %*sState '%s' %s " ) , Index * UE : : StateTree : : DebugIndentSize , TEXT ( " " ) ,
* DebugGetStatePath ( Transition . NextActiveStates , Index ) ,
* UEnum : : GetDisplayValueAsText ( CurrentTransition . ChangeType ) . ToString ( ) ) ;
}
2022-05-31 04:51:18 -04:00
// Activate tasks on current state.
for ( int32 TaskIndex = State . TasksBegin ; TaskIndex < ( State . TasksBegin + State . TasksNum ) ; TaskIndex + + )
{
2023-02-13 20:06:02 -05:00
const FStateTreeTaskBase & Task = StateTree . Nodes [ TaskIndex ] . Get < const FStateTreeTaskBase > ( ) ;
2022-09-23 20:02:42 -04:00
SetNodeDataView ( Task , InstanceStructIndex , InstanceObjectIndex ) ;
2022-05-31 04:51:18 -04:00
// Copy bound properties.
if ( Task . BindingsBatch . IsValid ( ) )
{
2022-09-23 20:02:42 -04:00
StateTree . PropertyBindings . CopyTo ( DataViews , Task . BindingsBatch , DataViews [ Task . DataViewIndex . Get ( ) ] ) ;
2022-05-31 04:51:18 -04:00
}
2023-06-05 13:12:19 -04:00
// Ignore disabled task
if ( Task . bTaskEnabled = = false )
{
STATETREE_LOG ( VeryVerbose , TEXT ( " %*sSkipped 'EnterState' for disabled Task: '%s' " ) , UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * Task . Name . ToString ( ) ) ;
continue ;
}
2022-09-28 09:55:53 -04:00
const bool bShouldCallStateChange = CurrentTransition . ChangeType = = EStateTreeStateChangeType : : Changed
| | ( CurrentTransition . ChangeType = = EStateTreeStateChangeType : : Sustained & & Task . bShouldStateChangeOnReselect ) ;
if ( bIsEnteringState & & bShouldCallStateChange )
2022-05-31 04:51:18 -04:00
{
2022-11-03 14:21:53 -04:00
STATETREE_LOG ( Verbose , TEXT ( " %*s Task '%s' " ) , Index * UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * Task . Name . ToString ( ) ) ;
2023-04-19 13:26:23 -04:00
EStateTreeRunStatus Status = EStateTreeRunStatus : : Unset ;
{
QUICK_SCOPE_CYCLE_COUNTER ( StateTree_Task_EnterState ) ;
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_Task_EnterState ) ;
Status = Task . EnterState ( * this , CurrentTransition ) ;
}
2022-11-22 08:13:54 -05:00
2023-08-04 14:55:23 -04:00
STATETREE_TRACE_TASK_EVENT ( TaskIndex , DataViews [ Task . DataViewIndex . Get ( ) ] , EStateTreeTraceEventType : : OnEntered , Status ) ;
2023-06-09 15:27:31 -04:00
2022-11-22 08:13:54 -05:00
if ( Status ! = EStateTreeRunStatus : : Running )
{
// Store the first state that completed, will be used to decide where to trigger transitions.
if ( ! Exec . CompletedStateHandle . IsValid ( ) )
{
Exec . CompletedStateHandle = CurrentHandle ;
}
Result = Status ;
}
2022-05-31 04:51:18 -04:00
if ( Status = = EStateTreeRunStatus : : Failed )
{
2022-11-22 08:13:54 -05:00
// Store how far in the enter state we got. This will be used to match the StateCompleted() and ExitState() calls.
2022-05-31 04:51:18 -04:00
Exec . EnterStateFailedTaskIndex = FStateTreeIndex16 ( TaskIndex ) ;
break ;
}
}
}
2023-08-04 14:55:23 -04:00
if ( bIsEnteringState )
{
STATETREE_TRACE_STATE_EVENT ( CurrentHandle , EStateTreeTraceEventType : : OnEntered ) ;
}
2022-05-31 04:51:18 -04:00
}
return Result ;
}
2022-09-23 20:02:42 -04:00
void FStateTreeExecutionContext : : ExitState ( const FStateTreeTransitionResult & Transition )
2022-05-31 04:51:18 -04:00
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_ExitState ) ;
if ( Transition . CurrentActiveStates . IsEmpty ( ) )
{
return ;
}
2022-09-23 20:02:42 -04:00
FStateTreeExecutionState & Exec = GetExecState ( ) ;
2022-05-31 04:51:18 -04:00
// On target branch means that the state is the target of current transition or child of it.
// States which were active before and will remain active, but are not on target branch will not get
// EnterState called. That is, a transition is handled as "replan from this state".
bool bOnTargetBranch = false ;
FStateTreeStateHandle ExitedStates [ FStateTreeActiveStates : : MaxStates ] ;
EStateTreeStateChangeType ExitedStateChangeType [ FStateTreeActiveStates : : MaxStates ] ;
int32 ExitedStateActiveIndex [ FStateTreeActiveStates : : MaxStates ] ;
int32 NumExitedStates = 0 ;
// Do property copy on all states, propagating the results from last tick.
// Collect the states that need to be called, the actual call is done below in reverse order.
check ( Exec . FirstTaskStructIndex . IsValid ( ) & & Exec . FirstTaskObjectIndex . IsValid ( ) ) ;
int32 InstanceStructIndex = Exec . FirstTaskStructIndex . Get ( ) ;
int32 InstanceObjectIndex = Exec . FirstTaskObjectIndex . Get ( ) ;
for ( int32 Index = 0 ; Index < Transition . CurrentActiveStates . Num ( ) ; Index + + )
{
const FStateTreeStateHandle CurrentHandle = Transition . CurrentActiveStates [ Index ] ;
const FStateTreeStateHandle NextHandle = Transition . NextActiveStates . GetStateSafe ( Index ) ;
2022-09-23 20:02:42 -04:00
const FCompactStateTreeState & State = StateTree . States [ CurrentHandle . Index ] ;
2022-05-31 04:51:18 -04:00
if ( State . Type = = EStateTreeStateType : : Linked )
{
2022-09-23 20:02:42 -04:00
UpdateLinkedStateParameters ( State , InstanceStructIndex ) ;
2022-05-31 04:51:18 -04:00
InstanceStructIndex + + ;
}
else if ( State . Type = = EStateTreeStateType : : Subtree )
{
2022-09-23 20:02:42 -04:00
UpdateSubtreeStateParameters ( State ) ;
2022-05-31 04:51:18 -04:00
}
const bool bRemainsActive = NextHandle = = CurrentHandle ;
bOnTargetBranch = bOnTargetBranch | | NextHandle = = Transition . TargetState ;
const EStateTreeStateChangeType ChangeType = bRemainsActive ? EStateTreeStateChangeType : : Sustained : EStateTreeStateChangeType : : Changed ;
if ( ! bRemainsActive | | bOnTargetBranch )
{
// Should call ExitState() on this state.
check ( NumExitedStates < FStateTreeActiveStates : : MaxStates ) ;
ExitedStates [ NumExitedStates ] = CurrentHandle ;
ExitedStateChangeType [ NumExitedStates ] = ChangeType ;
ExitedStateActiveIndex [ NumExitedStates ] = Index ;
NumExitedStates + + ;
}
// Do property copies, ExitState() is called below.
for ( int32 TaskIndex = State . TasksBegin ; TaskIndex < ( State . TasksBegin + State . TasksNum ) ; TaskIndex + + )
{
2023-02-13 20:06:02 -05:00
const FStateTreeTaskBase & Task = StateTree . Nodes [ TaskIndex ] . Get < const FStateTreeTaskBase > ( ) ;
2022-09-23 20:02:42 -04:00
SetNodeDataView ( Task , InstanceStructIndex , InstanceObjectIndex ) ;
2022-05-31 04:51:18 -04:00
// Copy bound properties.
2022-11-01 15:11:19 -04:00
if ( Task . BindingsBatch . IsValid ( ) & & Task . bShouldCopyBoundPropertiesOnExitState )
2022-05-31 04:51:18 -04:00
{
2022-09-23 20:02:42 -04:00
StateTree . PropertyBindings . CopyTo ( DataViews , Task . BindingsBatch , DataViews [ Task . DataViewIndex . Get ( ) ] ) ;
2022-05-31 04:51:18 -04:00
}
}
}
// Call in reverse order.
2022-11-03 14:21:53 -04:00
STATETREE_LOG ( Log , TEXT ( " Exit state '%s' (%d) " ) , * DebugGetStatePath ( Transition . CurrentActiveStates ) , Exec . StateChangeCount ) ;
2023-08-04 14:55:23 -04:00
STATETREE_TRACE_SCOPED_PHASE ( EStateTreeUpdatePhase : : ExitStates ) ;
2022-11-03 14:21:53 -04:00
2022-05-31 04:51:18 -04:00
FStateTreeTransitionResult CurrentTransition = Transition ;
for ( int32 Index = NumExitedStates - 1 ; Index > = 0 ; Index - - )
{
const FStateTreeStateHandle CurrentHandle = ExitedStates [ Index ] ;
2022-09-23 20:02:42 -04:00
const FCompactStateTreeState & State = StateTree . States [ CurrentHandle . Index ] ;
2022-12-02 07:57:31 -05:00
// Remove any delayed transitions that belong to this state.
Exec . DelayedTransitions . RemoveAllSwap (
[ Begin = State . TransitionsBegin , End = State . TransitionsBegin + State . TransitionsNum ] ( const FStateTreeTransitionDelayedState & DelayedState )
{
return DelayedState . TransitionIndex . Get ( ) > = Begin & & DelayedState . TransitionIndex . Get ( ) < End ;
} ) ;
2022-09-28 09:55:53 -04:00
2022-05-31 04:51:18 -04:00
CurrentTransition . CurrentState = CurrentHandle ;
2022-09-28 09:55:53 -04:00
CurrentTransition . ChangeType = ExitedStateChangeType [ Index ] ;
2022-11-03 14:21:53 -04:00
STATETREE_LOG ( Log , TEXT ( " %*sState '%s' %s " ) , Index * UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * DebugGetStatePath ( Transition . CurrentActiveStates , ExitedStateActiveIndex [ Index ] ) , * UEnum : : GetDisplayValueAsText ( CurrentTransition . ChangeType ) . ToString ( ) ) ;
2023-08-04 14:55:23 -04:00
STATETREE_TRACE_STATE_EVENT ( CurrentHandle , EStateTreeTraceEventType : : OnExiting ) ;
2022-05-31 04:51:18 -04:00
// Tasks
for ( int32 TaskIndex = ( State . TasksBegin + State . TasksNum ) - 1 ; TaskIndex > = State . TasksBegin ; TaskIndex - - )
{
// Call task completed only if EnterState() was called.
// The task order in the tree (BF) allows us to use the comparison.
// Relying here that invalid value of Exec.EnterStateFailedTaskIndex == MAX_uint16.
if ( TaskIndex < = Exec . EnterStateFailedTaskIndex . Get ( ) )
{
2023-02-13 20:06:02 -05:00
const FStateTreeTaskBase & Task = StateTree . Nodes [ TaskIndex ] . Get < const FStateTreeTaskBase > ( ) ;
2022-05-31 04:51:18 -04:00
2023-06-05 13:12:19 -04:00
// Ignore disabled task
if ( Task . bTaskEnabled = = false )
{
STATETREE_LOG ( VeryVerbose , TEXT ( " %*sSkipped 'ExitState' for disabled Task: '%s' " ) , UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * Task . Name . ToString ( ) ) ;
continue ;
}
2022-09-28 09:55:53 -04:00
const bool bShouldCallStateChange = CurrentTransition . ChangeType = = EStateTreeStateChangeType : : Changed
| | ( CurrentTransition . ChangeType = = EStateTreeStateChangeType : : Sustained & & Task . bShouldStateChangeOnReselect ) ;
if ( bShouldCallStateChange )
2022-05-31 04:51:18 -04:00
{
2022-11-03 14:21:53 -04:00
STATETREE_LOG ( Verbose , TEXT ( " %*s Task '%s' " ) , Index * UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * Task . Name . ToString ( ) ) ;
2022-09-28 09:55:53 -04:00
{
QUICK_SCOPE_CYCLE_COUNTER ( StateTree_Task_ExitState ) ;
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_Task_ExitState ) ;
Task . ExitState ( * this , CurrentTransition ) ;
}
2023-08-04 14:55:23 -04:00
STATETREE_TRACE_TASK_EVENT ( TaskIndex , DataViews [ Task . DataViewIndex . Get ( ) ] , EStateTreeTraceEventType : : OnExited , Transition . CurrentRunStatus ) ;
2022-05-31 04:51:18 -04:00
}
}
}
2023-08-04 14:55:23 -04:00
STATETREE_TRACE_STATE_EVENT ( CurrentHandle , EStateTreeTraceEventType : : OnExited ) ;
2022-05-31 04:51:18 -04:00
}
}
2022-09-23 20:02:42 -04:00
void FStateTreeExecutionContext : : StateCompleted ( )
2022-05-31 04:51:18 -04:00
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_StateCompleted ) ;
2022-09-23 20:02:42 -04:00
const FStateTreeExecutionState & Exec = GetExecState ( ) ;
2022-05-31 04:51:18 -04:00
if ( Exec . ActiveStates . IsEmpty ( ) )
{
return ;
}
2022-11-03 14:21:53 -04:00
STATETREE_LOG ( Verbose , TEXT ( " State Completed %s (%d) " ) , * UEnum : : GetDisplayValueAsText ( Exec . LastTickStatus ) . ToString ( ) , Exec . StateChangeCount ) ;
2022-05-31 04:51:18 -04:00
// Call from child towards root to allow to pass results back.
// Note: Completed is assumed to be called immediately after tick or enter state, so there's no property copying.
for ( int32 Index = Exec . ActiveStates . Num ( ) - 1 ; Index > = 0 ; Index - - )
{
const FStateTreeStateHandle CurrentHandle = Exec . ActiveStates [ Index ] ;
2022-09-23 20:02:42 -04:00
const FCompactStateTreeState & State = StateTree . States [ CurrentHandle . Index ] ;
2022-05-31 04:51:18 -04:00
2023-01-23 12:48:04 -05:00
FCurrentlyProcessedStateScope StateScope ( * this , CurrentHandle ) ;
2022-11-03 14:21:53 -04:00
STATETREE_LOG ( Verbose , TEXT ( " %*sState '%s' " ) , Index * UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * DebugGetStatePath ( Exec . ActiveStates , Index ) ) ;
2023-05-29 10:13:21 -04:00
STATETREE_TRACE_STATE_EVENT ( CurrentHandle , EStateTreeTraceEventType : : OnStateCompleted ) ;
2022-05-31 04:51:18 -04:00
// Notify Tasks
for ( int32 TaskIndex = ( State . TasksBegin + State . TasksNum ) - 1 ; TaskIndex > = State . TasksBegin ; TaskIndex - - )
{
// Call task completed only if EnterState() was called.
// The task order in the tree (BF) allows us to use the comparison.
// Relying here that invalid value of Exec.EnterStateFailedTaskIndex == MAX_uint16.
if ( TaskIndex < = Exec . EnterStateFailedTaskIndex . Get ( ) )
{
2023-02-13 20:06:02 -05:00
const FStateTreeTaskBase & Task = StateTree . Nodes [ TaskIndex ] . Get < const FStateTreeTaskBase > ( ) ;
2022-05-31 04:51:18 -04:00
2023-06-05 13:12:19 -04:00
// Ignore disabled task
if ( Task . bTaskEnabled = = false )
{
STATETREE_LOG ( VeryVerbose , TEXT ( " %*sSkipped 'StateCompleted' for disabled Task: '%s' " ) , UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * Task . Name . ToString ( ) ) ;
continue ;
}
2022-11-03 14:21:53 -04:00
STATETREE_LOG ( Verbose , TEXT ( " %*s Task '%s' " ) , Index * UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * Task . Name . ToString ( ) ) ;
2022-05-31 04:51:18 -04:00
Task . StateCompleted ( * this , Exec . LastTickStatus , Exec . ActiveStates ) ;
}
}
}
}
2023-05-23 10:46:16 -04:00
EStateTreeRunStatus FStateTreeExecutionContext : : TickEvaluatorsAndGlobalTasks ( const float DeltaTime , const bool bTickGlobalTasks )
2022-05-31 04:51:18 -04:00
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_TickEvaluators ) ;
2023-05-29 10:13:21 -04:00
STATETREE_TRACE_SCOPED_PHASE ( EStateTreeUpdatePhase : : TickingGlobalTasks ) ;
2022-05-31 04:51:18 -04:00
2023-03-14 13:35:46 -04:00
STATETREE_CLOG ( StateTree . EvaluatorsNum > 0 , VeryVerbose , TEXT ( " Ticking Evaluators " ) ) ;
2022-05-31 04:51:18 -04:00
// Tick evaluators
int32 InstanceStructIndex = 1 ; // Exec is at index 0
int32 InstanceObjectIndex = 0 ;
2022-09-23 20:02:42 -04:00
for ( int32 EvalIndex = StateTree . EvaluatorsBegin ; EvalIndex < ( StateTree . EvaluatorsBegin + StateTree . EvaluatorsNum ) ; EvalIndex + + )
2022-05-31 04:51:18 -04:00
{
2023-02-13 20:06:02 -05:00
const FStateTreeEvaluatorBase & Eval = StateTree . Nodes [ EvalIndex ] . Get < const FStateTreeEvaluatorBase > ( ) ;
2022-09-23 20:02:42 -04:00
SetNodeDataView ( Eval , InstanceStructIndex , InstanceObjectIndex ) ;
2022-05-31 04:51:18 -04:00
// Copy bound properties.
if ( Eval . BindingsBatch . IsValid ( ) )
{
2022-09-23 20:02:42 -04:00
StateTree . PropertyBindings . CopyTo ( DataViews , Eval . BindingsBatch , DataViews [ Eval . DataViewIndex . Get ( ) ] ) ;
2022-05-31 04:51:18 -04:00
}
2023-03-14 13:35:46 -04:00
STATETREE_LOG ( VeryVerbose , TEXT ( " Tick: '%s' " ) , * Eval . Name . ToString ( ) ) ;
2022-05-31 04:51:18 -04:00
{
QUICK_SCOPE_CYCLE_COUNTER ( StateTree_Eval_Tick ) ;
Eval . Tick ( * this , DeltaTime ) ;
2023-08-07 18:06:40 -04:00
STATETREE_TRACE_EVALUATOR_EVENT ( EvalIndex , DataViews [ Eval . DataViewIndex . Get ( ) ] , EStateTreeTraceEventType : : OnTicked ) ;
2022-05-31 04:51:18 -04:00
}
}
2023-01-10 15:44:28 -05:00
EStateTreeRunStatus Result = EStateTreeRunStatus : : Running ;
if ( bTickGlobalTasks )
{
// Used to stop ticking tasks after one fails, but we still want to keep updating the data views so that property binding works properly.
bool bShouldTickTasks = true ;
const bool bHasEvents = ! EventsToProcess . IsEmpty ( ) ;
for ( int32 TaskIndex = StateTree . GlobalTasksBegin ; TaskIndex < ( StateTree . GlobalTasksBegin + StateTree . GlobalTasksNum ) ; TaskIndex + + )
{
2023-02-13 20:06:02 -05:00
const FStateTreeTaskBase & Task = StateTree . Nodes [ TaskIndex ] . Get < const FStateTreeTaskBase > ( ) ;
2023-06-09 05:20:18 -04:00
SetNodeDataView ( Task , InstanceStructIndex , InstanceObjectIndex ) ;
2023-06-05 13:12:19 -04:00
// Ignore disabled task
if ( Task . bTaskEnabled = = false )
{
STATETREE_LOG ( VeryVerbose , TEXT ( " %*sSkipped 'Tick' for disabled Task: '%s' " ) , UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * Task . Name . ToString ( ) ) ;
continue ;
}
2023-01-10 15:44:28 -05:00
const bool bNeedsTick = bShouldTickTasks & & ( Task . bShouldCallTick | | ( bHasEvents & & Task . bShouldCallTickOnlyOnEvents ) ) ;
STATETREE_LOG ( VeryVerbose , TEXT ( " Tick: '%s' %s " ) , * Task . Name . ToString ( ) , ! bNeedsTick ? TEXT ( " [not ticked] " ) : TEXT ( " " ) ) ;
if ( ! bNeedsTick )
{
continue ;
}
2023-06-09 15:27:31 -04:00
const FStateTreeDataView TaskDataView = DataViews [ Task . DataViewIndex . Get ( ) ] ;
2023-01-10 15:44:28 -05:00
// Copy bound properties.
// Only copy properties when the task is actually ticked, and copy properties at tick is requested.
if ( Task . BindingsBatch . IsValid ( ) & & Task . bShouldCopyBoundPropertiesOnTick )
{
2023-06-09 15:27:31 -04:00
StateTree . PropertyBindings . CopyTo ( DataViews , Task . BindingsBatch , TaskDataView ) ;
2023-01-10 15:44:28 -05:00
}
2023-08-04 14:55:23 -04:00
//STATETREE_TRACE_TASK_EVENT(TaskIndex, TaskDataView, EStateTreeTraceEventType::OnTickingTask, EStateTreeRunStatus::Running);
2023-04-19 13:26:23 -04:00
EStateTreeRunStatus TaskResult = EStateTreeRunStatus : : Unset ;
2023-01-10 15:44:28 -05:00
{
QUICK_SCOPE_CYCLE_COUNTER ( StateTree_Task_Tick ) ;
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_Task_Tick ) ;
2023-04-19 13:26:23 -04:00
TaskResult = Task . Tick ( * this , DeltaTime ) ;
}
2023-01-10 15:44:28 -05:00
2023-06-09 15:27:31 -04:00
STATETREE_TRACE_TASK_EVENT ( TaskIndex , TaskDataView ,
2023-08-07 18:06:40 -04:00
TaskResult ! = EStateTreeRunStatus : : Running ? EStateTreeTraceEventType : : OnTaskCompleted : EStateTreeTraceEventType : : OnTicked ,
2023-06-09 15:27:31 -04:00
TaskResult ) ;
2023-04-19 13:26:23 -04:00
// If a global task succeeds or fails, it will stop the whole tree.
if ( TaskResult ! = EStateTreeRunStatus : : Running )
{
Result = TaskResult ;
}
if ( TaskResult = = EStateTreeRunStatus : : Failed )
{
bShouldTickTasks = false ;
2023-01-10 15:44:28 -05:00
}
}
}
return Result ;
2022-05-31 04:51:18 -04:00
}
2023-06-05 06:33:07 -04:00
EStateTreeRunStatus FStateTreeExecutionContext : : StartEvaluatorsAndGlobalTasks ( FStateTreeIndex16 & OutLastInitializedTaskIndex )
2022-05-31 04:51:18 -04:00
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_StartEvaluators ) ;
2023-05-29 10:13:21 -04:00
STATETREE_TRACE_SCOPED_PHASE ( EStateTreeUpdatePhase : : StartGlobalTasks ) ;
2022-05-31 04:51:18 -04:00
2023-01-10 15:44:28 -05:00
STATETREE_CLOG ( StateTree . EvaluatorsNum > 0 | | StateTree . GlobalTasksNum > 0 , Verbose , TEXT ( " Start Evaluators & Global tasks " ) ) ;
2022-05-31 04:51:18 -04:00
2023-01-10 15:44:28 -05:00
OutLastInitializedTaskIndex = FStateTreeIndex16 ( ) ;
2023-06-05 06:33:07 -04:00
EStateTreeRunStatus Result = EStateTreeRunStatus : : Running ;
2022-05-31 04:51:18 -04:00
2023-01-10 15:44:28 -05:00
// Start evaluators
2022-05-31 04:51:18 -04:00
int32 InstanceStructIndex = 1 ; // Exec is at index 0
int32 InstanceObjectIndex = 0 ;
2022-09-23 20:02:42 -04:00
for ( int32 EvalIndex = StateTree . EvaluatorsBegin ; EvalIndex < ( StateTree . EvaluatorsBegin + StateTree . EvaluatorsNum ) ; EvalIndex + + )
2022-05-31 04:51:18 -04:00
{
2023-02-13 20:06:02 -05:00
const FStateTreeEvaluatorBase & Eval = StateTree . Nodes [ EvalIndex ] . Get < const FStateTreeEvaluatorBase > ( ) ;
2022-09-23 20:02:42 -04:00
SetNodeDataView ( Eval , InstanceStructIndex , InstanceObjectIndex ) ;
2022-05-31 04:51:18 -04:00
// Copy bound properties.
if ( Eval . BindingsBatch . IsValid ( ) )
{
2022-09-23 20:02:42 -04:00
StateTree . PropertyBindings . CopyTo ( DataViews , Eval . BindingsBatch , DataViews [ Eval . DataViewIndex . Get ( ) ] ) ;
2022-05-31 04:51:18 -04:00
}
2023-01-10 15:44:28 -05:00
STATETREE_LOG ( Verbose , TEXT ( " Start: '%s' " ) , * Eval . Name . ToString ( ) ) ;
{
QUICK_SCOPE_CYCLE_COUNTER ( StateTree_Eval_TreeStart ) ;
Eval . TreeStart ( * this ) ;
2023-08-07 18:06:40 -04:00
STATETREE_TRACE_EVALUATOR_EVENT ( EvalIndex , DataViews [ Eval . DataViewIndex . Get ( ) ] , EStateTreeTraceEventType : : OnTreeStarted ) ;
2023-01-10 15:44:28 -05:00
}
}
// Start Global tasks
// Even if we call Enter/ExitState() on global tasks, they do not enter any specific state.
const FStateTreeTransitionResult Transition = { } ; // Empty transition
for ( int32 TaskIndex = StateTree . GlobalTasksBegin ; TaskIndex < ( StateTree . GlobalTasksBegin + StateTree . GlobalTasksNum ) ; TaskIndex + + )
{
2023-02-13 20:06:02 -05:00
const FStateTreeTaskBase & Task = StateTree . Nodes [ TaskIndex ] . Get < const FStateTreeTaskBase > ( ) ;
2023-01-10 15:44:28 -05:00
SetNodeDataView ( Task , InstanceStructIndex , InstanceObjectIndex ) ;
// Copy bound properties.
if ( Task . BindingsBatch . IsValid ( ) )
{
StateTree . PropertyBindings . CopyTo ( DataViews , Task . BindingsBatch , DataViews [ Task . DataViewIndex . Get ( ) ] ) ;
}
2023-06-05 13:12:19 -04:00
// Ignore disabled task
if ( Task . bTaskEnabled = = false )
{
STATETREE_LOG ( VeryVerbose , TEXT ( " %*sSkipped 'EnterState' for disabled Task: '%s' " ) , UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * Task . Name . ToString ( ) ) ;
continue ;
}
2023-01-10 15:44:28 -05:00
STATETREE_LOG ( Verbose , TEXT ( " Start: '%s' " ) , * Task . Name . ToString ( ) ) ;
{
QUICK_SCOPE_CYCLE_COUNTER ( StateTree_Task_TreeStart ) ;
2023-06-09 15:27:31 -04:00
const EStateTreeRunStatus TaskStatus = Task . EnterState ( * this , Transition ) ;
2023-08-04 14:55:23 -04:00
STATETREE_TRACE_TASK_EVENT ( TaskIndex , DataViews [ Task . DataViewIndex . Get ( ) ] , EStateTreeTraceEventType : : OnEntered , TaskStatus ) ;
2023-06-09 15:27:31 -04:00
2023-06-05 06:33:07 -04:00
if ( TaskStatus ! = EStateTreeRunStatus : : Running )
2023-01-10 15:44:28 -05:00
{
OutLastInitializedTaskIndex = FStateTreeIndex16 ( TaskIndex ) ;
2023-06-05 06:33:07 -04:00
Result = TaskStatus ;
2023-01-10 15:44:28 -05:00
break ;
}
}
}
2023-06-05 06:33:07 -04:00
return Result ;
2023-01-10 15:44:28 -05:00
}
2023-06-05 06:33:07 -04:00
void FStateTreeExecutionContext : : StopEvaluatorsAndGlobalTasks ( const EStateTreeRunStatus CompletionStatus , const FStateTreeIndex16 LastInitializedTaskIndex )
2023-01-10 15:44:28 -05:00
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_StopEvaluators ) ;
2023-05-29 10:13:21 -04:00
STATETREE_TRACE_SCOPED_PHASE ( EStateTreeUpdatePhase : : StopGlobalTasks ) ;
2023-01-10 15:44:28 -05:00
STATETREE_CLOG ( StateTree . EvaluatorsNum > 0 , Verbose , TEXT ( " Stop Evaluators & Global Tasks " ) ) ;
// Stop evaluators
int32 InstanceStructIndex = 1 ; // Exec is at index 0
int32 InstanceObjectIndex = 0 ;
for ( int32 EvalIndex = StateTree . EvaluatorsBegin ; EvalIndex < ( StateTree . EvaluatorsBegin + StateTree . EvaluatorsNum ) ; EvalIndex + + )
{
2023-02-13 20:06:02 -05:00
const FStateTreeEvaluatorBase & Eval = StateTree . Nodes [ EvalIndex ] . Get < const FStateTreeEvaluatorBase > ( ) ;
2023-01-10 15:44:28 -05:00
SetNodeDataView ( Eval , InstanceStructIndex , InstanceObjectIndex ) ;
// Copy bound properties.
if ( Eval . BindingsBatch . IsValid ( ) )
{
StateTree . PropertyBindings . CopyTo ( DataViews , Eval . BindingsBatch , DataViews [ Eval . DataViewIndex . Get ( ) ] ) ;
}
}
// Stop Global tasks
for ( int32 TaskIndex = StateTree . GlobalTasksBegin ; TaskIndex < ( StateTree . GlobalTasksBegin + StateTree . GlobalTasksNum ) ; TaskIndex + + )
{
2023-02-13 20:06:02 -05:00
const FStateTreeTaskBase & Task = StateTree . Nodes [ TaskIndex ] . Get < const FStateTreeTaskBase > ( ) ;
2023-01-10 15:44:28 -05:00
SetNodeDataView ( Task , InstanceStructIndex , InstanceObjectIndex ) ;
// Copy bound properties.
if ( Task . BindingsBatch . IsValid ( ) & & Task . bShouldCopyBoundPropertiesOnExitState )
{
StateTree . PropertyBindings . CopyTo ( DataViews , Task . BindingsBatch , DataViews [ Task . DataViewIndex . Get ( ) ] ) ;
}
}
2023-06-05 06:33:07 -04:00
// Call in reverse order.
FStateTreeTransitionResult Transition ;
Transition . TargetState = FStateTreeStateHandle : : FromCompletionStatus ( CompletionStatus ) ;
Transition . CurrentActiveStates = { } ;
Transition . CurrentRunStatus = CompletionStatus ;
Transition . NextActiveStates = FStateTreeActiveStates ( Transition . TargetState ) ;
2023-01-10 15:44:28 -05:00
for ( int32 TaskIndex = ( StateTree . GlobalTasksBegin + StateTree . GlobalTasksNum ) - 1 ; TaskIndex > = StateTree . GlobalTasksBegin ; TaskIndex - - )
{
2023-02-13 20:06:02 -05:00
const FStateTreeTaskBase & Task = StateTree . Nodes [ TaskIndex ] . Get < const FStateTreeTaskBase > ( ) ;
2023-01-10 15:44:28 -05:00
2023-06-05 13:12:19 -04:00
// Ignore disabled task
if ( Task . bTaskEnabled = = false )
{
STATETREE_LOG ( VeryVerbose , TEXT ( " %*sSkipped 'ExitState' for disabled Task: '%s' " ) , UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * Task . Name . ToString ( ) ) ;
continue ;
}
2023-01-10 15:44:28 -05:00
// Relying here that invalid value of LastInitializedTaskIndex == MAX_uint16.
if ( TaskIndex < = LastInitializedTaskIndex . Get ( ) )
{
STATETREE_LOG ( Verbose , TEXT ( " Stop: '%s' " ) , * Task . Name . ToString ( ) ) ;
{
QUICK_SCOPE_CYCLE_COUNTER ( StateTree_Task_TreeStop ) ;
Task . ExitState ( * this , Transition ) ;
}
2023-08-04 14:55:23 -04:00
STATETREE_TRACE_TASK_EVENT ( TaskIndex , DataViews [ Task . DataViewIndex . Get ( ) ] , EStateTreeTraceEventType : : OnExited , Transition . CurrentRunStatus ) ;
2023-01-10 15:44:28 -05:00
}
}
for ( int32 EvalIndex = ( StateTree . EvaluatorsBegin + StateTree . EvaluatorsNum ) - 1 ; EvalIndex > = StateTree . EvaluatorsBegin ; EvalIndex - - )
{
2023-02-13 20:06:02 -05:00
const FStateTreeEvaluatorBase & Eval = StateTree . Nodes [ EvalIndex ] . Get < const FStateTreeEvaluatorBase > ( ) ;
2022-05-31 04:51:18 -04:00
STATETREE_LOG ( Verbose , TEXT ( " Stop: '%s' " ) , * Eval . Name . ToString ( ) ) ;
{
QUICK_SCOPE_CYCLE_COUNTER ( StateTree_Eval_TreeStop ) ;
Eval . TreeStop ( * this ) ;
2023-08-07 18:06:40 -04:00
STATETREE_TRACE_EVALUATOR_EVENT ( EvalIndex , DataViews [ Eval . DataViewIndex . Get ( ) ] , EStateTreeTraceEventType : : OnTreeStopped ) ;
2022-05-31 04:51:18 -04:00
}
}
}
2022-09-23 20:02:42 -04:00
EStateTreeRunStatus FStateTreeExecutionContext : : TickTasks ( const float DeltaTime )
2022-05-31 04:51:18 -04:00
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_TickTasks ) ;
2023-05-29 10:13:21 -04:00
STATETREE_TRACE_SCOPED_PHASE ( EStateTreeUpdatePhase : : TickingTasks ) ;
2022-05-31 04:51:18 -04:00
2022-11-22 08:13:54 -05:00
FStateTreeExecutionState & Exec = GetExecState ( ) ;
2022-05-31 04:51:18 -04:00
if ( Exec . ActiveStates . IsEmpty ( ) )
{
return EStateTreeRunStatus : : Failed ;
}
EStateTreeRunStatus Result = EStateTreeRunStatus : : Running ;
int32 NumTotalTasks = 0 ;
2022-11-01 15:11:19 -04:00
const bool bHasEvents = ! EventsToProcess . IsEmpty ( ) ;
2022-05-31 04:51:18 -04:00
check ( Exec . FirstTaskStructIndex . IsValid ( ) & & Exec . FirstTaskObjectIndex . IsValid ( ) ) ;
int32 InstanceStructIndex = Exec . FirstTaskStructIndex . Get ( ) ;
int32 InstanceObjectIndex = Exec . FirstTaskObjectIndex . Get ( ) ;
2022-11-22 08:13:54 -05:00
Exec . CompletedStateHandle = FStateTreeStateHandle : : Invalid ;
2023-01-10 15:44:28 -05:00
// Used to stop ticking tasks after one fails, but we still want to keep updating the data views so that property binding works properly.
2022-09-27 10:55:54 -04:00
bool bShouldTickTasks = true ;
2022-11-03 14:21:53 -04:00
STATETREE_CLOG ( Exec . ActiveStates . Num ( ) > 0 , VeryVerbose , TEXT ( " Ticking Tasks " ) ) ;
2022-09-27 10:55:54 -04:00
for ( int32 Index = 0 ; Index < Exec . ActiveStates . Num ( ) ; Index + + )
2022-05-31 04:51:18 -04:00
{
const FStateTreeStateHandle CurrentHandle = Exec . ActiveStates [ Index ] ;
2022-09-23 20:02:42 -04:00
const FCompactStateTreeState & State = StateTree . States [ CurrentHandle . Index ] ;
2022-05-31 04:51:18 -04:00
2023-01-23 12:48:04 -05:00
FCurrentlyProcessedStateScope StateScope ( * this , CurrentHandle ) ;
2023-08-04 14:55:23 -04:00
STATETREE_TRACE_SCOPED_STATE ( CurrentHandle ) ;
2022-11-03 14:21:53 -04:00
STATETREE_CLOG ( State . TasksNum > 0 , VeryVerbose , TEXT ( " %*sState '%s' " ) , Index * UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * DebugGetStatePath ( Exec . ActiveStates , Index ) ) ;
2022-05-31 04:51:18 -04:00
if ( State . Type = = EStateTreeStateType : : Linked )
{
2022-09-23 20:02:42 -04:00
UpdateLinkedStateParameters ( State , InstanceStructIndex ) ;
2022-05-31 04:51:18 -04:00
InstanceStructIndex + + ;
}
else if ( State . Type = = EStateTreeStateType : : Subtree )
{
2022-09-23 20:02:42 -04:00
UpdateSubtreeStateParameters ( State ) ;
2022-05-31 04:51:18 -04:00
}
2022-09-27 10:55:54 -04:00
// Update Tasks data and tick if possible (ie. if no task has yet failed and so bShouldTickTasks is true)
2022-05-31 04:51:18 -04:00
for ( int32 TaskIndex = State . TasksBegin ; TaskIndex < ( State . TasksBegin + State . TasksNum ) ; TaskIndex + + )
{
2023-02-13 20:06:02 -05:00
const FStateTreeTaskBase & Task = StateTree . Nodes [ TaskIndex ] . Get < const FStateTreeTaskBase > ( ) ;
2023-06-09 05:20:18 -04:00
SetNodeDataView ( Task , InstanceStructIndex , InstanceObjectIndex ) ;
2023-06-05 13:12:19 -04:00
// Ignore disabled task
if ( Task . bTaskEnabled = = false )
{
STATETREE_LOG ( VeryVerbose , TEXT ( " %*sSkipped 'Tick' for disabled Task: '%s' " ) , UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * Task . Name . ToString ( ) ) ;
continue ;
}
2022-11-03 14:21:53 -04:00
const bool bNeedsTick = bShouldTickTasks & & ( Task . bShouldCallTick | | ( bHasEvents & & Task . bShouldCallTickOnlyOnEvents ) ) ;
2023-01-10 15:44:28 -05:00
STATETREE_LOG ( VeryVerbose , TEXT ( " %*s Tick: '%s' %s " ) , Index * UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * Task . Name . ToString ( ) , ! bNeedsTick ? TEXT ( " [not ticked] " ) : TEXT ( " " ) ) ;
if ( ! bNeedsTick )
{
continue ;
}
2023-05-09 12:57:56 -04:00
const FStateTreeDataView TaskDataView = DataViews [ Task . DataViewIndex . Get ( ) ] ;
2022-11-03 14:21:53 -04:00
2022-05-31 04:51:18 -04:00
// Copy bound properties.
2022-11-03 14:21:53 -04:00
// Only copy properties when the task is actually ticked, and copy properties at tick is requested.
2023-01-10 15:44:28 -05:00
if ( Task . BindingsBatch . IsValid ( ) & & Task . bShouldCopyBoundPropertiesOnTick )
2022-05-31 04:51:18 -04:00
{
2023-05-09 12:57:56 -04:00
StateTree . PropertyBindings . CopyTo ( DataViews , Task . BindingsBatch , TaskDataView ) ;
2022-05-31 04:51:18 -04:00
}
2023-04-19 13:26:23 -04:00
2023-08-04 14:55:23 -04:00
//STATETREE_TRACE_TASK_EVENT(TaskIndex, TaskDataView, EStateTreeTraceEventType::OnTickingTask, EStateTreeRunStatus::Running);
2023-04-19 13:26:23 -04:00
EStateTreeRunStatus TaskResult = EStateTreeRunStatus : : Unset ;
2022-05-31 04:51:18 -04:00
{
QUICK_SCOPE_CYCLE_COUNTER ( StateTree_Task_Tick ) ;
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_Task_Tick ) ;
2023-04-19 13:26:23 -04:00
TaskResult = Task . Tick ( * this , DeltaTime ) ;
}
2022-05-31 04:51:18 -04:00
2023-05-09 12:57:56 -04:00
STATETREE_TRACE_TASK_EVENT ( TaskIndex , TaskDataView ,
2023-08-07 18:06:40 -04:00
TaskResult ! = EStateTreeRunStatus : : Running ? EStateTreeTraceEventType : : OnTaskCompleted : EStateTreeTraceEventType : : OnTicked ,
2023-05-09 12:57:56 -04:00
TaskResult ) ;
2023-04-19 13:26:23 -04:00
// TODO: Add more control over which states can control the failed/succeeded result.
if ( TaskResult ! = EStateTreeRunStatus : : Running )
{
// Store the first state that completed, will be used to decide where to trigger transitions.
if ( ! Exec . CompletedStateHandle . IsValid ( ) )
2022-05-31 04:51:18 -04:00
{
2023-04-19 13:26:23 -04:00
Exec . CompletedStateHandle = CurrentHandle ;
2022-05-31 04:51:18 -04:00
}
2023-04-19 13:26:23 -04:00
Result = TaskResult ;
}
if ( TaskResult = = EStateTreeRunStatus : : Failed )
{
bShouldTickTasks = false ;
2022-05-31 04:51:18 -04:00
}
}
NumTotalTasks + = State . TasksNum ;
}
if ( NumTotalTasks = = 0 )
{
// No tasks, done ticking.
Result = EStateTreeRunStatus : : Succeeded ;
}
return Result ;
}
2023-01-23 12:48:04 -05:00
bool FStateTreeExecutionContext : : TestAllConditions ( const int32 ConditionsOffset , const int32 ConditionsNum )
2022-05-31 04:51:18 -04:00
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_TestConditions ) ;
2023-01-23 12:48:04 -05:00
if ( ConditionsNum = = 0 | | ! SharedInstanceData . IsValid ( ) )
2022-05-31 04:51:18 -04:00
{
return true ;
}
TStaticArray < EStateTreeConditionOperand , UE : : StateTree : : MaxConditionIndent + 1 > Operands ( InPlace , EStateTreeConditionOperand : : Copy ) ;
TStaticArray < bool , UE : : StateTree : : MaxConditionIndent + 1 > Values ( InPlace , false ) ;
int32 Level = 0 ;
for ( int32 Index = 0 ; Index < ConditionsNum ; Index + + )
{
2023-10-05 08:21:08 -04:00
const int32 ConditionIndex = ConditionsOffset + Index ;
const FStateTreeConditionBase & Cond = StateTree . Nodes [ ConditionIndex ] . Get < const FStateTreeConditionBase > ( ) ;
2023-04-19 13:26:23 -04:00
FStateTreeDataView & DataView = DataViews [ Cond . DataViewIndex . Get ( ) ] ;
2022-05-31 04:51:18 -04:00
if ( Cond . bInstanceIsObject )
{
2023-04-19 13:26:23 -04:00
DataView = SharedInstanceData - > GetMutableObject ( Cond . InstanceIndex . Get ( ) ) ;
2022-05-31 04:51:18 -04:00
}
else
{
2023-04-19 13:26:23 -04:00
DataView = SharedInstanceData - > GetMutableStruct ( Cond . InstanceIndex . Get ( ) ) ;
2022-05-31 04:51:18 -04:00
}
2023-06-05 13:12:19 -04:00
bool bValue = false ;
if ( Cond . EvaluationMode = = EStateTreeConditionEvaluationMode : : Evaluated )
2022-05-31 04:51:18 -04:00
{
2023-06-05 13:12:19 -04:00
// Copy bound properties.
if ( Cond . BindingsBatch . IsValid ( ) )
2022-05-31 04:51:18 -04:00
{
2023-06-05 13:12:19 -04:00
if ( ! StateTree . PropertyBindings . CopyTo ( DataViews , Cond . BindingsBatch , DataView ) )
{
// If the source data cannot be accessed, the whole expression evaluates to false.
Values [ 0 ] = false ;
break ;
}
}
bValue = Cond . TestCondition ( * this ) ;
2023-10-05 08:21:08 -04:00
STATETREE_TRACE_CONDITION_EVENT ( ConditionIndex , DataView , bValue ? EStateTreeTraceEventType : : Passed : EStateTreeTraceEventType : : Failed ) ;
2023-06-05 13:12:19 -04:00
// Reset copied properties that might contain object references.
if ( Cond . BindingsBatch . IsValid ( ) )
{
StateTree . PropertyBindings . ResetObjects ( Cond . BindingsBatch , DataView ) ;
2022-05-31 04:51:18 -04:00
}
}
2023-06-05 13:12:19 -04:00
else
2022-11-30 07:17:26 -05:00
{
2023-06-05 13:12:19 -04:00
bValue = Cond . EvaluationMode = = EStateTreeConditionEvaluationMode : : ForcedTrue ? true : /* EStateTreeConditionEvaluationMode::AlwaysFalse */ false ;
2023-10-05 08:21:08 -04:00
STATETREE_TRACE_CONDITION_EVENT ( ConditionIndex , FStateTreeDataView { } , bValue ? EStateTreeTraceEventType : : Passed : EStateTreeTraceEventType : : Failed ) ;
2022-11-30 07:17:26 -05:00
}
2022-05-31 04:51:18 -04:00
const int32 DeltaIndent = Cond . DeltaIndent ;
const int32 OpenParens = FMath : : Max ( 0 , DeltaIndent ) + 1 ; // +1 for the current value that is stored at the empty slot at the top of the value stack.
const int32 ClosedParens = FMath : : Max ( 0 , - DeltaIndent ) + 1 ;
// Store the operand to apply when merging higher level down when returning to this level.
// @todo: remove this conditions in 5.1, needs resaving existing StateTrees.
const EStateTreeConditionOperand Operand = Index = = 0 ? EStateTreeConditionOperand : : Copy : Cond . Operand ;
Operands [ Level ] = Operand ;
// Store current value at the top of the stack.
Level + = OpenParens ;
Values [ Level ] = bValue ;
// Evaluate and merge down values based on closed braces.
// The current value is placed in parens (see +1 above), which makes merging down and applying the new value consistent.
// The default operand is copy, so if the value is needed immediately, it is just copied down, or if we're on the same level,
// the operand storing above gives handles with the right logic.
for ( int32 Paren = 0 ; Paren < ClosedParens ; Paren + + )
{
Level - - ;
switch ( Operands [ Level ] )
{
case EStateTreeConditionOperand : : Copy :
Values [ Level ] = Values [ Level + 1 ] ;
break ;
case EStateTreeConditionOperand : : And :
Values [ Level ] & = Values [ Level + 1 ] ;
break ;
case EStateTreeConditionOperand : : Or :
Values [ Level ] | = Values [ Level + 1 ] ;
break ;
}
Operands [ Level ] = EStateTreeConditionOperand : : Copy ;
}
}
return Values [ 0 ] ;
}
2022-11-01 15:11:19 -04:00
FString FStateTreeExecutionContext : : DebugGetEventsAsString ( ) const
{
FString Result ;
for ( const FStateTreeEvent & Event : EventsToProcess )
{
if ( ! Result . IsEmpty ( ) )
{
Result + = TEXT ( " , " ) ;
}
Result + = Event . Tag . ToString ( ) ;
}
return Result ;
}
2023-10-17 16:15:53 -04:00
bool FStateTreeExecutionContext : : RequestTransition ( const FStateTreeStateHandle NextState , const EStateTreeTransitionPriority Priority , const EStateTreeSelectionFallback Fallback )
2022-12-02 07:57:31 -05:00
{
2023-01-23 12:48:04 -05:00
// Skip lower priority transitions.
if ( NextTransition . Priority > = Priority )
2022-12-02 07:57:31 -05:00
{
2023-01-23 12:48:04 -05:00
return false ;
2022-12-02 07:57:31 -05:00
}
2023-01-23 12:48:04 -05:00
const FStateTreeExecutionState & Exec = GetExecState ( ) ;
if ( NextState . IsCompletionState ( ) )
{
2023-09-22 09:53:34 -04:00
SetupNextTransition ( NextState , Priority ) ;
2023-01-23 12:48:04 -05:00
STATETREE_LOG ( Verbose , TEXT ( " Transition on state '%s' -[%s]-> state '%s' " ) ,
* GetSafeStateName ( NextTransition . CurrentActiveStates . Last ( ) ) , * GetSafeStateName ( NextState ) , * GetSafeStateName ( NextTransition . NextActiveStates . Last ( ) ) ) ;
return true ;
}
if ( ! NextState . IsValid ( ) )
{
// NotSet is no-operation, but can be used to mask a transition at parent state. Returning unset keeps updating current state.
2023-09-22 09:53:34 -04:00
SetupNextTransition ( FStateTreeStateHandle : : Invalid , Priority ) ;
2022-12-02 07:57:31 -05:00
return true ;
}
2023-01-23 12:48:04 -05:00
FStateTreeActiveStates NewActiveState ;
2023-04-12 07:59:16 -04:00
FStateTreeActiveStates VisitedStates ;
2023-10-17 16:15:53 -04:00
if ( SelectState ( NextState , NewActiveState , VisitedStates , Fallback ) )
2023-01-23 12:48:04 -05:00
{
2023-09-22 09:53:34 -04:00
SetupNextTransition ( NextState , Priority ) ;
2023-01-23 12:48:04 -05:00
NextTransition . NextActiveStates = NewActiveState ;
STATETREE_LOG ( Verbose , TEXT ( " Transition on state '%s' -[%s]-> state '%s' " ) ,
* GetSafeStateName ( NextTransition . CurrentActiveStates . Last ( ) ) , * GetSafeStateName ( NextState ) , * GetSafeStateName ( NextTransition . NextActiveStates . Last ( ) ) ) ;
return true ;
}
2022-12-02 07:57:31 -05:00
return false ;
}
2023-09-22 09:53:34 -04:00
void FStateTreeExecutionContext : : SetupNextTransition ( const FStateTreeStateHandle NextState , const EStateTreeTransitionPriority Priority )
{
const FStateTreeExecutionState & Exec = GetExecState ( ) ;
NextTransition . CurrentActiveStates = Exec . ActiveStates ;
NextTransition . CurrentRunStatus = Exec . LastTickStatus ;
NextTransition . SourceState = CurrentlyProcessedState ;
NextTransition . TargetState = NextState ;
if ( NextState = = FStateTreeStateHandle : : Invalid )
{
NextTransition . NextActiveStates . Reset ( ) ;
}
else
{
NextTransition . NextActiveStates = FStateTreeActiveStates ( NextState ) ;
}
NextTransition . Priority = Priority ;
}
2023-01-23 12:48:04 -05:00
bool FStateTreeExecutionContext : : TriggerTransitions ( )
2022-05-31 04:51:18 -04:00
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_TriggerTransition ) ;
2023-05-29 10:13:21 -04:00
STATETREE_TRACE_SCOPED_PHASE ( EStateTreeUpdatePhase : : TriggerTransitions ) ;
2022-05-31 04:51:18 -04:00
2023-01-23 12:48:04 -05:00
FAllowDirectTransitionsScope AllowDirectTransitionsScope ( * this ) ; // Set flag for the scope of this function to allow direct transitions without buffering.
2022-09-23 20:02:42 -04:00
FStateTreeExecutionState & Exec = GetExecState ( ) ;
2022-11-22 08:13:54 -05:00
2023-05-09 12:57:56 -04:00
if ( EventsToProcess . Num ( ) > 0 )
{
2023-09-22 09:53:34 -04:00
STATETREE_LOG_AND_TRACE ( Verbose , TEXT ( " Trigger transitions with events [%s] " ) , * DebugGetEventsAsString ( ) ) ;
2023-05-09 12:57:56 -04:00
}
2022-12-02 07:57:31 -05:00
2023-01-23 12:48:04 -05:00
NextTransition . Reset ( ) ;
2022-11-23 09:22:14 -05:00
2022-12-02 07:57:31 -05:00
//
2023-01-23 12:48:04 -05:00
// Process transition requests
//
for ( const FStateTreeTransitionRequest & Request : InstanceData . GetTransitionRequests ( ) )
{
2023-08-04 14:55:23 -04:00
if ( RequestTransition ( Request . TargetState , Request . Priority ) )
{
NextTransitionSource = FStateTreeTransitionSource ( EStateTreeTransitionSourceType : : ExternalRequest , Request . TargetState , Request . Priority ) ;
}
2023-01-23 12:48:04 -05:00
}
InstanceData . ResetTransitionRequests ( ) ;
//
// Check tick, event, and task based transitions first.
2022-12-02 07:57:31 -05:00
//
2023-06-13 04:36:07 -04:00
if ( Exec . ActiveStates . Num ( ) > 0 )
2022-05-31 04:51:18 -04:00
{
2023-06-13 04:36:07 -04:00
// Setup data views for the tasks that will get called.
// It is possible that not all tasks views are set up at this stage (e.g. failed tick, pending transition handling).
check ( Exec . FirstTaskStructIndex . IsValid ( ) & & Exec . FirstTaskObjectIndex . IsValid ( ) ) ;
int32 InstanceStructIndex = Exec . FirstTaskStructIndex . Get ( ) ;
int32 InstanceObjectIndex = Exec . FirstTaskObjectIndex . Get ( ) ;
2023-01-23 12:48:04 -05:00
2023-06-13 04:36:07 -04:00
for ( int32 Index = 0 ; Index < Exec . ActiveStates . Num ( ) ; Index + + )
2023-01-23 12:48:04 -05:00
{
2023-06-13 04:36:07 -04:00
const FStateTreeStateHandle CurrentHandle = Exec . ActiveStates [ Index ] ;
const FCompactStateTreeState & State = StateTree . States [ CurrentHandle . Index ] ;
2023-01-23 12:48:04 -05:00
2023-06-13 04:36:07 -04:00
if ( State . bHasTransitionTasks )
2023-01-23 12:48:04 -05:00
{
2023-06-13 04:36:07 -04:00
// Update index to skip over the linked state params
if ( State . Type = = EStateTreeStateType : : Linked )
2023-06-05 13:12:19 -04:00
{
2023-06-13 04:36:07 -04:00
InstanceStructIndex + + ;
2023-06-05 13:12:19 -04:00
}
2023-06-13 04:36:07 -04:00
for ( int32 TaskIndex = State . TasksBegin ; TaskIndex < ( State . TasksBegin + State . TasksNum ) ; TaskIndex + + )
2023-01-23 12:48:04 -05:00
{
2023-06-13 04:36:07 -04:00
const FStateTreeTaskBase & Task = StateTree . Nodes [ TaskIndex ] . Get < const FStateTreeTaskBase > ( ) ;
if ( Task . bShouldAffectTransitions )
2022-05-31 04:51:18 -04:00
{
2023-06-13 04:36:07 -04:00
SetNodeDataView ( Task , InstanceStructIndex , InstanceObjectIndex ) ;
2023-03-14 10:40:47 -04:00
}
else
{
2023-06-13 04:36:07 -04:00
if ( Task . bInstanceIsObject )
{
InstanceObjectIndex + + ;
}
else
{
InstanceStructIndex + + ;
}
}
}
}
else
{
// Skip over all instances in the state.
InstanceStructIndex + = ( int32 ) State . TaskInstanceStructNum ;
InstanceObjectIndex + = ( int32 ) State . TaskInstanceObjectNum ;
}
}
for ( int32 StateIndex = Exec . ActiveStates . Num ( ) - 1 ; StateIndex > = 0 ; StateIndex - - )
{
const FStateTreeStateHandle StateHandle = Exec . ActiveStates [ StateIndex ] ;
const FCompactStateTreeState & State = StateTree . States [ StateHandle . Index ] ;
2023-08-15 14:21:54 -04:00
// Do not process any transitions from a disabled state
if ( ! State . bEnabled )
{
continue ;
}
2023-06-13 04:36:07 -04:00
FCurrentlyProcessedStateScope StateScope ( * this , StateHandle ) ;
2023-08-04 14:55:23 -04:00
STATETREE_TRACE_SCOPED_STATE ( StateHandle ) ;
2023-06-13 04:36:07 -04:00
if ( State . bHasTransitionTasks )
{
STATETREE_CLOG ( State . TasksNum > 0 , VeryVerbose , TEXT ( " %*sTrigger task transitions in state '%s' " ) , StateIndex * UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * DebugGetStatePath ( Exec . ActiveStates , StateIndex ) ) ;
for ( int32 TaskIndex = ( State . TasksBegin + State . TasksNum ) - 1 ; TaskIndex > = State . TasksBegin ; TaskIndex - - )
{
const FStateTreeTaskBase & Task = StateTree . Nodes [ TaskIndex ] . Get < const FStateTreeTaskBase > ( ) ;
// Ignore disabled task
if ( Task . bTaskEnabled = = false )
{
STATETREE_LOG ( VeryVerbose , TEXT ( " %*sSkipped 'TriggerTransitions' for disabled Task: '%s' " ) , UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * Task . Name . ToString ( ) ) ;
continue ;
}
if ( Task . bShouldAffectTransitions )
{
STATETREE_LOG ( VeryVerbose , TEXT ( " %*sTriggerTransitions: '%s' " ) , UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * Task . Name . ToString ( ) ) ;
2023-09-05 13:01:06 -04:00
STATETREE_TRACE_TASK_EVENT ( TaskIndex , DataViews [ Task . DataViewIndex . Get ( ) ] , EStateTreeTraceEventType : : OnEvaluating , EStateTreeRunStatus : : Running ) ;
2023-06-13 04:36:07 -04:00
check ( DataViews [ Task . DataViewIndex . Get ( ) ] . IsValid ( ) ) ;
Task . TriggerTransitions ( * this ) ;
}
}
}
for ( uint8 i = 0 ; i < State . TransitionsNum ; i + + )
{
// All transition conditions must pass
const int16 TransitionIndex = State . TransitionsBegin + i ;
const FCompactStateTransition & Transition = StateTree . Transitions [ TransitionIndex ] ;
2023-06-20 13:49:25 -04:00
// Skip disabled transitions
if ( Transition . bTransitionEnabled = = false )
{
continue ;
}
2023-06-13 04:36:07 -04:00
// No need to test the transition if same or higher priority transition has already been processed.
if ( Transition . Priority < = NextTransition . Priority )
{
continue ;
}
// Skip completion transitions
if ( EnumHasAnyFlags ( Transition . Trigger , EStateTreeTransitionTrigger : : OnStateCompleted ) )
{
continue ;
}
// If a delayed transition has passed the delay, and remove it from the queue, and try trigger it.
FStateTreeTransitionDelayedState * DelayedState = nullptr ;
if ( Transition . HasDelay ( ) )
{
DelayedState = Exec . FindDelayedTransition ( FStateTreeIndex16 ( TransitionIndex ) ) ;
if ( DelayedState ! = nullptr & & DelayedState - > TimeLeft < = 0.0f )
{
STATETREE_LOG ( Verbose , TEXT ( " Passed delayed transition from '%s' (%s) -> '%s' " ) ,
* GetSafeStateName ( Exec . ActiveStates . Last ( ) ) , * State . Name . ToString ( ) , * GetSafeStateName ( Transition . State ) ) ;
Exec . DelayedTransitions . RemoveAllSwap ( [ TransitionIndex ] ( const FStateTreeTransitionDelayedState & DelayedState )
{
return DelayedState . TransitionIndex . Get ( ) = = TransitionIndex ;
} ) ;
// Trigger Delayed Transition when the delay has passed.
2023-10-17 16:15:53 -04:00
if ( RequestTransition ( Transition . State , Transition . Priority , Transition . Fallback ) )
2023-06-20 13:49:25 -04:00
{
2023-08-04 14:55:23 -04:00
NextTransitionSource = FStateTreeTransitionSource ( FStateTreeIndex16 ( TransitionIndex ) , Transition . State , Transition . Priority ) ;
2023-06-20 13:49:25 -04:00
}
2023-03-14 10:40:47 -04:00
continue ;
2022-05-31 04:51:18 -04:00
}
}
2023-03-14 10:40:47 -04:00
2023-06-13 04:36:07 -04:00
const bool bShouldTrigger = Transition . Trigger = = EStateTreeTransitionTrigger : : OnTick
| | ( Transition . Trigger = = EStateTreeTransitionTrigger : : OnEvent
& & HasEventToProcess ( Transition . EventTag ) ) ;
bool bPassed = false ;
if ( bShouldTrigger )
{
2023-08-04 14:55:23 -04:00
STATETREE_TRACE_TRANSITION_EVENT ( FStateTreeTransitionSource ( FStateTreeIndex16 ( TransitionIndex ) , Transition . State , Transition . Priority ) , EStateTreeTraceEventType : : OnEvaluating ) ;
2023-06-13 04:36:07 -04:00
STATETREE_TRACE_SCOPED_PHASE ( EStateTreeUpdatePhase : : TransitionConditions ) ;
bPassed = TestAllConditions ( Transition . ConditionsBegin , Transition . ConditionsNum ) ;
}
if ( bPassed )
{
// If the transitions is delayed, set up the delay.
if ( Transition . HasDelay ( ) )
{
if ( DelayedState = = nullptr )
{
// Initialize new delayed transition.
const float DelayDuration = Transition . Delay . GetRandomDuration ( ) ;
if ( DelayDuration > 0.0f )
{
DelayedState = & Exec . DelayedTransitions . AddDefaulted_GetRef ( ) ;
DelayedState - > TransitionIndex = FStateTreeIndex16 ( TransitionIndex ) ;
DelayedState - > TimeLeft = DelayDuration ;
BeginDelayedTransition ( * DelayedState ) ;
STATETREE_LOG ( Verbose , TEXT ( " Delayed transition triggered from '%s' (%s) -> '%s' %.1fs " ) ,
* GetSafeStateName ( Exec . ActiveStates . Last ( ) ) , * State . Name . ToString ( ) , * GetSafeStateName ( Transition . State ) , DelayedState - > TimeLeft ) ;
// Delay state added, skip requesting the transition.
continue ;
}
// Fallthrough to request transition if duration was zero.
}
else
{
// We get here if the transitions re-triggers during the delay, on which case we'll just ignore it.
continue ;
}
}
2023-10-17 16:15:53 -04:00
if ( RequestTransition ( Transition . State , Transition . Priority , Transition . Fallback ) )
2023-06-20 13:49:25 -04:00
{
2023-08-04 14:55:23 -04:00
NextTransitionSource = FStateTreeTransitionSource ( FStateTreeIndex16 ( TransitionIndex ) , Transition . State , Transition . Priority ) ;
2023-06-20 13:49:25 -04:00
}
2023-06-13 04:36:07 -04:00
}
2022-05-31 04:51:18 -04:00
}
}
}
2023-01-23 12:48:04 -05:00
if ( StateTree . bHasGlobalTransitionTasks )
{
STATETREE_LOG ( VeryVerbose , TEXT ( " Trigger global task transitions " ) ) ;
for ( int32 TaskIndex = ( StateTree . GlobalTasksBegin + StateTree . GlobalTasksNum ) - 1 ; TaskIndex > = StateTree . GlobalTasksBegin ; TaskIndex - - )
{
2023-02-13 20:06:02 -05:00
const FStateTreeTaskBase & Task = StateTree . Nodes [ TaskIndex ] . Get < const FStateTreeTaskBase > ( ) ;
2023-06-05 13:12:19 -04:00
// Ignore disabled task
if ( Task . bTaskEnabled = = false )
{
STATETREE_LOG ( VeryVerbose , TEXT ( " %*sSkipped 'TriggerTransitions' for disabled Task: '%s' " ) , UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * Task . Name . ToString ( ) ) ;
continue ;
}
2023-01-23 12:48:04 -05:00
if ( Task . bShouldAffectTransitions )
{
STATETREE_LOG ( VeryVerbose , TEXT ( " %*sTriggerTransitions: '%s' " ) , UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * Task . Name . ToString ( ) ) ;
2023-09-05 13:01:06 -04:00
STATETREE_TRACE_TASK_EVENT ( TaskIndex , DataViews [ Task . DataViewIndex . Get ( ) ] , EStateTreeTraceEventType : : OnEvaluating , EStateTreeRunStatus : : Running ) ;
2023-01-23 12:48:04 -05:00
check ( DataViews [ Task . DataViewIndex . Get ( ) ] . IsValid ( ) ) ;
Task . TriggerTransitions ( * this ) ;
}
}
}
2022-12-02 07:57:31 -05:00
//
// Check state completion transitions.
//
2023-09-22 09:53:34 -04:00
bool bProcessSubTreeCompletion = true ;
2023-01-23 12:48:04 -05:00
if ( NextTransition . Priority = = EStateTreeTransitionPriority : : None
& & Exec . LastTickStatus ! = EStateTreeRunStatus : : Running )
2022-05-31 04:51:18 -04:00
{
2022-12-02 07:57:31 -05:00
// Start from the last completed state.
const int32 StateStartIndex = Exec . CompletedStateHandle . IsValid ( ) ? Exec . ActiveStates . IndexOfReverse ( Exec . CompletedStateHandle ) : ( Exec . ActiveStates . Num ( ) - 1 ) ;
const EStateTreeTransitionTrigger CompletionTrigger = Exec . LastTickStatus = = EStateTreeRunStatus : : Succeeded ? EStateTreeTransitionTrigger : : OnStateSucceeded : EStateTreeTransitionTrigger : : OnStateFailed ;
2022-05-31 04:51:18 -04:00
2022-12-02 07:57:31 -05:00
check ( StateStartIndex > = 0 & & StateStartIndex < Exec . ActiveStates . Num ( ) ) ;
// Check completion transitions
for ( int32 StateIndex = StateStartIndex ; StateIndex > = 0 ; StateIndex - - )
{
2023-01-23 12:48:04 -05:00
const FStateTreeStateHandle StateHandle = Exec . ActiveStates [ StateIndex ] ;
const FCompactStateTreeState & State = StateTree . States [ StateHandle . Index ] ;
FCurrentlyProcessedStateScope StateScope ( * this , StateHandle ) ;
2023-08-04 14:55:23 -04:00
STATETREE_TRACE_SCOPED_STATE_PHASE ( StateHandle , EStateTreeUpdatePhase : : TriggerTransitions ) ;
2022-12-02 07:57:31 -05:00
for ( uint8 i = 0 ; i < State . TransitionsNum ; i + + )
{
// All transition conditions must pass
const int16 TransitionIndex = State . TransitionsBegin + i ;
const FCompactStateTransition & Transition = StateTree . Transitions [ TransitionIndex ] ;
2023-06-20 13:49:25 -04:00
// Skip disabled transitions
if ( Transition . bTransitionEnabled = = false )
{
continue ;
}
2022-12-02 07:57:31 -05:00
if ( EnumHasAnyFlags ( Transition . Trigger , CompletionTrigger ) )
{
2023-05-23 10:46:16 -04:00
bool bPassed = false ;
{
2023-08-04 14:55:23 -04:00
STATETREE_TRACE_TRANSITION_EVENT ( FStateTreeTransitionSource ( FStateTreeIndex16 ( TransitionIndex ) , Transition . State , Transition . Priority ) , EStateTreeTraceEventType : : OnEvaluating ) ;
2023-05-29 10:13:21 -04:00
STATETREE_TRACE_SCOPED_PHASE ( EStateTreeUpdatePhase : : TransitionConditions ) ;
2023-05-23 10:46:16 -04:00
bPassed = TestAllConditions ( Transition . ConditionsBegin , Transition . ConditionsNum ) ;
}
if ( bPassed )
2022-12-02 07:57:31 -05:00
{
2023-01-23 12:48:04 -05:00
// No delay allowed on completion conditions.
// No priority on completion transitions, use the priority to signal that state is selected.
2023-10-17 16:15:53 -04:00
if ( RequestTransition ( Transition . State , EStateTreeTransitionPriority : : Normal , Transition . Fallback ) )
2022-12-02 07:57:31 -05:00
{
2023-08-04 14:55:23 -04:00
NextTransitionSource = FStateTreeTransitionSource ( FStateTreeIndex16 ( TransitionIndex ) , Transition . State , Transition . Priority ) ;
2023-01-23 12:48:04 -05:00
break ;
2022-12-02 07:57:31 -05:00
}
}
}
}
2023-01-23 12:48:04 -05:00
if ( NextTransition . Priority ! = EStateTreeTransitionPriority : : None )
{
break ;
}
2022-12-02 07:57:31 -05:00
}
2023-01-23 12:48:04 -05:00
if ( NextTransition . Priority = = EStateTreeTransitionPriority : : None )
2022-12-02 07:57:31 -05:00
{
2023-09-22 09:53:34 -04:00
STATETREE_LOG_AND_TRACE ( Verbose , TEXT ( " Could not trigger completion transition, jump back to root state. " ) ) ;
2023-01-23 12:48:04 -05:00
FCurrentlyProcessedStateScope StateScope ( * this , FStateTreeStateHandle : : Root ) ;
2023-08-04 14:55:23 -04:00
if ( RequestTransition ( FStateTreeStateHandle : : Root , EStateTreeTransitionPriority : : Normal ) )
{
NextTransitionSource = FStateTreeTransitionSource ( EStateTreeTransitionSourceType : : Internal , FStateTreeStateHandle : : Root , EStateTreeTransitionPriority : : Normal ) ;
}
2023-09-22 09:53:34 -04:00
else
{
STATETREE_LOG_AND_TRACE ( Warning , TEXT ( " Failed to select root state. Stopping the tree with failure. " ) ) ;
SetupNextTransition ( FStateTreeStateHandle : : Failed , EStateTreeTransitionPriority : : Critical ) ;
// In this case we don't want to complete subtrees, we want to force the whole tree to stop.
bProcessSubTreeCompletion = false ;
}
2022-12-02 07:57:31 -05:00
}
}
2023-01-23 12:48:04 -05:00
// Check if the transition was succeed/failed, if we're on a sub-tree, complete the subtree instead of transition.
2023-09-22 09:53:34 -04:00
if ( NextTransition . TargetState . IsCompletionState ( ) & & bProcessSubTreeCompletion )
2023-01-23 12:48:04 -05:00
{
const FStateTreeStateHandle ParentLinkedState = GetParentLinkedStateHandle ( Exec . ActiveStates , NextTransition . SourceState ) ;
if ( ParentLinkedState . IsValid ( ) )
{
const EStateTreeRunStatus RunStatus = NextTransition . TargetState . ToCompletionStatus ( ) ;
2023-03-14 13:35:46 -04:00
STATETREE_LOG ( Verbose , TEXT ( " Completed subtree '%s' from state '%s' (%s): %s " ) ,
2023-01-23 12:48:04 -05:00
* GetSafeStateName ( ParentLinkedState ) , * GetSafeStateName ( Exec . ActiveStates . Last ( ) ) , * GetSafeStateName ( NextTransition . SourceState ) , * UEnum : : GetDisplayValueAsText ( RunStatus ) . ToString ( ) ) ;
2023-06-12 07:34:11 -04:00
// Set the parent linked state as last completed state, and update tick status to the status from the transition.
2023-01-23 12:48:04 -05:00
Exec . CompletedStateHandle = ParentLinkedState ;
Exec . LastTickStatus = RunStatus ;
2023-06-12 07:34:11 -04:00
// Clear the transition and return that no transition took place.
// Since the LastTickStatus != running, the transition loop will try another transition
// now starting from the linked parent state. If we run out of retires in the selection loop (e.g. very deep hierarchy)
// we will continue on next tick.
NextTransition . Reset ( ) ;
2023-01-23 12:48:04 -05:00
return false ;
}
}
return NextTransition . TargetState . IsValid ( ) ;
2022-05-31 04:51:18 -04:00
}
2022-11-23 09:22:14 -05:00
FStateTreeStateHandle FStateTreeExecutionContext : : GetParentLinkedStateHandle ( const FStateTreeActiveStates & ActiveStates , const int32 StartStartIndex ) const
{
check ( ActiveStates . IsValidIndex ( StartStartIndex ) ) ;
for ( int32 StateIndex = StartStartIndex ; StateIndex > = 0 ; StateIndex - - )
{
const FCompactStateTreeState & State = StateTree . States [ ActiveStates [ StateIndex ] . Index ] ;
if ( State . LinkedState . IsValid ( ) )
{
return ActiveStates [ StateIndex ] ;
}
}
return FStateTreeStateHandle ( ) ;
}
2023-01-23 12:48:04 -05:00
FStateTreeStateHandle FStateTreeExecutionContext : : GetParentLinkedStateHandle ( const FStateTreeActiveStates & ActiveStates , const FStateTreeStateHandle StartStateHandle ) const
{
// Find start state
int32 StateIndex = ActiveStates . Num ( ) - 1 ;
while ( StateIndex > = 0 )
{
if ( ActiveStates [ StateIndex ] = = StartStateHandle )
{
break ;
}
StateIndex - - ;
}
2023-06-12 07:34:11 -04:00
// The function result is used to iteratively traverse to the root-most parent linked state.
// Skip the start state, as we want to always find a parent state to the start state, or else the iteration will hit infinite loop.
StateIndex - - ;
2023-01-23 12:48:04 -05:00
// Find parent linked state.
while ( StateIndex > = 0 )
{
const FCompactStateTreeState & State = StateTree . States [ ActiveStates [ StateIndex ] . Index ] ;
if ( State . LinkedState . IsValid ( ) )
{
return ActiveStates [ StateIndex ] ;
}
StateIndex - - ;
}
return FStateTreeStateHandle ( ) ;
}
2023-10-17 16:15:53 -04:00
bool FStateTreeExecutionContext : : SelectState ( const FStateTreeStateHandle NextState , FStateTreeActiveStates & OutNewActiveState , FStateTreeActiveStates & VisitedStates , const EStateTreeSelectionFallback Fallback )
2022-05-31 04:51:18 -04:00
{
2022-09-23 20:02:42 -04:00
const FStateTreeExecutionState & Exec = GetExecState ( ) ;
2022-05-31 04:51:18 -04:00
if ( ! NextState . IsValid ( ) )
{
return false ;
}
// Find common ancestor of `NextState` in the current active states and connect.
// This allows transitions within a subtree.
OutNewActiveState = Exec . ActiveStates ;
TStaticArray < FStateTreeStateHandle , FStateTreeActiveStates : : MaxStates > InBetweenStates ;
int32 NumInBetweenStates = 0 ;
int32 CommonActiveAncestorIndex = INDEX_NONE ;
// Walk towards the root from current state.
FStateTreeStateHandle CurrState = NextState ;
while ( CurrState . IsValid ( ) )
{
// Store the states that are in between the 'NextState' and common ancestor.
InBetweenStates [ NumInBetweenStates + + ] = CurrState ;
// Check if the state can be found in the active states.
CommonActiveAncestorIndex = OutNewActiveState . IndexOfReverse ( CurrState ) ;
if ( CommonActiveAncestorIndex ! = INDEX_NONE )
{
break ;
}
if ( NumInBetweenStates = = InBetweenStates . Num ( ) )
{
2023-04-12 07:59:16 -04:00
STATETREE_LOG ( Error , TEXT ( " %hs: Too many parent states when selecting state '%s' from '%s'. '%s' using StateTree '%s'. " ) ,
__FUNCTION__ , * GetSafeStateName ( NextState ) , * GetStateStatusString ( Exec ) , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & StateTree ) ) ;
2022-05-31 04:51:18 -04:00
return false ;
}
2022-09-23 20:02:42 -04:00
CurrState = StateTree . States [ CurrState . Index ] . Parent ;
2022-05-31 04:51:18 -04:00
}
// Max takes care of INDEX_NONE, by setting the num to 0.
OutNewActiveState . SetNum ( FMath : : Max ( 0 , CommonActiveAncestorIndex ) ) ;
// Append in between state in reverse order, they were collected from leaf towards the root.
bool bActiveStatesOverflow = false ;
for ( int32 Index = NumInBetweenStates - 1 ; Index > 0 ; Index - - )
{
bActiveStatesOverflow | = ! OutNewActiveState . Push ( InBetweenStates [ Index ] ) ;
}
if ( bActiveStatesOverflow )
{
2023-04-12 07:59:16 -04:00
STATETREE_LOG ( Error , TEXT ( " %hs: Reached max execution depth when trying to select state %s from '%s'. '%s' using StateTree '%s'. " ) ,
__FUNCTION__ , * GetSafeStateName ( NextState ) , * GetStateStatusString ( Exec ) , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & StateTree ) ) ;
2022-05-31 04:51:18 -04:00
return false ;
}
2023-10-17 16:15:53 -04:00
if ( SelectStateInternal ( NextState , OutNewActiveState , VisitedStates ) )
{
return true ;
}
// Failed to Select Next State, handle fallback here
// Return true on the first next sibling that gets selected successfully
if ( Fallback = = EStateTreeSelectionFallback : : NextSelectableSibling & & NumInBetweenStates > = 2 )
{
// InBetweenStates is in reversed order (i.e. from leaf to root)
FStateTreeStateHandle Parent = InBetweenStates [ 1 ] ;
if ( Parent . IsValid ( ) )
{
const FCompactStateTreeState & ParentState = StateTree . States [ Parent . Index ] ;
uint16 ChildState = StateTree . States [ NextState . Index ] . GetNextSibling ( ) ;
for ( ; ChildState < ParentState . ChildrenEnd ; ChildState = StateTree . States [ ChildState ] . GetNextSibling ( ) )
{
FStateTreeStateHandle ChildStateHandle = FStateTreeStateHandle ( ChildState ) ;
if ( SelectStateInternal ( ChildStateHandle , OutNewActiveState , VisitedStates ) )
{
return true ;
}
}
}
}
return false ;
2022-05-31 04:51:18 -04:00
}
2023-04-12 07:59:16 -04:00
bool FStateTreeExecutionContext : : SelectStateInternal ( const FStateTreeStateHandle NextState , FStateTreeActiveStates & OutNewActiveState , FStateTreeActiveStates & VisitedStates )
2022-05-31 04:51:18 -04:00
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_SelectState ) ;
2022-09-23 20:02:42 -04:00
const FStateTreeExecutionState & Exec = GetExecState ( ) ;
2022-05-31 04:51:18 -04:00
if ( ! NextState . IsValid ( ) )
{
// Trying to select non-existing state.
2023-04-12 07:59:16 -04:00
STATETREE_LOG ( Error , TEXT ( " %hs: Trying to select invalid state from '%s'. '%s' using StateTree '%s'. " ) ,
__FUNCTION__ , * GetStateStatusString ( Exec ) , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & StateTree ) ) ;
2022-05-31 04:51:18 -04:00
return false ;
}
2022-09-23 20:02:42 -04:00
const FCompactStateTreeState & State = StateTree . States [ NextState . Index ] ;
2022-05-31 04:51:18 -04:00
2023-06-05 13:12:19 -04:00
if ( State . bEnabled = = false )
{
// Do not select disabled state
STATETREE_LOG ( VeryVerbose , TEXT ( " %hs: Ignoring disabled state '%s'. '%s' using StateTree '%s'. " ) ,
__FUNCTION__ , * GetSafeStateName ( NextState ) , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & StateTree ) ) ;
return false ;
}
2023-08-04 14:55:23 -04:00
STATETREE_TRACE_SCOPED_STATE_PHASE ( NextState , EStateTreeUpdatePhase : : StateSelection ) ;
2023-05-23 10:46:16 -04:00
2022-05-31 04:51:18 -04:00
// Check that the state can be entered
2023-05-23 10:46:16 -04:00
bool bEnterConditionsPassed = false ;
if ( State . SelectionBehavior ! = EStateTreeStateSelectionBehavior : : None )
{
2023-05-29 10:13:21 -04:00
STATETREE_TRACE_SCOPED_PHASE ( EStateTreeUpdatePhase : : EnterConditions ) ;
2023-05-23 10:46:16 -04:00
bEnterConditionsPassed = TestAllConditions ( State . EnterConditionsBegin , State . EnterConditionsNum ) ;
}
if ( bEnterConditionsPassed )
2022-05-31 04:51:18 -04:00
{
if ( ! OutNewActiveState . Push ( NextState ) )
{
2023-04-12 07:59:16 -04:00
STATETREE_LOG ( Error , TEXT ( " %hs: Reached max execution depth when trying to select state %s from '%s'. '%s' using StateTree '%s'. " ) ,
__FUNCTION__ , * GetSafeStateName ( NextState ) , * GetStateStatusString ( Exec ) , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & StateTree ) ) ;
return false ;
}
if ( ! VisitedStates . Push ( NextState ) )
{
STATETREE_LOG ( Error , TEXT ( " %hs: Reached max visited state depth when trying to select state %s from '%s'. '%s' using StateTree '%s'. " ) ,
__FUNCTION__ , * GetSafeStateName ( NextState ) , * GetStateStatusString ( Exec ) , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & StateTree ) ) ;
2022-05-31 04:51:18 -04:00
return false ;
}
if ( State . LinkedState . IsValid ( ) )
{
// If State is linked, proceed to the linked state.
2023-04-12 07:59:16 -04:00
if ( SelectStateInternal ( State . LinkedState , OutNewActiveState , VisitedStates ) )
2022-05-31 04:51:18 -04:00
{
// Selection succeeded
return true ;
}
}
2023-04-12 07:59:16 -04:00
else if ( State . SelectionBehavior = = EStateTreeStateSelectionBehavior : : TryEnterState )
2022-05-31 04:51:18 -04:00
{
// Select this state.
2023-05-29 10:13:21 -04:00
STATETREE_TRACE_STATE_EVENT ( NextState , EStateTreeTraceEventType : : OnStateSelected ) ;
2022-05-31 04:51:18 -04:00
return true ;
}
2023-04-12 07:59:16 -04:00
else if ( State . SelectionBehavior = = EStateTreeStateSelectionBehavior : : TryFollowTransitions )
{
EStateTreeTransitionPriority CurrentPriority = EStateTreeTransitionPriority : : None ;
for ( uint8 i = 0 ; i < State . TransitionsNum ; i + + )
{
const int16 TransitionIndex = State . TransitionsBegin + i ;
const FCompactStateTransition & Transition = StateTree . Transitions [ TransitionIndex ] ;
2023-06-20 13:49:25 -04:00
// Skip disabled transitions
if ( Transition . bTransitionEnabled = = false )
{
continue ;
}
2023-04-12 07:59:16 -04:00
// No need to test the transition if same or higher priority transition has already been processed.
if ( Transition . Priority < = CurrentPriority )
{
continue ;
}
// Skip completion transitions
if ( EnumHasAnyFlags ( Transition . Trigger , EStateTreeTransitionTrigger : : OnStateCompleted ) )
{
continue ;
}
// Cannot follow transitions with delay.
if ( Transition . HasDelay ( ) )
{
continue ;
}
// Try to prevent (infinite) loops in the selection.
if ( VisitedStates . Contains ( Transition . State ) )
{
STATETREE_LOG ( Error , TEXT ( " %hs: Loop detected when trying to select state %s from '%s'. Prior states: %s. '%s' using StateTree '%s'. " ) ,
__FUNCTION__ , * GetSafeStateName ( NextState ) , * GetStateStatusString ( Exec ) , * DebugGetStatePath ( VisitedStates ) , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & StateTree ) ) ;
continue ;
}
const bool bShouldTrigger = Transition . Trigger = = EStateTreeTransitionTrigger : : OnTick
| | ( Transition . Trigger = = EStateTreeTransitionTrigger : : OnEvent
& & HasEventToProcess ( Transition . EventTag ) ) ;
2023-05-23 10:46:16 -04:00
bool bTransitionConditionsPassed = false ;
if ( bShouldTrigger )
{
2023-08-04 14:55:23 -04:00
STATETREE_TRACE_TRANSITION_EVENT ( FStateTreeTransitionSource ( FStateTreeIndex16 ( TransitionIndex ) , Transition . State , Transition . Priority ) , EStateTreeTraceEventType : : OnEvaluating ) ;
2023-05-29 10:13:21 -04:00
STATETREE_TRACE_SCOPED_PHASE ( EStateTreeUpdatePhase : : TransitionConditions ) ;
2023-05-23 10:46:16 -04:00
bTransitionConditionsPassed = bShouldTrigger & & TestAllConditions ( Transition . ConditionsBegin , Transition . ConditionsNum ) ;
}
if ( bTransitionConditionsPassed )
2023-04-12 07:59:16 -04:00
{
// Using SelectState() instead of SelectStateInternal to treat the transitions the same way as regular transitions,
// e.g. it may jump to a completely different branch.
FStateTreeActiveStates NewActiveState ;
2023-10-17 16:15:53 -04:00
if ( SelectState ( Transition . State , NewActiveState , VisitedStates , Transition . Fallback ) )
2023-04-12 07:59:16 -04:00
{
// Selection succeeded
OutNewActiveState = NewActiveState ;
CurrentPriority = Transition . Priority ;
}
}
}
if ( CurrentPriority ! = EStateTreeTransitionPriority : : None )
{
return true ;
}
}
else if ( State . SelectionBehavior = = EStateTreeStateSelectionBehavior : : TrySelectChildrenInOrder )
{
if ( State . HasChildren ( ) )
{
// If the state has children, proceed to select children.
for ( uint16 ChildState = State . ChildrenBegin ; ChildState < State . ChildrenEnd ; ChildState = StateTree . States [ ChildState ] . GetNextSibling ( ) )
{
if ( SelectStateInternal ( FStateTreeStateHandle ( ChildState ) , OutNewActiveState , VisitedStates ) )
{
// Selection succeeded
return true ;
}
}
}
else
{
// Select this state (For backwards compatibility)
2023-05-29 10:13:21 -04:00
STATETREE_TRACE_STATE_EVENT ( NextState , EStateTreeTraceEventType : : OnStateSelected ) ;
2023-04-12 07:59:16 -04:00
return true ;
}
}
2022-05-31 04:51:18 -04:00
OutNewActiveState . Pop ( ) ;
2023-04-12 07:59:16 -04:00
VisitedStates . Pop ( ) ;
2022-05-31 04:51:18 -04:00
}
// Nothing got selected.
return false ;
}
FString FStateTreeExecutionContext : : GetSafeStateName ( const FStateTreeStateHandle State ) const
{
if ( State = = FStateTreeStateHandle : : Invalid )
{
return TEXT ( " (State Invalid) " ) ;
}
else if ( State = = FStateTreeStateHandle : : Succeeded )
{
return TEXT ( " (State Succeeded) " ) ;
}
else if ( State = = FStateTreeStateHandle : : Failed )
{
return TEXT ( " (State Failed) " ) ;
}
2022-09-23 20:02:42 -04:00
else if ( StateTree . States . IsValidIndex ( State . Index ) )
2022-05-31 04:51:18 -04:00
{
2022-09-23 20:02:42 -04:00
return * StateTree . States [ State . Index ] . Name . ToString ( ) ;
2022-05-31 04:51:18 -04:00
}
return TEXT ( " (Unknown) " ) ;
}
FString FStateTreeExecutionContext : : DebugGetStatePath ( const FStateTreeActiveStates & ActiveStates , const int32 ActiveStateIndex ) const
{
FString StatePath ;
2022-11-03 14:21:53 -04:00
const int32 Num = ActiveStateIndex = = INDEX_NONE ? ActiveStates . Num ( ) : ( ActiveStateIndex + 1 ) ;
if ( ! ensureMsgf ( ActiveStates . IsValidIndex ( Num - 1 ) , TEXT ( " Provided index must be valid " ) ) )
2022-05-31 04:51:18 -04:00
{
return StatePath ;
}
2022-11-03 14:21:53 -04:00
for ( int32 i = 0 ; i < Num ; i + + )
2022-05-31 04:51:18 -04:00
{
2022-09-23 20:02:42 -04:00
const FCompactStateTreeState & State = StateTree . States [ ActiveStates [ i ] . Index ] ;
2022-05-31 04:51:18 -04:00
StatePath . Appendf ( TEXT ( " %s%s " ) , i = = 0 ? TEXT ( " " ) : TEXT ( " . " ) , * State . Name . ToString ( ) ) ;
}
return StatePath ;
}
FString FStateTreeExecutionContext : : GetStateStatusString ( const FStateTreeExecutionState & ExecState ) const
{
return GetSafeStateName ( ExecState . ActiveStates . Last ( ) ) + TEXT ( " : " ) + UEnum : : GetDisplayValueAsText ( ExecState . LastTickStatus ) . ToString ( ) ;
}
2022-09-23 20:02:42 -04:00
EStateTreeRunStatus FStateTreeExecutionContext : : GetLastTickStatus ( ) const
2022-05-31 04:51:18 -04:00
{
2022-09-23 20:02:42 -04:00
const FStateTreeExecutionState & Exec = GetExecState ( ) ;
2022-05-31 04:51:18 -04:00
return Exec . LastTickStatus ;
}
FString FStateTreeExecutionContext : : GetInstanceDescription ( ) const
{
2023-06-06 11:25:12 -04:00
return FString : : Printf ( TEXT ( " %s " ) , * GetNameSafe ( & Owner ) ) ;
2022-05-31 04:51:18 -04:00
}
2022-09-23 20:02:42 -04:00
const FStateTreeActiveStates & FStateTreeExecutionContext : : GetActiveStates ( ) const
2022-05-31 04:51:18 -04:00
{
2022-09-23 20:02:42 -04:00
const FStateTreeExecutionState & Exec = GetExecState ( ) ;
2022-05-31 04:51:18 -04:00
return Exec . ActiveStates ;
}
# if WITH_GAMEPLAY_DEBUGGER
2022-09-23 20:02:42 -04:00
FString FStateTreeExecutionContext : : GetDebugInfoString ( ) const
2022-05-31 04:51:18 -04:00
{
2022-09-23 20:02:42 -04:00
const FStateTreeExecutionState & Exec = GetExecState ( ) ;
2022-05-31 04:51:18 -04:00
2022-09-23 20:02:42 -04:00
FString DebugString = FString : : Printf ( TEXT ( " StateTree (asset: '%s') \n " ) , * GetFullNameSafe ( & StateTree ) ) ;
2022-05-31 04:51:18 -04:00
DebugString + = TEXT ( " Status: " ) ;
switch ( Exec . TreeRunStatus )
{
case EStateTreeRunStatus : : Failed :
DebugString + = TEXT ( " Failed \n " ) ;
break ;
case EStateTreeRunStatus : : Succeeded :
DebugString + = TEXT ( " Succeeded \n " ) ;
break ;
case EStateTreeRunStatus : : Running :
DebugString + = TEXT ( " Running \n " ) ;
break ;
default :
DebugString + = TEXT ( " -- \n " ) ;
}
2022-09-23 20:02:42 -04:00
if ( StateTree . EvaluatorsNum > 0 )
2022-05-31 04:51:18 -04:00
{
DebugString + = TEXT ( " \n Evaluators: \n " ) ;
2022-09-23 20:02:42 -04:00
for ( int32 EvalIndex = StateTree . EvaluatorsBegin ; EvalIndex < ( StateTree . EvaluatorsBegin + StateTree . EvaluatorsNum ) ; EvalIndex + + )
2022-05-31 04:51:18 -04:00
{
2023-02-13 20:06:02 -05:00
const FStateTreeEvaluatorBase & Eval = StateTree . Nodes [ EvalIndex ] . Get < const FStateTreeEvaluatorBase > ( ) ;
2022-05-31 04:51:18 -04:00
Eval . AppendDebugInfoString ( DebugString , * this ) ;
}
}
// Active States
DebugString + = TEXT ( " Current State: \n " ) ;
for ( int32 Index = 0 ; Index < Exec . ActiveStates . Num ( ) ; Index + + )
{
FStateTreeStateHandle Handle = Exec . ActiveStates [ Index ] ;
if ( Handle . IsValid ( ) )
{
2022-09-23 20:02:42 -04:00
const FCompactStateTreeState & State = StateTree . States [ Handle . Index ] ;
2022-05-31 04:51:18 -04:00
DebugString + = FString : : Printf ( TEXT ( " [%s] \n " ) , * State . Name . ToString ( ) ) ;
if ( State . TasksNum > 0 )
{
DebugString + = TEXT ( " \n Tasks: \n " ) ;
for ( int32 TaskIndex = State . TasksBegin ; TaskIndex < ( State . TasksBegin + State . TasksNum ) ; TaskIndex + + )
{
2023-02-13 20:06:02 -05:00
const FStateTreeTaskBase & Task = StateTree . Nodes [ TaskIndex ] . Get < const FStateTreeTaskBase > ( ) ;
2023-06-05 13:12:19 -04:00
if ( Task . bTaskEnabled )
{
Task . AppendDebugInfoString ( DebugString , * this ) ;
}
2022-05-31 04:51:18 -04:00
}
}
}
}
return DebugString ;
}
# endif // WITH_GAMEPLAY_DEBUGGER
# if WITH_STATETREE_DEBUG
2022-09-23 20:02:42 -04:00
void FStateTreeExecutionContext : : DebugPrintInternalLayout ( )
2022-05-31 04:51:18 -04:00
{
LOG_SCOPE_VERBOSITY_OVERRIDE ( LogStateTree , ELogVerbosity : : Log ) ;
2022-09-23 20:02:42 -04:00
FString DebugString = FString : : Printf ( TEXT ( " StateTree (asset: '%s') \n " ) , * GetFullNameSafe ( & StateTree ) ) ;
2022-05-31 04:51:18 -04:00
// Tree items (e.g. tasks, evaluators, conditions)
2022-09-23 20:02:42 -04:00
DebugString + = FString : : Printf ( TEXT ( " \n Items(%d) \n " ) , StateTree . Nodes . Num ( ) ) ;
for ( int32 Index = 0 ; Index < StateTree . Nodes . Num ( ) ; Index + + )
2022-05-31 04:51:18 -04:00
{
2022-12-05 09:16:27 -05:00
const FConstStructView Node = StateTree . Nodes [ Index ] ;
2022-05-31 04:51:18 -04:00
DebugString + = FString : : Printf ( TEXT ( " %s \n " ) , Node . IsValid ( ) ? * Node . GetScriptStruct ( ) - > GetName ( ) : TEXT ( " null " ) ) ;
}
// Instance InstanceData data (e.g. tasks)
2022-09-23 20:02:42 -04:00
DebugString + = FString : : Printf ( TEXT ( " \n Instance Structs(%d) \n " ) , StateTree . DefaultInstanceData . NumStructs ( ) ) ;
for ( int32 Index = 0 ; Index < StateTree . DefaultInstanceData . NumStructs ( ) ; Index + + )
2022-05-31 04:51:18 -04:00
{
2022-09-23 20:02:42 -04:00
const FConstStructView Data = StateTree . DefaultInstanceData . GetStruct ( Index ) ;
DebugString + = FString : : Printf ( TEXT ( " %s \n " ) , Data . IsValid ( ) ? * Data . GetScriptStruct ( ) - > GetName ( ) : TEXT ( " null " ) ) ;
2022-05-31 04:51:18 -04:00
}
2022-09-23 20:02:42 -04:00
DebugString + = FString : : Printf ( TEXT ( " \n Instance Objects(%d) \n " ) , StateTree . DefaultInstanceData . NumObjects ( ) ) ;
for ( int32 Index = 0 ; Index < StateTree . DefaultInstanceData . NumObjects ( ) ; Index + + )
2022-05-31 04:51:18 -04:00
{
2022-09-23 20:02:42 -04:00
const UObject * Data = StateTree . DefaultInstanceData . GetObject ( Index ) ;
DebugString + = FString : : Printf ( TEXT ( " %s \n " ) , * GetNameSafe ( Data ) ) ;
2022-05-31 04:51:18 -04:00
}
// External data (e.g. fragments, subsystems)
2022-09-23 20:02:42 -04:00
DebugString + = FString : : Printf ( TEXT ( " \n External Data(%d) \n [ %-40s | %-8s | %5s ] \n " ) , StateTree . ExternalDataDescs . Num ( ) , TEXT ( " Name " ) , TEXT ( " Optional " ) , TEXT ( " Index " ) ) ;
for ( const FStateTreeExternalDataDesc & Desc : StateTree . ExternalDataDescs )
2022-05-31 04:51:18 -04:00
{
2022-11-03 14:21:53 -04:00
DebugString + = FString : : Printf ( TEXT ( " | %-40s | %8s | %5d | \n " ) , Desc . Struct ? * Desc . Struct - > GetName ( ) : TEXT ( " null " ) , * UEnum : : GetDisplayValueAsText ( Desc . Requirement ) . ToString ( ) , Desc . Handle . DataViewIndex . Get ( ) ) ;
2022-05-31 04:51:18 -04:00
}
// Bindings
2022-09-23 20:02:42 -04:00
StateTree . PropertyBindings . DebugPrintInternalLayout ( DebugString ) ;
2022-05-31 04:51:18 -04:00
// Transitions
2023-01-23 12:48:04 -05:00
DebugString + = FString : : Printf ( TEXT ( " \n Transitions(%d) \n [ %-3s | %15s | %-20s | %-40s | %-8s ] \n " ) , StateTree . Transitions . Num ( )
, TEXT ( " Idx " ) , TEXT ( " State " ) , TEXT ( " Transition Trigger " ) , TEXT ( " Transition Event Tag " ) , TEXT ( " Num Cond " ) ) ;
2022-09-23 20:02:42 -04:00
for ( const FCompactStateTransition & Transition : StateTree . Transitions )
2022-05-31 04:51:18 -04:00
{
2023-01-23 12:48:04 -05:00
DebugString + = FString : : Printf ( TEXT ( " | %3d | %15s | %-20s | %-40s | %8d | \n " ) ,
2022-05-31 04:51:18 -04:00
Transition . ConditionsBegin , * Transition . State . Describe ( ) ,
2022-09-01 09:06:53 -04:00
* UEnum : : GetDisplayValueAsText ( Transition . Trigger ) . ToString ( ) ,
* Transition . EventTag . ToString ( ) ,
2022-05-31 04:51:18 -04:00
Transition . ConditionsNum ) ;
}
// DataViews
DebugString + = FString : : Printf ( TEXT ( " \n DataViews(%d) \n " ) , DataViews . Num ( ) ) ;
for ( const FStateTreeDataView & DataView : DataViews )
{
DebugString + = FString : : Printf ( TEXT ( " [%s] \n " ) , DataView . IsValid ( ) ? * DataView . GetStruct ( ) - > GetName ( ) : TEXT ( " null " ) ) ;
}
// States
DebugString + = FString : : Printf ( TEXT ( " \n States(%d) \n "
" [ %-30s | %15s | %5s [%3s:%-3s[ | Begin Idx : %4s %4s %4s %4s | Num : %4s %4s %4s %4s | Transitions : %-16s %-40s %-16s %-40s ] \n " ) ,
2022-09-23 20:02:42 -04:00
StateTree . States . Num ( ) ,
2022-05-31 04:51:18 -04:00
TEXT ( " Name " ) , TEXT ( " Parent " ) , TEXT ( " Child " ) , TEXT ( " Beg " ) , TEXT ( " End " ) ,
TEXT ( " Cond " ) , TEXT ( " Tr " ) , TEXT ( " Tsk " ) , TEXT ( " Evt " ) , TEXT ( " Cond " ) , TEXT ( " Tr " ) , TEXT ( " Tsk " ) , TEXT ( " Evt " ) ,
TEXT ( " Done State " ) , TEXT ( " Done Type " ) , TEXT ( " Failed State " ) , TEXT ( " Failed Type " )
) ;
2022-09-23 20:02:42 -04:00
for ( const FCompactStateTreeState & State : StateTree . States )
2022-05-31 04:51:18 -04:00
{
DebugString + = FString : : Printf ( TEXT ( " | %-30s | %15s | %5s [%3d:%-3d[ | %9s %4d %4d %4d | %3s %4d %4d %4d \n " ) ,
* State . Name . ToString ( ) , * State . Parent . Describe ( ) ,
TEXT ( " " ) , State . ChildrenBegin , State . ChildrenEnd ,
TEXT ( " " ) , State . EnterConditionsBegin , State . TransitionsBegin , State . TasksBegin ,
TEXT ( " " ) , State . EnterConditionsNum , State . TransitionsNum , State . TasksNum ) ;
}
// Evaluators
2022-09-23 20:02:42 -04:00
if ( StateTree . EvaluatorsNum )
2022-05-31 04:51:18 -04:00
{
DebugString + = FString : : Printf ( TEXT ( " \n Evaluators \n [ %-30s | %8s | %10s ] \n " ) ,
TEXT ( " Name " ) , TEXT ( " Bindings " ) , TEXT ( " Struct Idx " ) ) ;
2022-09-23 20:02:42 -04:00
for ( int32 EvalIndex = StateTree . EvaluatorsBegin ; EvalIndex < ( StateTree . EvaluatorsBegin + StateTree . EvaluatorsNum ) ; EvalIndex + + )
2022-05-31 04:51:18 -04:00
{
2023-02-13 20:06:02 -05:00
const FStateTreeEvaluatorBase & Eval = StateTree . Nodes [ EvalIndex ] . Get < const FStateTreeEvaluatorBase > ( ) ;
2022-05-31 04:51:18 -04:00
DebugString + = FString : : Printf ( TEXT ( " | %-30s | %8d | %10d | \n " ) ,
* Eval . Name . ToString ( ) , Eval . BindingsBatch . Get ( ) , Eval . DataViewIndex . Get ( ) ) ;
}
}
DebugString + = FString : : Printf ( TEXT ( " \n Tasks \n [ %-30s | %-30s | %8s | %10s ] \n " ) ,
TEXT ( " State " ) , TEXT ( " Name " ) , TEXT ( " Bindings " ) , TEXT ( " Struct Idx " ) ) ;
2022-09-23 20:02:42 -04:00
for ( const FCompactStateTreeState & State : StateTree . States )
2022-05-31 04:51:18 -04:00
{
// Tasks
if ( State . TasksNum )
{
for ( int32 TaskIndex = State . TasksBegin ; TaskIndex < ( State . TasksBegin + State . TasksNum ) ; TaskIndex + + )
{
2023-02-13 20:06:02 -05:00
const FStateTreeTaskBase & Task = StateTree . Nodes [ TaskIndex ] . Get < const FStateTreeTaskBase > ( ) ;
2022-05-31 04:51:18 -04:00
DebugString + = FString : : Printf ( TEXT ( " | %-30s | %-30s | %8d | %10d | \n " ) , * State . Name . ToString ( ) ,
* Task . Name . ToString ( ) , Task . BindingsBatch . Get ( ) , Task . DataViewIndex . Get ( ) ) ;
}
}
}
UE_LOG ( LogStateTree , Log , TEXT ( " %s " ) , * DebugString ) ;
}
2022-09-23 20:02:42 -04:00
int32 FStateTreeExecutionContext : : GetStateChangeCount ( ) const
2022-05-31 04:51:18 -04:00
{
if ( ! InstanceData . IsValid ( ) )
{
return 0 ;
}
2022-09-23 20:02:42 -04:00
const FStateTreeExecutionState & Exec = GetExecState ( ) ;
2022-05-31 04:51:18 -04:00
return Exec . StateChangeCount ;
}
# endif // WITH_STATETREE_DEBUG
2022-09-23 20:02:42 -04:00
FString FStateTreeExecutionContext : : GetActiveStateName ( ) const
2022-05-31 04:51:18 -04:00
{
if ( ! InstanceData . IsValid ( ) )
{
return FString ( TEXT ( " <None> " ) ) ;
}
2022-09-23 20:02:42 -04:00
const FStateTreeExecutionState & Exec = GetExecState ( ) ;
2022-05-31 04:51:18 -04:00
FString FullStateName ;
// Active States
for ( int32 Index = 0 ; Index < Exec . ActiveStates . Num ( ) ; Index + + )
{
const FStateTreeStateHandle Handle = Exec . ActiveStates [ Index ] ;
if ( Handle . IsValid ( ) )
{
2022-09-23 20:02:42 -04:00
const FCompactStateTreeState & State = StateTree . States [ Handle . Index ] ;
2022-05-31 04:51:18 -04:00
bool bIsLinked = false ;
if ( Index > 0 )
{
FullStateName + = TEXT ( " \n " ) ;
bIsLinked = Exec . ActiveStates [ Index - 1 ] ! = State . Parent ;
}
FullStateName + = FString : : Printf ( TEXT ( " %*s- " ) , Index * 3 , TEXT ( " " ) ) ; // Indent
FullStateName + = * State . Name . ToString ( ) ;
if ( bIsLinked )
{
FullStateName + = TEXT ( " > " ) ;
}
}
}
switch ( Exec . TreeRunStatus )
{
case EStateTreeRunStatus : : Failed :
FullStateName + = TEXT ( " FAILED \n " ) ;
break ;
case EStateTreeRunStatus : : Succeeded :
FullStateName + = TEXT ( " SUCCEEDED \n " ) ;
break ;
case EStateTreeRunStatus : : Running :
// Empty
break ;
default :
FullStateName + = TEXT ( " -- \n " ) ;
}
return FullStateName ;
}
2022-09-23 20:02:42 -04:00
TArray < FName > FStateTreeExecutionContext : : GetActiveStateNames ( ) const
2022-05-31 04:51:18 -04:00
{
TArray < FName > Result ;
if ( ! InstanceData . IsValid ( ) )
{
return Result ;
}
2022-09-23 20:02:42 -04:00
const FStateTreeExecutionState & Exec = GetExecState ( ) ;
2022-05-31 04:51:18 -04:00
// Active States
for ( int32 Index = 0 ; Index < Exec . ActiveStates . Num ( ) ; Index + + )
{
const FStateTreeStateHandle Handle = Exec . ActiveStates [ Index ] ;
if ( Handle . IsValid ( ) )
{
2022-09-23 20:02:42 -04:00
const FCompactStateTreeState & State = StateTree . States [ Handle . Index ] ;
2022-05-31 04:51:18 -04:00
Result . Add ( State . Name ) ;
}
}
return Result ;
}
# undef STATETREE_LOG
# undef STATETREE_CLOG