2022-05-31 04:51:18 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "StateTreeExecutionContext.h"
# include "StateTree.h"
# include "StateTreeTaskBase.h"
# include "StateTreeEvaluatorBase.h"
# include "StateTreeConditionBase.h"
# include "Containers/StaticArray.h"
# include "VisualLogger/VisualLogger.h"
# include "ProfilingDebugging/CsvProfiler.h"
# include "Logging/LogScopedVerbosityOverride.h"
2022-09-28 01:06:15 -04:00
# include UE_INLINE_GENERATED_CPP_BY_NAME(StateTreeExecutionContext)
2022-05-31 04:51:18 -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__)
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 ( ) ;
}
else
{
STATETREE_LOG ( Warning , TEXT ( " %s: StateTree asset is not valid ('%s' using StateTree '%s') " ) ,
ANSI_TO_TCHAR ( __FUNCTION__ ) , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & StateTree ) ) ;
}
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
{
2022-09-23 20:02:42 -04:00
DataViews [ StateTree . ParametersDataViewIndex . Get ( ) ] = FStateTreeDataView ( StateTree . GetDefaultParameters ( ) . GetMutableValue ( ) ) ;
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
{
2022-09-23 20:02:42 -04:00
DataViews [ StateTree . ParametersDataViewIndex . Get ( ) ] = FStateTreeDataView ( Parameters . GetMutableValue ( ) ) ;
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
{
2022-12-02 07:57:31 -05:00
FStateTreeDataView StateParamsInstance = InstanceData . GetMutableStruct ( ParameterInstanceIndex ) ;
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
{
2022-09-23 20:02:42 -04:00
STATETREE_LOG ( Warning , TEXT ( " %s: StateTree context is not initialized properly ('%s' using StateTree '%s') " ) ,
ANSI_TO_TCHAR ( __FUNCTION__ ) , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & StateTree ) ) ;
2022-05-31 04:51:18 -04:00
return EStateTreeRunStatus : : Failed ;
}
2022-11-23 09:22:14 -05:00
// Stop if still running previous state.
Stop ( ) ;
2022-05-31 04:51:18 -04:00
// Initialize instance data if needed.
if ( ! InstanceData . IsValid ( ) )
{
const FStateTreeActiveStates Empty ;
2022-09-23 20:02:42 -04:00
UpdateInstanceData ( Empty , Empty ) ;
2022-05-31 04:51:18 -04:00
if ( ! InstanceData . IsValid ( ) )
{
STATETREE_LOG ( Warning , TEXT ( " %s: Failed to initialize instance data on '%s' using StateTree '%s'. Try to recompile the StateTree asset. " ) ,
2022-09-23 20:02:42 -04:00
ANSI_TO_TCHAR ( __FUNCTION__ ) , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & StateTree ) ) ;
2022-05-31 04:51:18 -04:00
return EStateTreeRunStatus : : Failed ;
}
}
2022-09-07 17:12:18 -04:00
2022-09-23 20:02:42 -04:00
const TSharedPtr < FStateTreeInstanceData > SharedInstanceData = StateTree . GetSharedInstanceData ( ) ;
2022-09-07 17:12:18 -04:00
check ( SharedInstanceData . IsValid ( ) ) ;
2022-05-31 04:51:18 -04:00
// Call TreeStart on evaluators.
2022-09-23 20:02:42 -04:00
StartEvaluators ( ) ;
2022-05-31 04:51:18 -04:00
// First tick
2022-09-23 20:02:42 -04:00
TickEvaluators ( 0.0f ) ;
2022-05-31 04:51:18 -04:00
// Initialize to unset running state.
2022-11-23 09:22:14 -05:00
FStateTreeExecutionState * Exec = & GetExecState ( ) ; // Using pointer as we will need to reacquire the exec later.
2022-05-31 04:51:18 -04:00
Exec - > TreeRunStatus = EStateTreeRunStatus : : Running ;
Exec - > ActiveStates . Reset ( ) ;
Exec - > LastTickStatus = EStateTreeRunStatus : : Unset ;
static const FStateTreeStateHandle RootState = FStateTreeStateHandle ( 0 ) ;
FStateTreeActiveStates NextActiveStates ;
2022-09-23 20:02:42 -04:00
if ( SelectState ( * SharedInstanceData . Get ( ) , RootState , NextActiveStates ) )
2022-05-31 04:51:18 -04:00
{
if ( NextActiveStates . Last ( ) = = FStateTreeStateHandle : : Succeeded | | NextActiveStates . Last ( ) = = FStateTreeStateHandle : : Failed )
{
// Transition to a terminal state (succeeded/failed), or default transition failed.
STATETREE_LOG ( Warning , TEXT ( " %s: Tree %s at StateTree start on '%s' using StateTree '%s'. " ) ,
2022-09-23 20:02:42 -04:00
ANSI_TO_TCHAR ( __FUNCTION__ ) , NextActiveStates . Last ( ) = = FStateTreeStateHandle : : Succeeded ? TEXT ( " succeeded " ) : TEXT ( " failed " ) , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & StateTree ) ) ;
2022-05-31 04:51:18 -04:00
Exec - > TreeRunStatus = NextActiveStates . Last ( ) = = FStateTreeStateHandle : : Succeeded ? EStateTreeRunStatus : : Succeeded : EStateTreeRunStatus : : Failed ;
}
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.
2022-09-23 20:02:42 -04:00
const EStateTreeRunStatus LastTickStatus = EnterState ( Transition ) ;
2022-05-31 04:51:18 -04:00
// Need to reacquire the exec state as EnterState may alter the allocation.
2022-09-23 20:02:42 -04:00
Exec = & GetExecState ( ) ;
2022-05-31 04:51:18 -04:00
Exec - > LastTickStatus = LastTickStatus ;
// 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
}
}
}
if ( Exec - > ActiveStates . IsEmpty ( ) )
{
// Should not happen. This may happen if initial state could not be selected.
STATETREE_LOG ( Error , TEXT ( " %s: 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. " ) ,
2022-09-23 20:02:42 -04:00
ANSI_TO_TCHAR ( __FUNCTION__ ) , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & StateTree ) ) ;
2022-05-31 04:51:18 -04:00
Exec - > TreeRunStatus = EStateTreeRunStatus : : Failed ;
}
return Exec - > TreeRunStatus ;
}
2022-09-23 20:02:42 -04:00
EStateTreeRunStatus FStateTreeExecutionContext : : Stop ( )
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
{
2022-09-23 20:02:42 -04:00
STATETREE_LOG ( Warning , TEXT ( " %s: StateTree context is not initialized properly ('%s' using StateTree '%s') " ) ,
ANSI_TO_TCHAR ( __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
2022-09-23 20:02:42 -04:00
FStateTreeExecutionState & Exec = GetExecState ( ) ;
2022-05-31 04:51:18 -04:00
2022-11-23 09:22:14 -05:00
if ( Exec . TreeRunStatus ! = EStateTreeRunStatus : : Running )
{
return 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-09-23 20:02:42 -04:00
TickEvaluators ( 0.0f ) ;
2022-05-31 04:51:18 -04:00
// Exit states if still in some valid state.
2022-09-19 19:47:11 -04:00
if ( Exec . TreeRunStatus = = EStateTreeRunStatus : : Running & & ! Exec . ActiveStates . IsEmpty ( ) )
2022-05-31 04:51:18 -04:00
{
// Transition to Succeeded state.
FStateTreeTransitionResult Transition ;
Transition . TargetState = FStateTreeStateHandle : : Succeeded ;
Transition . CurrentActiveStates = Exec . ActiveStates ;
Transition . CurrentRunStatus = Exec . LastTickStatus ;
Transition . NextActiveStates = FStateTreeActiveStates ( FStateTreeStateHandle : : Succeeded ) ;
2022-09-23 20:02:42 -04:00
ExitState ( Transition ) ;
2022-05-31 04:51:18 -04:00
Exec . TreeRunStatus = EStateTreeRunStatus : : Succeeded ;
2022-09-19 19:47:11 -04:00
Exec . ActiveStates . Reset ( ) ;
2022-05-31 04:51:18 -04:00
}
else
{
Exec . TreeRunStatus = Exec . ActiveStates . Last ( ) = = FStateTreeStateHandle : : Succeeded ? EStateTreeRunStatus : : Succeeded : EStateTreeRunStatus : : Failed ;
}
// Call TreeStop on evaluators.
2022-09-23 20:02:42 -04:00
StopEvaluators ( ) ;
2022-05-31 04:51:18 -04:00
Exec . ActiveStates . Reset ( ) ;
Exec . LastTickStatus = EStateTreeRunStatus : : Unset ;
Exec . FirstTaskStructIndex = FStateTreeIndex16 : : Invalid ;
Exec . FirstTaskObjectIndex = FStateTreeIndex16 : : Invalid ;
const EStateTreeRunStatus Result = Exec . TreeRunStatus ;
// 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 ) ;
2022-09-23 20:02:42 -04:00
if ( ! IsValid ( ) )
2022-05-31 04:51:18 -04:00
{
2022-09-23 20:02:42 -04:00
STATETREE_LOG ( Warning , TEXT ( " %s: StateTree context is not initialized properly ('%s' using StateTree '%s') " ) ,
ANSI_TO_TCHAR ( __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 ( ) )
{
STATETREE_LOG ( Error , TEXT ( " %s: Tick called on %s using StateTree %s with invalid instance data. Start() must be called before Tick(). " ) ,
2022-09-23 20:02:42 -04:00
ANSI_TO_TCHAR ( __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-09-23 20:02:42 -04:00
const TSharedPtr < FStateTreeInstanceData > SharedInstanceData = StateTree . GetSharedInstanceData ( ) ;
2022-09-07 17:12:18 -04:00
check ( SharedInstanceData . IsValid ( ) ) ;
2022-11-01 15:11:19 -04:00
2022-11-30 07:17:26 -05:00
FStateTreeEventQueue & EventQueue = InstanceData . GetMutableEventQueue ( ) ;
2022-11-01 15:11:19 -04:00
2022-09-23 20:02:42 -04:00
FStateTreeExecutionState * Exec = & GetExecState ( ) ;
2022-05-31 04:51:18 -04:00
2022-09-01 09:06:53 -04:00
// Capture events added between ticks.
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
// No ticking of the tree is done or stopped.
if ( Exec - > TreeRunStatus ! = EStateTreeRunStatus : : Running )
{
return Exec - > TreeRunStatus ;
}
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
}
// Tick global evaluators.
2022-09-23 20:02:42 -04:00
TickEvaluators ( DeltaTime ) ;
2022-05-31 04:51:18 -04:00
if ( Exec - > LastTickStatus = = EStateTreeRunStatus : : Running )
{
// Tick tasks on active states.
2022-09-23 20:02:42 -04:00
Exec - > LastTickStatus = TickTasks ( DeltaTime ) ;
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
}
}
// 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-11-01 15:11:19 -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 ( ) ) ;
2022-05-31 04:51:18 -04:00
// Trigger conditional transitions or state succeed/failed transitions. First tick transition is handled here too.
FStateTreeTransitionResult Transition ;
2022-09-23 20:02:42 -04:00
if ( TriggerTransitions ( * SharedInstanceData . Get ( ) , Transition ) )
2022-05-31 04:51:18 -04:00
{
2022-09-01 09:06:53 -04:00
// We have committed to state change, consume events that were accumulated during the tick above.
2022-11-01 15:11:19 -04:00
EventQueue . Reset ( ) ;
2022-09-01 09:06:53 -04:00
2022-09-23 20:02:42 -04:00
ExitState ( Transition ) ;
2022-09-01 09:06:53 -04:00
2022-05-31 04:51:18 -04:00
if ( Transition . NextActiveStates . Last ( ) = = FStateTreeStateHandle : : Succeeded | | Transition . NextActiveStates . Last ( ) = = FStateTreeStateHandle : : Failed )
{
// Transition to a terminal state (succeeded/failed), or default transition failed.
Exec - > TreeRunStatus = Transition . NextActiveStates . Last ( ) = = FStateTreeStateHandle : : Succeeded ? EStateTreeRunStatus : : Succeeded : EStateTreeRunStatus : : Failed ;
2022-09-19 19:47:11 -04:00
Exec - > ActiveStates . Reset ( ) ;
2022-05-31 04:51:18 -04:00
return Exec - > TreeRunStatus ;
}
2022-09-01 09:06:53 -04:00
// Append and consume the events accumulated during the state exit.
2022-11-01 15:11:19 -04:00
EventsToProcess . Append ( EventQueue . GetEvents ( ) ) ;
EventQueue . Reset ( ) ;
2022-09-01 09:06:53 -04:00
2022-05-31 04:51:18 -04:00
// Enter state tasks can fail/succeed, treat it same as tick.
2022-09-23 20:02:42 -04:00
const EStateTreeRunStatus LastTickStatus = EnterState ( Transition ) ;
2022-05-31 04:51:18 -04:00
2022-11-01 15:11:19 -04:00
// Consider events so far processed. Events sent during EnterState went into EventQueue, and are processed in next iteration.
EventsToProcess . Reset ( ) ;
2022-05-31 04:51:18 -04:00
// Need to reacquire the exec state as EnterState may alter the allocation.
2022-09-23 20:02:42 -04:00
Exec = & GetExecState ( ) ;
2022-05-31 04:51:18 -04:00
Exec - > LastTickStatus = LastTickStatus ;
// 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
}
}
// Stop as soon as have found a running state.
if ( Exec - > LastTickStatus = = EStateTreeRunStatus : : Running )
{
break ;
}
}
2022-09-01 09:06:53 -04:00
2022-05-31 04:51:18 -04:00
if ( Exec - > ActiveStates . IsEmpty ( ) )
{
// Should not happen. This may happen if a state completion transition could not be selected.
STATETREE_LOG ( Error , TEXT ( " %s: Failed to select state on '%s' using StateTree '%s'. This should not happen, state completion transition is likely missing. " ) ,
2022-09-23 20:02:42 -04:00
ANSI_TO_TCHAR ( __FUNCTION__ ) , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & StateTree ) ) ;
2022-05-31 04:51:18 -04:00
Exec - > TreeRunStatus = EStateTreeRunStatus : : Failed ;
return Exec - > TreeRunStatus ;
}
2022-09-01 09:06:53 -04:00
EventsToProcess . Reset ( ) ;
2022-05-31 04:51:18 -04:00
return Exec - > TreeRunStatus ;
}
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
{
2022-09-23 20:02:42 -04:00
STATETREE_LOG ( Warning , TEXT ( " %s: StateTree context is not initialized properly ('%s' using StateTree '%s') " ) ,
ANSI_TO_TCHAR ( __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 ( ) )
{
2022-09-23 20:02:42 -04:00
STATETREE_LOG ( Error , TEXT ( " %s: SendEvent called on %s using StateTree %s with invalid instance data. Start() must be called before sending events. " ) ,
ANSI_TO_TCHAR ( __FUNCTION__ ) , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & StateTree ) ) ;
2022-09-01 09:06:53 -04:00
return ;
}
2022-11-03 14:21:53 -04:00
STATETREE_LOG ( Verbose , TEXT ( " Send Event '%s' " ) , * Tag . ToString ( ) ) ;
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
}
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
{
2022-09-23 20:02:42 -04:00
const FStateTreeEvaluatorBase & Eval = StateTree . Nodes [ EvalIndex ] . Get < 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
}
}
// 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 + + )
{
2022-09-23 20:02:42 -04:00
const FStateTreeTaskBase & Task = StateTree . Nodes [ TaskIndex ] . Get < 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 ;
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 ;
Exec . EnterStateFailedTaskIndex = FStateTreeIndex16 : : Invalid ; // This will make all tasks to be accepted.
Exec . ActiveStates . Reset ( ) ;
// Do property copy on all states, propagating the results from last tick.
check ( Exec . FirstTaskStructIndex . IsValid ( ) & & Exec . FirstTaskObjectIndex . IsValid ( ) ) ;
int32 InstanceStructIndex = Exec . FirstTaskStructIndex . Get ( ) ;
int32 InstanceObjectIndex = Exec . FirstTaskObjectIndex . Get ( ) ;
2022-11-03 14:21:53 -04:00
STATETREE_LOG ( Log , TEXT ( " Enter state '%s' (%d) " ) , * DebugGetStatePath ( Transition . NextActiveStates ) , Exec . StateChangeCount ) ;
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
if ( ! Exec . ActiveStates . Push ( CurrentHandle ) )
{
STATETREE_LOG ( Error , TEXT ( " %s: Reached max execution depth when trying to enter state '%s'. '%s' using StateTree '%s'. " ) ,
2022-09-23 20:02:42 -04:00
ANSI_TO_TCHAR ( __FUNCTION__ ) , * GetStateStatusString ( Exec ) , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & StateTree ) ) ;
2022-05-31 04:51:18 -04:00
break ;
}
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 ;
const bool bIsEnteringState = ! bWasActive | | bOnTargetBranch ;
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
2022-11-03 14:21:53 -04:00
STATETREE_CLOG ( bIsEnteringState , 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 + + )
{
2022-09-23 20:02:42 -04:00
const FStateTreeTaskBase & Task = StateTree . Nodes [ TaskIndex ] . Get < FStateTreeTaskBase > ( ) ;
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
}
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 ( ) ) ;
2022-05-31 04:51:18 -04:00
QUICK_SCOPE_CYCLE_COUNTER ( StateTree_Task_EnterState ) ;
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_Task_EnterState ) ;
2022-09-28 09:55:53 -04:00
const EStateTreeRunStatus Status = Task . EnterState ( * this , CurrentTransition ) ;
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 ;
}
}
}
}
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 + + )
{
2022-09-23 20:02:42 -04:00
const FStateTreeTaskBase & Task = StateTree . Nodes [ TaskIndex ] . Get < FStateTreeTaskBase > ( ) ;
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 ) ;
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 ( ) ) ;
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 ( ) )
{
2022-09-23 20:02:42 -04:00
const FStateTreeTaskBase & Task = StateTree . Nodes [ TaskIndex ] . Get < FStateTreeTaskBase > ( ) ;
2022-05-31 04:51:18 -04:00
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 ) ;
}
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
2022-11-03 14:21:53 -04:00
STATETREE_LOG ( Verbose , TEXT ( " %*sState '%s' " ) , Index * UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * DebugGetStatePath ( Exec . ActiveStates , Index ) ) ;
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 ( ) )
{
2022-09-23 20:02:42 -04:00
const FStateTreeTaskBase & Task = StateTree . Nodes [ TaskIndex ] . Get < FStateTreeTaskBase > ( ) ;
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-05-31 04:51:18 -04:00
Task . StateCompleted ( * this , Exec . LastTickStatus , Exec . ActiveStates ) ;
}
}
}
}
2022-09-23 20:02:42 -04:00
void FStateTreeExecutionContext : : TickEvaluators ( const float DeltaTime )
2022-05-31 04:51:18 -04:00
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_TickEvaluators ) ;
2022-09-23 20:02:42 -04:00
STATETREE_CLOG ( StateTree . EvaluatorsNum > 0 , Verbose , 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
{
2022-09-23 20:02:42 -04:00
const FStateTreeEvaluatorBase & Eval = StateTree . Nodes [ EvalIndex ] . Get < FStateTreeEvaluatorBase > ( ) ;
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
}
STATETREE_LOG ( Verbose , TEXT ( " Tick: '%s' " ) , * Eval . Name . ToString ( ) ) ;
{
QUICK_SCOPE_CYCLE_COUNTER ( StateTree_Eval_Tick ) ;
Eval . Tick ( * this , DeltaTime ) ;
}
}
}
2022-09-23 20:02:42 -04:00
void FStateTreeExecutionContext : : StartEvaluators ( )
2022-05-31 04:51:18 -04:00
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_StartEvaluators ) ;
2022-09-23 20:02:42 -04:00
STATETREE_CLOG ( StateTree . EvaluatorsNum > 0 , Verbose , TEXT ( " Start 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
{
2022-09-23 20:02:42 -04:00
const FStateTreeEvaluatorBase & Eval = StateTree . Nodes [ EvalIndex ] . Get < FStateTreeEvaluatorBase > ( ) ;
2022-05-31 04:51:18 -04:00
if ( Eval . bInstanceIsObject )
{
DataViews [ Eval . DataViewIndex . Get ( ) ] = InstanceData . GetMutableObject ( InstanceObjectIndex ) ;
InstanceObjectIndex + + ;
}
else
{
DataViews [ Eval . DataViewIndex . Get ( ) ] = InstanceData . GetMutableStruct ( InstanceStructIndex ) ;
InstanceStructIndex + + ;
}
// 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
}
STATETREE_LOG ( Verbose , TEXT ( " Start: '%s' " ) , * Eval . Name . ToString ( ) ) ;
{
QUICK_SCOPE_CYCLE_COUNTER ( StateTree_Eval_TreeStart ) ;
Eval . TreeStart ( * this ) ;
}
}
}
2022-09-23 20:02:42 -04:00
void FStateTreeExecutionContext : : StopEvaluators ( )
2022-05-31 04:51:18 -04:00
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_StopEvaluators ) ;
2022-09-23 20:02:42 -04:00
STATETREE_CLOG ( StateTree . EvaluatorsNum > 0 , Verbose , TEXT ( " Stop 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
{
2022-09-23 20:02:42 -04:00
const FStateTreeEvaluatorBase & Eval = StateTree . Nodes [ EvalIndex ] . Get < FStateTreeEvaluatorBase > ( ) ;
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
}
STATETREE_LOG ( Verbose , TEXT ( " Stop: '%s' " ) , * Eval . Name . ToString ( ) ) ;
{
QUICK_SCOPE_CYCLE_COUNTER ( StateTree_Eval_TreeStop ) ;
Eval . TreeStop ( * this ) ;
}
}
}
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 ) ;
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 ;
2022-09-27 10:55:54 -04:00
// Used to stop ticking tasks after one fails, but we still want to keep updating the data of them
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
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 + + )
{
2022-09-23 20:02:42 -04:00
const FStateTreeTaskBase & Task = StateTree . Nodes [ TaskIndex ] . Get < FStateTreeTaskBase > ( ) ;
SetNodeDataView ( Task , InstanceStructIndex , InstanceObjectIndex ) ;
2022-05-31 04:51:18 -04:00
2022-11-03 14:21:53 -04:00
const bool bNeedsTick = bShouldTickTasks & & ( Task . bShouldCallTick | | ( bHasEvents & & Task . bShouldCallTickOnlyOnEvents ) ) ;
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.
if ( Task . BindingsBatch . IsValid ( ) & & bNeedsTick & & Task . bShouldCopyBoundPropertiesOnTick )
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
}
2022-11-01 15:11:19 -04:00
2022-11-03 14:21:53 -04:00
STATETREE_LOG ( VeryVerbose , TEXT ( " %*s Tick: '%s' %s " ) , Index * UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * Task . Name . ToString ( ) , ! bNeedsTick ? TEXT ( " [not ticked] " ) : TEXT ( " " ) ) ;
if ( bNeedsTick )
2022-05-31 04:51:18 -04:00
{
QUICK_SCOPE_CYCLE_COUNTER ( StateTree_Task_Tick ) ;
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_Task_Tick ) ;
const EStateTreeRunStatus TaskResult = Task . Tick ( * this , DeltaTime ) ;
// TODO: Add more control over which states can control the failed/succeeded result.
if ( TaskResult ! = EStateTreeRunStatus : : Running )
{
2022-11-22 08:13:54 -05:00
// Store the first state that completed, will be used to decide where to trigger transitions.
if ( ! Exec . CompletedStateHandle . IsValid ( ) )
{
Exec . CompletedStateHandle = CurrentHandle ;
}
2022-05-31 04:51:18 -04:00
Result = TaskResult ;
}
2022-09-27 10:55:54 -04:00
2022-05-31 04:51:18 -04:00
if ( TaskResult = = EStateTreeRunStatus : : Failed )
{
2022-09-27 10:55:54 -04:00
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 ;
}
2022-09-07 17:12:18 -04:00
bool FStateTreeExecutionContext : : TestAllConditions ( FStateTreeInstanceData & SharedInstanceData , const int32 ConditionsOffset , const int32 ConditionsNum )
2022-05-31 04:51:18 -04:00
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_TestConditions ) ;
if ( ConditionsNum = = 0 )
{
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 + + )
{
2022-09-23 20:02:42 -04:00
const FStateTreeConditionBase & Cond = StateTree . Nodes [ ConditionsOffset + Index ] . Get < FStateTreeConditionBase > ( ) ;
2022-05-31 04:51:18 -04:00
if ( Cond . bInstanceIsObject )
{
DataViews [ Cond . DataViewIndex . Get ( ) ] = SharedInstanceData . GetMutableObject ( Cond . InstanceIndex . Get ( ) ) ;
}
else
{
DataViews [ Cond . DataViewIndex . Get ( ) ] = SharedInstanceData . GetMutableStruct ( Cond . InstanceIndex . Get ( ) ) ;
}
// Copy bound properties.
if ( Cond . BindingsBatch . IsValid ( ) )
{
2022-09-23 20:02:42 -04:00
if ( ! StateTree . PropertyBindings . CopyTo ( DataViews , Cond . BindingsBatch , DataViews [ Cond . DataViewIndex . Get ( ) ] ) )
2022-05-31 04:51:18 -04:00
{
// If the source data cannot be accessed, the whole expression evaluates to false.
Values [ 0 ] = false ;
break ;
}
}
const bool bValue = Cond . TestCondition ( * this ) ;
2022-11-30 07:17:26 -05:00
// Reset copied properties that might contain object references.
if ( Cond . BindingsBatch . IsValid ( ) )
{
StateTree . PropertyBindings . ResetObjects ( Cond . BindingsBatch , DataViews [ Cond . DataViewIndex . Get ( ) ] ) ;
}
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 ;
}
2022-12-02 07:57:31 -05:00
bool FStateTreeExecutionContext : : SelectTransition ( FStateTreeInstanceData & SharedInstanceData , FStateTreeExecutionState & Exec ,
const int16 StateIndex , const FCompactStateTreeState & State , const FCompactStateTransition & Transition , FStateTreeTransitionResult & OutTransition )
{
if ( Transition . Type = = EStateTreeTransitionType : : GotoState | | Transition . Type = = EStateTreeTransitionType : : NextState )
{
OutTransition . TargetState = Transition . State ;
OutTransition . NextActiveStates . Reset ( ) ;
if ( SelectState ( SharedInstanceData , Transition . State , OutTransition . NextActiveStates ) )
{
STATETREE_LOG ( Verbose , TEXT ( " Transition on state '%s' (%s) -[%s]-> state '%s' " ) ,
* GetSafeStateName ( Exec . ActiveStates . Last ( ) ) , * State . Name . ToString ( ) , * GetSafeStateName ( Transition . State ) , * GetSafeStateName ( OutTransition . NextActiveStates . Last ( ) ) ) ;
return true ;
}
return false ;
}
if ( Transition . Type = = EStateTreeTransitionType : : Succeeded | | Transition . Type = = EStateTreeTransitionType : : Failed )
{
// Tree succeeded or failed.
// If we are on a linked state, find the first parent linked state and mark that as completed, continue to find completion transitions.
const FStateTreeStateHandle ParentLinkedState = GetParentLinkedStateHandle ( Exec . ActiveStates , StateIndex ) ;
if ( ParentLinkedState . IsValid ( ) )
{
STATETREE_LOG ( Verbose , TEXT ( " Compled subtree '%s' from state '%s' (%s): %s " ) ,
* GetSafeStateName ( ParentLinkedState ) , * GetSafeStateName ( Exec . ActiveStates . Last ( ) ) , * State . Name . ToString ( ) , * UEnum : : GetDisplayValueAsText ( Transition . Type ) . ToString ( ) ) ;
Exec . CompletedStateHandle = ParentLinkedState ;
Exec . LastTickStatus = Transition . Type = = EStateTreeTransitionType : : Succeeded ? EStateTreeRunStatus : : Succeeded : EStateTreeRunStatus : : Failed ;
return TriggerTransitions ( SharedInstanceData , OutTransition ) ;
}
STATETREE_LOG ( Verbose , TEXT ( " Stop tree execution from state '%s' (%s): %s " ) ,
* GetSafeStateName ( Exec . ActiveStates . Last ( ) ) , * State . Name . ToString ( ) , * UEnum : : GetDisplayValueAsText ( Transition . Type ) . ToString ( ) ) ;
const FStateTreeStateHandle NextState = Transition . Type = = EStateTreeTransitionType : : Succeeded ? FStateTreeStateHandle : : Succeeded : FStateTreeStateHandle : : Failed ;
OutTransition . TargetState = NextState ;
OutTransition . NextActiveStates = FStateTreeActiveStates ( NextState ) ;
return true ;
}
return false ;
}
2022-09-23 20:02:42 -04:00
bool FStateTreeExecutionContext : : TriggerTransitions ( FStateTreeInstanceData & SharedInstanceData , FStateTreeTransitionResult & OutTransition )
2022-05-31 04:51:18 -04:00
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_TriggerTransition ) ;
2022-09-23 20:02:42 -04:00
FStateTreeExecutionState & Exec = GetExecState ( ) ;
2022-11-22 08:13:54 -05:00
2022-11-01 15:11:19 -04:00
STATETREE_CLOG ( ! EventsToProcess . IsEmpty ( ) , Verbose , TEXT ( " Trigger transitions with events [%s] " ) , * DebugGetEventsAsString ( ) ) ;
2022-12-02 07:57:31 -05:00
2022-09-01 09:06:53 -04:00
auto HasEvent = [ this ] ( const FGameplayTag QueriedTag )
{
if ( EventsToProcess . IsEmpty ( ) )
{
return false ;
}
return EventsToProcess . ContainsByPredicate ( [ QueriedTag ] ( const FStateTreeEvent & Event )
{
2022-11-23 09:22:14 -05:00
return Event . Tag . MatchesTag ( QueriedTag ) ;
2022-09-01 09:06:53 -04:00
} ) ;
} ;
2022-11-23 09:22:14 -05:00
OutTransition . CurrentActiveStates = Exec . ActiveStates ;
2022-12-02 07:57:31 -05:00
//
// Check tick and event based transitions first.
//
for ( int32 StateIndex = Exec . ActiveStates . Num ( ) - 1 ; StateIndex > = 0 ; StateIndex - - )
2022-05-31 04:51:18 -04:00
{
2022-09-23 20:02:42 -04:00
const FCompactStateTreeState & State = StateTree . States [ Exec . ActiveStates [ StateIndex ] . Index ] ;
2022-05-31 04:51:18 -04:00
for ( uint8 i = 0 ; i < State . TransitionsNum ; i + + )
{
// All transition conditions must pass
const int16 TransitionIndex = State . TransitionsBegin + i ;
2022-09-23 20:02:42 -04:00
const FCompactStateTransition & Transition = StateTree . Transitions [ TransitionIndex ] ;
2022-09-01 09:06:53 -04:00
2022-12-02 07:57:31 -05:00
if ( Transition . Trigger = = EStateTreeTransitionTrigger : : OnTick
| | ( Transition . Trigger = = EStateTreeTransitionTrigger : : OnEvent & & HasEvent ( Transition . EventTag ) ) )
2022-05-31 04:51:18 -04:00
{
2022-12-02 07:57:31 -05:00
// If delay has passed, allow to trigger the transition even if conditions do not pass.
FStateTreeTransitionDelayedState * DelayedState = Transition . HasDelay ( ) ? Exec . FindDelayedTransition ( FStateTreeIndex16 ( TransitionIndex ) ) : nullptr ;
const bool bDelayCompleted = DelayedState ! = nullptr & & DelayedState - > TimeLeft < = 0.0f ;
if ( bDelayCompleted | | TestAllConditions ( SharedInstanceData , Transition . ConditionsBegin , Transition . ConditionsNum ) )
2022-05-31 04:51:18 -04:00
{
2022-12-02 07:57:31 -05:00
// If the transitions is delayed, set up the delay.
if ( Transition . HasDelay ( ) )
2022-05-31 04:51:18 -04:00
{
2022-12-02 07:57:31 -05:00
if ( DelayedState = = nullptr )
{
DelayedState = & Exec . DelayedTransitions . AddDefaulted_GetRef ( ) ;
DelayedState - > TransitionIndex = FStateTreeIndex16 ( TransitionIndex ) ;
DelayedState - > TimeLeft = Transition . Delay . GetRandomDuration ( ) ;
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 ) ;
// Fall through to handle 0 delay.
}
check ( DelayedState ) ;
// We get here if the transitions re-triggers, or delay has completed.
// In case of re-trigger, we will just ignore it during the delay.
if ( DelayedState - > TimeLeft > 0.0f )
{
continue ;
}
STATETREE_LOG ( Verbose , TEXT ( " Passed delayed transition from '%s' (%s) -> '%s' " ) ,
* GetSafeStateName ( Exec . ActiveStates . Last ( ) ) , * State . Name . ToString ( ) , * GetSafeStateName ( Transition . State ) ) ;
// The transition passed the delay, and remove it from the queue, and try trigger it.
Exec . DelayedTransitions . RemoveAllSwap ( [ TransitionIndex ] ( const FStateTreeTransitionDelayedState & DelayedState )
{
return DelayedState . TransitionIndex . Get ( ) = = TransitionIndex ;
} ) ;
2022-05-31 04:51:18 -04:00
}
2022-12-02 07:57:31 -05:00
if ( Transition . Type = = EStateTreeTransitionType : : NotSet )
2022-05-31 04:51:18 -04:00
{
2022-12-02 07:57:31 -05:00
// NotSet is no-operation, but can be used to mask a transition at parent state. Returning unset keeps updating current state.
2022-05-31 04:51:18 -04:00
return false ;
}
2022-12-02 07:57:31 -05:00
if ( SelectTransition ( SharedInstanceData , Exec , StateIndex , State , Transition , OutTransition ) )
2022-05-31 04:51:18 -04:00
{
return true ;
}
}
}
}
}
2022-12-02 07:57:31 -05:00
//
// Check state completion transitions.
//
2022-05-31 04:51:18 -04:00
if ( Exec . LastTickStatus ! = EStateTreeRunStatus : : Running )
{
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 - - )
{
const FCompactStateTreeState & State = StateTree . States [ Exec . ActiveStates [ StateIndex ] . Index ] ;
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 ] ;
if ( EnumHasAnyFlags ( Transition . Trigger , CompletionTrigger ) )
{
if ( TestAllConditions ( SharedInstanceData , Transition . ConditionsBegin , Transition . ConditionsNum ) )
{
// No delay allowed on completion conditions.
if ( SelectTransition ( SharedInstanceData , Exec , StateIndex , State , Transition , OutTransition ) )
{
return true ;
}
}
}
}
}
if ( Exec . LastTickStatus ! = EStateTreeRunStatus : : Running )
{
// Could not trigger completion transition, jump back to start.
static const FStateTreeStateHandle RootState = FStateTreeStateHandle ( 0 ) ;
OutTransition . TargetState = RootState ;
return SelectState ( SharedInstanceData , RootState , OutTransition . NextActiveStates ) ;
}
}
2022-05-31 04:51:18 -04:00
// No transition triggered, keep on updating current state.
return false ;
}
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 ( ) ;
}
2022-09-23 20:02:42 -04:00
bool FStateTreeExecutionContext : : SelectState ( FStateTreeInstanceData & SharedInstanceData , const FStateTreeStateHandle NextState , FStateTreeActiveStates & OutNewActiveState )
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 ( ) )
{
STATETREE_LOG ( Error , TEXT ( " %s: Too many parent states when selecting state '%s' from '%s'. '%s' using StateTree '%s'. " ) ,
2022-09-23 20:02:42 -04:00
ANSI_TO_TCHAR ( __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 )
{
STATETREE_LOG ( Error , TEXT ( " %s: Reached max execution depth when trying to select state %s from '%s'. '%s' using StateTree '%s'. " ) ,
2022-09-23 20:02:42 -04:00
ANSI_TO_TCHAR ( __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
return SelectStateInternal ( SharedInstanceData , NextState , OutNewActiveState ) ;
2022-05-31 04:51:18 -04:00
}
2022-09-23 20:02:42 -04:00
bool FStateTreeExecutionContext : : SelectStateInternal ( FStateTreeInstanceData & SharedInstanceData , const FStateTreeStateHandle NextState , FStateTreeActiveStates & OutNewActiveState )
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.
STATETREE_LOG ( Error , TEXT ( " %s: Trying to select invalid state from '%s'. '%s' using StateTree '%s'. " ) ,
2022-09-23 20:02:42 -04:00
ANSI_TO_TCHAR ( __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
// Check that the state can be entered
2022-09-07 17:12:18 -04:00
if ( TestAllConditions ( SharedInstanceData , State . EnterConditionsBegin , State . EnterConditionsNum ) )
2022-05-31 04:51:18 -04:00
{
if ( ! OutNewActiveState . Push ( NextState ) )
{
STATETREE_LOG ( Error , TEXT ( " %s: Reached max execution depth when trying to select state %s from '%s'. '%s' using StateTree '%s'. " ) ,
2022-09-23 20:02:42 -04:00
ANSI_TO_TCHAR ( __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.
2022-09-23 20:02:42 -04:00
if ( SelectStateInternal ( SharedInstanceData , State . LinkedState , OutNewActiveState ) )
2022-05-31 04:51:18 -04:00
{
// Selection succeeded
return true ;
}
}
else if ( State . HasChildren ( ) )
{
// If the state has children, proceed to select children.
2022-09-23 20:02:42 -04:00
for ( uint16 ChildState = State . ChildrenBegin ; ChildState < State . ChildrenEnd ; ChildState = StateTree . States [ ChildState ] . GetNextSibling ( ) )
2022-05-31 04:51:18 -04:00
{
2022-09-23 20:02:42 -04:00
if ( SelectStateInternal ( SharedInstanceData , FStateTreeStateHandle ( ChildState ) , OutNewActiveState ) )
2022-05-31 04:51:18 -04:00
{
// Selection succeeded
return true ;
}
}
}
else
{
// Select this state.
return true ;
}
OutNewActiveState . Pop ( ) ;
}
// 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
{
2022-09-23 20:02:42 -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
{
2022-09-23 20:02:42 -04:00
const FStateTreeEvaluatorBase & Eval = StateTree . Nodes [ EvalIndex ] . Get < 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 + + )
{
2022-09-23 20:02:42 -04:00
const FStateTreeTaskBase & Task = StateTree . Nodes [ TaskIndex ] . Get < FStateTreeTaskBase > ( ) ;
2022-05-31 04:51:18 -04:00
Task . AppendDebugInfoString ( DebugString , * this ) ;
}
}
}
}
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-09-23 20:02:42 -04:00
const FStructView 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
2022-09-23 20:02:42 -04:00
DebugString + = FString : : Printf ( TEXT ( " \n Transitions(%d) \n [ %-3s | %15s | %-20s | %-20s | %-40s | %-8s ] \n " ) , StateTree . Transitions . Num ( )
2022-09-01 09:06:53 -04:00
, TEXT ( " Idx " ) , TEXT ( " State " ) , TEXT ( " Transition Type " ) , 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
{
2022-09-01 09:06:53 -04:00
DebugString + = FString : : Printf ( TEXT ( " | %3d | %15s | %-20s | %-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 . Type ) . ToString ( ) ,
* 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
{
2022-09-23 20:02:42 -04:00
const FStateTreeEvaluatorBase & Eval = StateTree . Nodes [ EvalIndex ] . Get < 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 + + )
{
2022-09-23 20:02:42 -04:00
const FStateTreeTaskBase & Task = StateTree . Nodes [ TaskIndex ] . Get < 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