2021-09-28 13:33:00 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "StateTreeExecutionContext.h"
# include "StateTree.h"
# include "StateTreeTaskBase.h"
# include "StateTreeEvaluatorBase.h"
# include "StateTreeConditionBase.h"
2022-04-12 15:55:39 -04:00
# include "Containers/StaticArray.h"
2021-09-28 13:33:00 -04:00
# include "VisualLogger/VisualLogger.h"
# include "ProfilingDebugging/CsvProfiler.h"
# include "Logging/LogScopedVerbosityOverride.h"
2022-04-12 15:55:39 -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__)
2021-09-28 13:33:00 -04:00
namespace UE : : StateTree
{
constexpr int32 DebugIndentSize = 2 ;
}
FStateTreeExecutionContext : : FStateTreeExecutionContext ( )
{
}
FStateTreeExecutionContext : : ~ FStateTreeExecutionContext ( )
{
}
bool FStateTreeExecutionContext : : Init ( UObject & InOwner , const UStateTree & InStateTree , const EStateTreeStorage InStorageType )
{
// Set owner first for proper logging (it will be reset in case of failure)
Owner = & InOwner ;
2021-11-24 04:26:29 -05:00
2022-02-24 08:19:23 -05:00
if ( ! InStateTree . IsReadyToRun ( ) )
2021-09-28 13:33:00 -04:00
{
STATETREE_LOG ( Error , TEXT ( " %s: StateTree asset '%s' is not valid. " ) , ANSI_TO_TCHAR ( __FUNCTION__ ) , * InStateTree . GetName ( ) ) ;
Reset ( ) ;
return false ;
}
2021-12-03 15:35:57 -05:00
2021-09-28 13:33:00 -04:00
StateTree = & InStateTree ;
StorageType = InStorageType ;
if ( StorageType = = EStateTreeStorage : : Internal )
{
2021-11-12 05:49:31 -05:00
// Duplicate instance state.
2022-03-23 08:06:00 -04:00
InternalInstanceData . CopyFrom ( InOwner , StateTree - > GetInstanceDataDefaultValue ( ) ) ;
2021-09-28 13:33:00 -04:00
}
2021-11-12 05:49:31 -05:00
// Initialize data views for all possible items.
DataViews . SetNum ( StateTree - > GetNumDataViews ( ) ) ;
2021-09-28 13:33:00 -04:00
2022-04-12 15:55:39 -04:00
// Set data views associated to the parameters using the default values
SetDefaultParameters ( ) ;
2021-09-28 13:33:00 -04:00
// Initialize array to keep track of which states have been updated.
VisitedStates . Init ( false , StateTree - > States . Num ( ) ) ;
2022-02-24 08:19:23 -05:00
2021-09-28 13:33:00 -04:00
return true ;
}
2022-04-12 15:55:39 -04:00
void FStateTreeExecutionContext : : SetDefaultParameters ( )
{
2022-05-06 08:00:26 -04:00
if ( ensureMsgf ( StateTree ! = nullptr , TEXT ( " Execution context must be initialized before calling %s " ) , ANSI_TO_TCHAR ( __FUNCTION__ ) )
& & DataViews . IsValidIndex ( StateTree - > DefaultParametersDataViewIndex ) )
2022-04-12 15:55:39 -04:00
{
2022-05-05 12:02:47 -04:00
DataViews [ StateTree - > DefaultParametersDataViewIndex ] = FStateTreeDataView ( StateTree - > GetDefaultParameters ( ) . GetMutableValue ( ) ) ;
2022-04-12 15:55:39 -04:00
}
}
2022-05-05 12:02:47 -04:00
void FStateTreeExecutionContext : : SetParameters ( const FInstancedPropertyBag & Parameters )
2022-04-12 15:55:39 -04:00
{
2022-05-05 12:02:47 -04:00
if ( ensureMsgf ( StateTree ! = nullptr , TEXT ( " Execution context must be initialized before calling %s " ) , ANSI_TO_TCHAR ( __FUNCTION__ ) )
& & ensureMsgf ( StateTree - > GetDefaultParameters ( ) . GetPropertyBagStruct ( ) = = Parameters . GetPropertyBagStruct ( ) ,
2022-05-06 08:00:26 -04:00
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 - > DefaultParametersDataViewIndex ) )
2022-04-12 15:55:39 -04:00
{
2022-05-05 12:02:47 -04:00
DataViews [ StateTree - > DefaultParametersDataViewIndex ] = FStateTreeDataView ( Parameters . GetMutableValue ( ) ) ;
2022-04-12 15:55:39 -04:00
}
}
2022-02-24 08:19:23 -05:00
void FStateTreeExecutionContext : : Reset ( )
2021-11-24 04:26:29 -05:00
{
2022-02-24 08:19:23 -05:00
InternalInstanceData . Reset ( ) ;
DataViews . Reset ( ) ;
StorageType = EStateTreeStorage : : Internal ;
VisitedStates . Reset ( ) ;
StateTree = nullptr ;
Owner = nullptr ;
}
2021-11-24 04:26:29 -05:00
2022-04-28 03:54:07 -04:00
void FStateTreeExecutionContext : : UpdateLinkedStateParameters ( FStateTreeInstanceData & InstanceData , const FCompactStateTreeState & State )
{
2022-05-05 08:44:57 -04:00
check ( State . ParameterInstanceIndex ! = MAX_uint16 ) ;
2022-04-28 03:54:07 -04:00
const FStateTreeDataView StateParamsInstance = GetInstanceData ( InstanceData , false , State . ParameterInstanceIndex ) ;
const FCompactStateTreeParameters & StateParams = StateParamsInstance . GetMutable < FCompactStateTreeParameters > ( ) ;
// Parameters property bag
const FStateTreeDataView ParametersView ( StateParams . Parameters . GetMutableValue ( ) ) ;
if ( StateParams . BindingsBatch . IsValid ( ) )
{
StateTree - > PropertyBindings . CopyTo ( DataViews , StateParams . BindingsBatch . Index , ParametersView ) ;
}
// Set the parameters as the input parameters for the linked state.
check ( State . LinkedState . IsValid ( ) ) ;
const FCompactStateTreeState & LinkedState = StateTree - > States [ State . LinkedState . Index ] ;
2022-05-05 08:44:57 -04:00
check ( LinkedState . ParameterDataViewIndex ! = MAX_uint16 ) ;
2022-04-28 03:54:07 -04:00
DataViews [ LinkedState . ParameterDataViewIndex ] = ParametersView ;
}
void FStateTreeExecutionContext : : UpdateSubtreeStateParameters ( FStateTreeInstanceData & InstanceData , const FCompactStateTreeState & State )
{
2022-05-05 08:44:57 -04:00
check ( State . ParameterDataViewIndex ! = MAX_uint16 ) ;
check ( State . ParameterInstanceIndex ! = MAX_uint16 ) ;
2022-04-28 03:54:07 -04:00
// 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 ] . IsValid ( ) )
{
return ;
}
// Set view to default parameters.
const FStateTreeDataView ParamInstanceView = GetInstanceData ( InstanceData , false , State . ParameterInstanceIndex ) ;
const FCompactStateTreeParameters & Params = ParamInstanceView . GetMutable < FCompactStateTreeParameters > ( ) ;
DataViews [ State . ParameterDataViewIndex ] = FStateTreeDataView ( Params . Parameters . GetMutableValue ( ) ) ;
}
2022-02-24 08:19:23 -05:00
EStateTreeRunStatus FStateTreeExecutionContext : : Start ( FStateTreeInstanceData * ExternalInstanceData )
2021-09-28 13:33:00 -04:00
{
2022-02-24 08:19:23 -05:00
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_Start ) ;
2021-09-28 13:33:00 -04:00
if ( ! Owner | | ! StateTree )
{
2021-11-15 03:46:57 -05:00
return EStateTreeRunStatus : : Failed ;
2021-09-28 13:33:00 -04:00
}
2021-11-15 03:46:57 -05:00
2022-02-24 08:19:23 -05:00
FStateTreeInstanceData & InstanceData = SelectMutableInstanceData ( ExternalInstanceData ) ;
2022-03-23 08:06:00 -04:00
checkSlow ( InstanceData . GetLayout ( ) = = StateTree - > GetInstanceDataDefaultValue ( ) . GetLayout ( ) ) ;
2022-02-24 08:19:23 -05:00
FStateTreeExecutionState & Exec = GetExecState ( InstanceData ) ;
2021-09-28 13:33:00 -04:00
2021-11-15 03:46:57 -05:00
// Stop if still running previous state.
2021-09-28 13:33:00 -04:00
if ( Exec . TreeRunStatus = = EStateTreeRunStatus : : Running )
{
2022-04-05 03:20:57 -04:00
FStateTreeTransitionResult Transition ;
Transition . TargetState = FStateTreeHandle : : Succeeded ;
Transition . CurrentActiveStates = Exec . ActiveStates ;
Transition . CurrentRunStatus = Exec . LastTickStatus ;
Transition . NextActiveStates = FStateTreeActiveStates ( FStateTreeHandle : : Succeeded ) ;
2022-02-24 08:19:23 -05:00
ExitState ( InstanceData , Transition ) ;
2021-09-28 13:33:00 -04:00
}
2021-11-15 03:46:57 -05:00
// Initialize to unset running state.
2021-09-28 13:33:00 -04:00
Exec . TreeRunStatus = EStateTreeRunStatus : : Running ;
2022-04-05 03:20:57 -04:00
Exec . ActiveStates . Reset ( ) ;
2021-09-28 13:33:00 -04:00
Exec . LastTickStatus = EStateTreeRunStatus : : Unset ;
2021-11-15 03:46:57 -05:00
// Select new Starting from root.
VisitedStates . Init ( false , StateTree - > States . Num ( ) ) ;
2022-04-05 03:20:57 -04:00
2021-11-15 03:46:57 -05:00
static const FStateTreeHandle RootState = FStateTreeHandle ( 0 ) ;
2022-04-05 03:20:57 -04:00
FStateTreeActiveStates NextActiveStates ;
if ( SelectState ( InstanceData , RootState , NextActiveStates ) )
2021-11-15 03:46:57 -05:00
{
2022-04-05 03:20:57 -04:00
if ( NextActiveStates . Last ( ) = = FStateTreeHandle : : Succeeded | | NextActiveStates . Last ( ) = = FStateTreeHandle : : Failed )
2021-11-15 03:46:57 -05:00
{
// 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-04-05 03:20:57 -04:00
ANSI_TO_TCHAR ( __FUNCTION__ ) , NextActiveStates . Last ( ) = = FStateTreeHandle : : Succeeded ? TEXT ( " succeeded " ) : TEXT ( " failed " ) , * GetNameSafe ( Owner ) , * GetNameSafe ( StateTree ) ) ;
Exec . TreeRunStatus = NextActiveStates . Last ( ) = = FStateTreeHandle : : Succeeded ? EStateTreeRunStatus : : Succeeded : EStateTreeRunStatus : : Failed ;
2021-11-15 03:46:57 -05:00
}
else
{
// Enter state tasks can fail/succeed, treat it same as tick.
2022-04-05 03:20:57 -04:00
FStateTreeTransitionResult Transition ;
Transition . TargetState = RootState ;
Transition . CurrentActiveStates = Exec . ActiveStates ;
Transition . CurrentRunStatus = Exec . LastTickStatus ;
Transition . NextActiveStates = NextActiveStates ; // Enter state will update Exec.ActiveStates.
2022-02-24 08:19:23 -05:00
Exec . LastTickStatus = EnterState ( InstanceData , Transition ) ;
2021-12-09 05:26:00 -05:00
2021-11-15 03:46:57 -05:00
// Report state completed immediately.
if ( Exec . LastTickStatus ! = EStateTreeRunStatus : : Running )
{
2022-04-05 03:20:57 -04:00
StateCompleted ( InstanceData ) ;
2021-11-15 03:46:57 -05:00
}
}
}
2022-04-05 03:20:57 -04:00
if ( Exec . ActiveStates . IsEmpty ( ) )
2021-11-15 03:46:57 -05:00
{
// 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. " ) ,
ANSI_TO_TCHAR ( __FUNCTION__ ) , * GetNameSafe ( Owner ) , * GetNameSafe ( StateTree ) ) ;
Exec . TreeRunStatus = EStateTreeRunStatus : : Failed ;
}
return Exec . TreeRunStatus ;
2021-09-28 13:33:00 -04:00
}
2022-02-24 08:19:23 -05:00
EStateTreeRunStatus FStateTreeExecutionContext : : Stop ( FStateTreeInstanceData * ExternalInstanceData )
2021-09-28 13:33:00 -04:00
{
2022-02-24 08:19:23 -05:00
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_Stop ) ;
2021-09-28 13:33:00 -04:00
if ( ! Owner | | ! StateTree )
{
2021-11-15 03:46:57 -05:00
return EStateTreeRunStatus : : Failed ;
2021-09-28 13:33:00 -04:00
}
2021-11-15 03:46:57 -05:00
2022-02-24 08:19:23 -05:00
FStateTreeInstanceData & InstanceData = SelectMutableInstanceData ( ExternalInstanceData ) ;
2022-03-23 08:06:00 -04:00
checkSlow ( InstanceData . GetLayout ( ) = = StateTree - > GetInstanceDataDefaultValue ( ) . GetLayout ( ) ) ;
2022-02-24 08:19:23 -05:00
FStateTreeExecutionState & Exec = GetExecState ( InstanceData ) ;
2021-09-28 13:33:00 -04:00
2021-12-09 05:26:00 -05:00
// Exit states if still in some valid state.
2022-04-05 03:20:57 -04:00
if ( ! Exec . ActiveStates . IsEmpty ( ) & & ( Exec . ActiveStates . Last ( ) ! = FStateTreeHandle : : Succeeded | | Exec . ActiveStates . Last ( ) ! = FStateTreeHandle : : Failed ) )
2021-09-28 13:33:00 -04:00
{
2021-12-09 05:26:00 -05:00
// Transition to Succeeded state.
2022-04-05 03:20:57 -04:00
FStateTreeTransitionResult Transition ;
Transition . TargetState = FStateTreeHandle : : Succeeded ;
Transition . CurrentActiveStates = Exec . ActiveStates ;
Transition . CurrentRunStatus = Exec . LastTickStatus ;
Transition . NextActiveStates = FStateTreeActiveStates ( FStateTreeHandle : : Succeeded ) ;
2022-02-24 08:19:23 -05:00
ExitState ( InstanceData , Transition ) ;
2022-04-05 03:20:57 -04:00
Exec . TreeRunStatus = EStateTreeRunStatus : : Succeeded ;
}
else
{
Exec . TreeRunStatus = Exec . ActiveStates . Last ( ) = = FStateTreeHandle : : Succeeded ? EStateTreeRunStatus : : Succeeded : EStateTreeRunStatus : : Failed ;
2021-09-28 13:33:00 -04:00
}
2021-12-09 05:26:00 -05:00
2022-04-05 03:20:57 -04:00
Exec . ActiveStates . Reset ( ) ;
2021-09-28 13:33:00 -04:00
Exec . LastTickStatus = EStateTreeRunStatus : : Unset ;
2021-11-15 03:46:57 -05:00
return Exec . TreeRunStatus ;
2021-09-28 13:33:00 -04:00
}
2022-02-24 08:19:23 -05:00
EStateTreeRunStatus FStateTreeExecutionContext : : Tick ( const float DeltaTime , FStateTreeInstanceData * ExternalInstanceData )
2021-09-28 13:33:00 -04:00
{
2022-02-24 08:19:23 -05:00
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_Tick ) ;
2021-09-28 13:33:00 -04:00
if ( ! Owner | | ! StateTree )
{
return EStateTreeRunStatus : : Failed ;
}
2022-02-24 08:19:23 -05:00
FStateTreeInstanceData & InstanceData = SelectMutableInstanceData ( ExternalInstanceData ) ;
2022-03-23 08:06:00 -04:00
checkSlow ( InstanceData . GetLayout ( ) = = StateTree - > GetInstanceDataDefaultValue ( ) . GetLayout ( ) ) ;
2022-02-24 08:19:23 -05:00
FStateTreeExecutionState & Exec = GetExecState ( InstanceData ) ;
2021-09-28 13:33:00 -04:00
// No ticking of the tree is done or stopped.
if ( Exec . TreeRunStatus ! = EStateTreeRunStatus : : Running )
{
return Exec . TreeRunStatus ;
}
// Update the gated transition time.
if ( Exec . GatedTransitionIndex ! = INDEX_NONE )
{
Exec . GatedTransitionTime - = DeltaTime ;
}
2021-11-15 03:46:57 -05:00
// Reset visited state, used to keep track which state's evaluators have been ticked.
VisitedStates . Init ( false , StateTree - > States . Num ( ) ) ;
if ( Exec . LastTickStatus = = EStateTreeRunStatus : : Running )
{
// Tick evaluators on active states. Further evaluator may be ticked during selection as non-active states are visited.
// Ticked states are kept track of and evaluators are ticked just once per frame.
2022-04-05 03:20:57 -04:00
TickEvaluators ( InstanceData , Exec . ActiveStates , EStateTreeEvaluationType : : Tick , DeltaTime ) ;
2021-11-15 03:46:57 -05:00
// Tick tasks on active states.
2022-04-05 03:20:57 -04:00
Exec . LastTickStatus = TickTasks ( InstanceData , Exec , DeltaTime ) ;
2021-11-15 03:46:57 -05:00
// Report state completed immediately.
if ( Exec . LastTickStatus ! = EStateTreeRunStatus : : Running )
{
2022-04-05 03:20:57 -04:00
StateCompleted ( InstanceData ) ;
2021-11-15 03:46:57 -05:00
}
}
2021-09-28 13:33:00 -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.
2022-04-12 15:55:39 -04:00
static constexpr int32 MaxIterations = 5 ;
2021-09-28 13:33:00 -04:00
for ( int32 Iter = 0 ; Iter < MaxIterations ; Iter + + )
{
2021-11-15 03:46:57 -05:00
// Trigger conditional transitions or state succeed/failed transitions. First tick transition is handled here too.
2022-04-05 03:20:57 -04:00
FStateTreeTransitionResult Transition ;
if ( TriggerTransitions ( InstanceData , Transition ) )
2021-09-28 13:33:00 -04:00
{
2022-02-24 08:19:23 -05:00
ExitState ( InstanceData , Transition ) ;
2021-09-28 13:33:00 -04:00
2022-04-05 03:20:57 -04:00
if ( Transition . NextActiveStates . Last ( ) = = FStateTreeHandle : : Succeeded | | Transition . NextActiveStates . Last ( ) = = FStateTreeHandle : : Failed )
2021-09-28 13:33:00 -04:00
{
// Transition to a terminal state (succeeded/failed), or default transition failed.
2022-04-05 03:20:57 -04:00
Exec . TreeRunStatus = Transition . NextActiveStates . Last ( ) = = FStateTreeHandle : : Succeeded ? EStateTreeRunStatus : : Succeeded : EStateTreeRunStatus : : Failed ;
2021-09-28 13:33:00 -04:00
return Exec . TreeRunStatus ;
}
// Enter state tasks can fail/succeed, treat it same as tick.
2022-02-24 08:19:23 -05:00
Exec . LastTickStatus = EnterState ( InstanceData , Transition ) ;
2021-12-09 05:26:00 -05:00
2021-09-28 13:33:00 -04:00
// Report state completed immediately.
if ( Exec . LastTickStatus ! = EStateTreeRunStatus : : Running )
{
2022-04-05 03:20:57 -04:00
StateCompleted ( InstanceData ) ;
2021-09-28 13:33:00 -04:00
}
}
// Stop as soon as have found a running state.
if ( Exec . LastTickStatus = = EStateTreeRunStatus : : Running )
{
break ;
}
2021-12-09 05:26:00 -05:00
// Reset visited states to allow evaluators to update during the next transition selection.
VisitedStates . Init ( false , StateTree - > States . Num ( ) ) ;
2021-09-28 13:33:00 -04:00
}
2022-04-05 03:20:57 -04:00
if ( Exec . ActiveStates . IsEmpty ( ) )
2021-09-28 13:33:00 -04:00
{
2021-11-15 03:46:57 -05:00
// 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. " ) ,
ANSI_TO_TCHAR ( __FUNCTION__ ) , * GetNameSafe ( Owner ) , * GetNameSafe ( StateTree ) ) ;
2021-09-28 13:33:00 -04:00
Exec . TreeRunStatus = EStateTreeRunStatus : : Failed ;
return Exec . TreeRunStatus ;
}
return Exec . TreeRunStatus ;
}
2022-02-24 08:19:23 -05:00
EStateTreeRunStatus FStateTreeExecutionContext : : EnterState ( FStateTreeInstanceData & InstanceData , const FStateTreeTransitionResult & Transition )
2021-09-28 13:33:00 -04:00
{
2022-02-24 08:19:23 -05:00
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_EnterState ) ;
2021-09-28 13:33:00 -04:00
2022-04-05 03:20:57 -04:00
if ( Transition . NextActiveStates . IsEmpty ( ) )
2021-09-28 13:33:00 -04:00
{
return EStateTreeRunStatus : : Failed ;
}
2022-02-24 08:19:23 -05:00
FStateTreeExecutionState & Exec = GetExecState ( InstanceData ) ;
2022-04-05 03:20:57 -04:00
Exec . StateChangeCount + + ;
2021-09-28 13:33:00 -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 ;
2022-04-05 03:20:57 -04:00
Exec . EnterStateFailedTaskIndex = MAX_uint16 ; // This will make all tasks to be accepted.
Exec . ActiveStates . Reset ( ) ;
2021-12-09 05:26:00 -05:00
2022-05-05 08:44:57 -04:00
// Do property copy on all states, propagating the results from last tick.
2022-04-05 03:20:57 -04:00
for ( int32 Index = 0 ; Index < Transition . NextActiveStates . Num ( ) & & Result ! = EStateTreeRunStatus : : Failed ; Index + + )
2021-09-28 13:33:00 -04:00
{
2022-04-05 03:20:57 -04:00
const FStateTreeHandle CurrentHandle = Transition . NextActiveStates [ Index ] ;
const FStateTreeHandle PreviousHandle = Transition . CurrentActiveStates . GetStateSafe ( Index ) ;
2022-04-05 09:44:28 -04:00
const FCompactStateTreeState & State = StateTree - > States [ CurrentHandle . Index ] ;
2022-04-05 03:20:57 -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'. " ) ,
ANSI_TO_TCHAR ( __FUNCTION__ ) , * GetStateStatusString ( Exec ) , * GetNameSafe ( Owner ) , * GetNameSafe ( StateTree ) ) ;
break ;
}
2022-04-28 03:54:07 -04:00
if ( State . Type = = EStateTreeStateType : : Linked )
{
UpdateLinkedStateParameters ( InstanceData , State ) ;
}
else if ( State . Type = = EStateTreeStateType : : Subtree )
{
UpdateSubtreeStateParameters ( InstanceData , State ) ;
}
2022-04-05 03:20:57 -04:00
bOnTargetBranch = bOnTargetBranch | | CurrentHandle = = Transition . TargetState ;
const bool bWasActive = PreviousHandle = = CurrentHandle ;
2022-05-05 08:44:57 -04:00
const bool bIsEnteringState = ! bWasActive | | bOnTargetBranch ;
2021-09-28 13:33:00 -04:00
2022-04-05 03:20:57 -04:00
CurrentTransition . CurrentState = CurrentHandle ;
2021-09-28 13:33:00 -04:00
const EStateTreeStateChangeType ChangeType = bWasActive ? EStateTreeStateChangeType : : Sustained : EStateTreeStateChangeType : : Changed ;
2022-05-05 08:44:57 -04:00
STATETREE_CLOG ( bIsEnteringState , Log , TEXT ( " %*sEnter state '%s' %s " ) , Index * UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * DebugGetStatePath ( Transition . NextActiveStates , Index ) , * UEnum : : GetValueAsString ( ChangeType ) ) ;
2021-09-28 13:33:00 -04:00
2021-12-09 05:26:00 -05:00
for ( int32 EvalIndex = State . EvaluatorsBegin ; EvalIndex < ( int32 ) ( State . EvaluatorsBegin + State . EvaluatorsNum ) ; EvalIndex + + )
2021-09-28 13:33:00 -04:00
{
2022-02-24 08:19:23 -05:00
const FStateTreeEvaluatorBase & Eval = GetNode < FStateTreeEvaluatorBase > ( EvalIndex ) ;
const FStateTreeDataView EvalInstanceView = GetInstanceData ( InstanceData , Eval . bInstanceIsObject , Eval . InstanceIndex ) ;
2021-11-12 05:49:31 -05:00
DataViews [ Eval . DataViewIndex ] = EvalInstanceView ;
2021-09-28 13:33:00 -04:00
// Copy bound properties.
if ( Eval . BindingsBatch . IsValid ( ) )
{
2021-11-12 05:49:31 -05:00
StateTree - > PropertyBindings . CopyTo ( DataViews , Eval . BindingsBatch . Index , EvalInstanceView ) ;
2021-09-28 13:33:00 -04:00
}
2022-05-05 08:44:57 -04:00
if ( bIsEnteringState )
2022-02-24 08:19:23 -05:00
{
2022-05-05 08:44:57 -04:00
STATETREE_LOG ( Verbose , TEXT ( " %*s Notify Evaluator '%s'. " ) , Index * UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * Eval . Name . ToString ( ) ) ;
2022-02-24 08:19:23 -05:00
QUICK_SCOPE_CYCLE_COUNTER ( StateTree_Eval_EnterState ) ;
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_Eval_EnterState ) ;
Eval . EnterState ( * this , ChangeType , CurrentTransition ) ;
}
2021-09-28 13:33:00 -04:00
}
// Activate tasks on current state.
2021-12-09 05:26:00 -05:00
for ( int32 TaskIndex = State . TasksBegin ; TaskIndex < ( int32 ) ( State . TasksBegin + State . TasksNum ) ; TaskIndex + + )
2021-09-28 13:33:00 -04:00
{
2022-02-24 08:19:23 -05:00
const FStateTreeTaskBase & Task = GetNode < FStateTreeTaskBase > ( TaskIndex ) ;
const FStateTreeDataView TaskInstanceView = GetInstanceData ( InstanceData , Task . bInstanceIsObject , Task . InstanceIndex ) ;
2021-11-12 05:49:31 -05:00
DataViews [ Task . DataViewIndex ] = TaskInstanceView ;
2021-09-28 13:33:00 -04:00
// Copy bound properties.
if ( Task . BindingsBatch . IsValid ( ) )
{
2021-11-12 05:49:31 -05:00
StateTree - > PropertyBindings . CopyTo ( DataViews , Task . BindingsBatch . Index , TaskInstanceView ) ;
2021-09-28 13:33:00 -04:00
}
2022-02-24 08:19:23 -05:00
2022-05-05 08:44:57 -04:00
if ( bIsEnteringState )
2021-09-28 13:33:00 -04:00
{
2022-05-05 08:44:57 -04:00
STATETREE_LOG ( Verbose , TEXT ( " %*s Notify Task '%s' " ) , Index * UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * Task . Name . ToString ( ) ) ;
2022-02-24 08:19:23 -05:00
QUICK_SCOPE_CYCLE_COUNTER ( StateTree_Task_EnterState ) ;
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_Task_EnterState ) ;
const EStateTreeRunStatus Status = Task . EnterState ( * this , ChangeType , CurrentTransition ) ;
if ( Status = = EStateTreeRunStatus : : Failed )
{
// Store how far in the enter state we got. This will be used to match the ExitState() calls.
Exec . EnterStateFailedTaskIndex = TaskIndex ;
Result = Status ;
break ;
}
2021-09-28 13:33:00 -04:00
}
}
}
return Result ;
}
2022-02-24 08:19:23 -05:00
void FStateTreeExecutionContext : : ExitState ( FStateTreeInstanceData & InstanceData , const FStateTreeTransitionResult & Transition )
2021-09-28 13:33:00 -04:00
{
2022-02-24 08:19:23 -05:00
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_ExitState ) ;
2022-04-05 03:20:57 -04:00
if ( Transition . CurrentActiveStates . IsEmpty ( ) )
2021-09-28 13:33:00 -04:00
{
return ;
}
// Reset transition delay
2022-02-24 08:19:23 -05:00
FStateTreeExecutionState & Exec = GetExecState ( InstanceData ) ;
2021-09-28 13:33:00 -04:00
Exec . GatedTransitionIndex = INDEX_NONE ;
Exec . GatedTransitionTime = 0.0f ;
// 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 ;
2022-05-05 08:44:57 -04:00
FStateTreeHandle 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.
2022-04-05 03:20:57 -04:00
for ( int32 Index = 0 ; Index < Transition . CurrentActiveStates . Num ( ) ; Index + + )
2021-09-28 13:33:00 -04:00
{
2022-04-05 03:20:57 -04:00
const FStateTreeHandle CurrentHandle = Transition . CurrentActiveStates [ Index ] ;
const FStateTreeHandle NextHandle = Transition . NextActiveStates . GetStateSafe ( Index ) ;
2022-04-05 09:44:28 -04:00
const FCompactStateTreeState & State = StateTree - > States [ CurrentHandle . Index ] ;
2022-04-05 03:20:57 -04:00
2022-04-28 03:54:07 -04:00
if ( State . Type = = EStateTreeStateType : : Linked )
{
UpdateLinkedStateParameters ( InstanceData , State ) ;
}
else if ( State . Type = = EStateTreeStateType : : Subtree )
{
UpdateSubtreeStateParameters ( InstanceData , State ) ;
}
2022-04-05 03:20:57 -04:00
const bool bRemainsActive = NextHandle = = CurrentHandle ;
bOnTargetBranch = bOnTargetBranch | | NextHandle = = Transition . TargetState ;
2021-09-28 13:33:00 -04:00
const EStateTreeStateChangeType ChangeType = bRemainsActive ? EStateTreeStateChangeType : : Sustained : EStateTreeStateChangeType : : Changed ;
2022-05-05 08:44:57 -04:00
if ( ! bRemainsActive | | bOnTargetBranch )
{
// Should call ExitState() on this state.
check ( NumExitedStates < FStateTreeActiveStates : : MaxStates ) ;
ExitedStates [ NumExitedStates ] = CurrentHandle ;
ExitedStateChangeType [ NumExitedStates ] = ChangeType ;
ExitedStateActiveIndex [ NumExitedStates ] = Index ;
NumExitedStates + + ;
}
2021-09-28 13:33:00 -04:00
2022-05-05 08:44:57 -04:00
// Do property copies, ExitState() is called below.
2021-12-09 05:26:00 -05:00
for ( int32 EvalIndex = State . EvaluatorsBegin ; EvalIndex < ( int32 ) ( State . EvaluatorsBegin + State . EvaluatorsNum ) ; EvalIndex + + )
2021-09-28 13:33:00 -04:00
{
2022-02-24 08:19:23 -05:00
const FStateTreeEvaluatorBase & Eval = GetNode < FStateTreeEvaluatorBase > ( EvalIndex ) ;
const FStateTreeDataView EvalInstanceView = GetInstanceData ( InstanceData , Eval . bInstanceIsObject , Eval . InstanceIndex ) ;
2021-11-12 05:49:31 -05:00
DataViews [ Eval . DataViewIndex ] = EvalInstanceView ;
2021-09-28 13:33:00 -04:00
// Copy bound properties.
if ( Eval . BindingsBatch . IsValid ( ) )
{
2021-11-12 05:49:31 -05:00
StateTree - > PropertyBindings . CopyTo ( DataViews , Eval . BindingsBatch . Index , EvalInstanceView ) ;
2021-09-28 13:33:00 -04:00
}
}
2021-12-09 05:26:00 -05:00
for ( int32 TaskIndex = State . TasksBegin ; TaskIndex < ( int32 ) ( State . TasksBegin + State . TasksNum ) ; TaskIndex + + )
2021-09-28 13:33:00 -04:00
{
2022-05-05 08:44:57 -04:00
const FStateTreeTaskBase & Task = GetNode < FStateTreeTaskBase > ( TaskIndex ) ;
const FStateTreeDataView TaskInstanceView = GetInstanceData ( InstanceData , Task . bInstanceIsObject , Task . InstanceIndex ) ;
DataViews [ Task . DataViewIndex ] = TaskInstanceView ;
// Copy bound properties.
if ( Task . BindingsBatch . IsValid ( ) )
{
StateTree - > PropertyBindings . CopyTo ( DataViews , Task . BindingsBatch . Index , TaskInstanceView ) ;
}
}
}
// Call in reverse order.
FStateTreeTransitionResult CurrentTransition = Transition ;
for ( int32 Index = NumExitedStates - 1 ; Index > = 0 ; Index - - )
{
const FStateTreeHandle CurrentHandle = ExitedStates [ Index ] ;
const FCompactStateTreeState & State = StateTree - > States [ CurrentHandle . Index ] ;
const EStateTreeStateChangeType ChangeType = ExitedStateChangeType [ Index ] ;
STATETREE_LOG ( Log , TEXT ( " %*sExit state '%s' %s " ) , Index * UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * DebugGetStatePath ( Transition . CurrentActiveStates , ExitedStateActiveIndex [ Index ] ) , * UEnum : : GetValueAsString ( ChangeType ) ) ;
CurrentTransition . CurrentState = CurrentHandle ;
// Tasks
for ( int32 TaskIndex = ( int32 ) ( State . TasksBegin + State . TasksNum ) - 1 ; TaskIndex > = State . TasksBegin ; TaskIndex - - )
{
// Call task completed only if EnterState() was called.
2021-12-09 05:26:00 -05:00
// The task order in the tree (BF) allows us to use the comparison.
if ( TaskIndex < = ( int32 ) Exec . EnterStateFailedTaskIndex )
2021-09-28 13:33:00 -04:00
{
2022-02-24 08:19:23 -05:00
const FStateTreeTaskBase & Task = GetNode < FStateTreeTaskBase > ( TaskIndex ) ;
2021-12-09 05:26:00 -05:00
STATETREE_LOG ( Verbose , TEXT ( " %*s Notify Task '%s' " ) , Index * UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * Task . Name . ToString ( ) ) ;
2022-02-24 08:19:23 -05:00
{
QUICK_SCOPE_CYCLE_COUNTER ( StateTree_Task_ExitState ) ;
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_Task_ExitState ) ;
Task . ExitState ( * this , ChangeType , CurrentTransition ) ;
}
2021-12-09 05:26:00 -05:00
}
2021-09-28 13:33:00 -04:00
}
2022-05-05 08:44:57 -04:00
// Evaluators
for ( int32 EvalIndex = ( int32 ) ( State . EvaluatorsBegin + State . EvaluatorsNum ) - 1 ; EvalIndex > = State . EvaluatorsBegin ; EvalIndex - - )
{
const FStateTreeEvaluatorBase & Eval = GetNode < FStateTreeEvaluatorBase > ( EvalIndex ) ;
STATETREE_LOG ( Verbose , TEXT ( " %*s Notify Evaluator '%s'. " ) , Index * UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * Eval . Name . ToString ( ) ) ;
{
QUICK_SCOPE_CYCLE_COUNTER ( StateTree_Eval_ExitState ) ;
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_Eval_ExitState ) ;
Eval . ExitState ( * this , ChangeType , CurrentTransition ) ;
}
}
2021-09-28 13:33:00 -04:00
}
}
2022-04-05 03:20:57 -04:00
void FStateTreeExecutionContext : : StateCompleted ( FStateTreeInstanceData & InstanceData )
2021-09-28 13:33:00 -04:00
{
2022-02-24 08:19:23 -05:00
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_StateCompleted ) ;
2022-04-05 03:20:57 -04:00
const FStateTreeExecutionState & Exec = GetExecState ( InstanceData ) ;
if ( Exec . ActiveStates . IsEmpty ( ) )
2021-09-28 13:33:00 -04:00
{
return ;
}
// 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.
2022-04-05 03:20:57 -04:00
for ( int32 Index = Exec . ActiveStates . Num ( ) - 1 ; Index > = 0 ; Index - - )
2021-09-28 13:33:00 -04:00
{
2022-04-05 03:20:57 -04:00
const FStateTreeHandle CurrentHandle = Exec . ActiveStates [ Index ] ;
2022-04-05 09:44:28 -04:00
const FCompactStateTreeState & State = StateTree - > States [ CurrentHandle . Index ] ;
2021-09-28 13:33:00 -04:00
2022-04-05 03:20:57 -04:00
STATETREE_LOG ( Verbose , TEXT ( " %*sState Completed '%s' %s " ) , Index * UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * DebugGetStatePath ( Exec . ActiveStates , Index ) , * UEnum : : GetValueAsString ( Exec . LastTickStatus ) ) ;
2021-09-28 13:33:00 -04:00
// Notify Tasks
2021-12-09 05:26:00 -05:00
for ( int32 TaskIndex = ( int32 ) ( State . TasksBegin + State . TasksNum ) - 1 ; TaskIndex > = State . TasksBegin ; TaskIndex - - )
2021-09-28 13:33:00 -04:00
{
2021-12-09 05:26:00 -05:00
// Call task completed only if EnterState() was called.
// The task order in the tree (BF) allows us to use the comparison.
if ( TaskIndex < = ( int32 ) Exec . EnterStateFailedTaskIndex )
{
2022-02-24 08:19:23 -05:00
const FStateTreeTaskBase & Task = GetNode < FStateTreeTaskBase > ( TaskIndex ) ;
2021-09-28 13:33:00 -04:00
2021-12-09 05:26:00 -05:00
STATETREE_LOG ( Verbose , TEXT ( " %*s Notify Task '%s' " ) , Index * UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * Task . Name . ToString ( ) ) ;
2022-04-05 03:20:57 -04:00
Task . StateCompleted ( * this , Exec . LastTickStatus , Exec . ActiveStates ) ;
2021-12-09 05:26:00 -05:00
}
2021-09-28 13:33:00 -04:00
}
// Notify evaluators
2021-12-09 05:26:00 -05:00
for ( int32 EvalIndex = ( int32 ) ( State . EvaluatorsBegin + State . EvaluatorsNum ) - 1 ; EvalIndex > = State . EvaluatorsBegin ; EvalIndex - - )
2021-09-28 13:33:00 -04:00
{
2022-02-24 08:19:23 -05:00
const FStateTreeEvaluatorBase & Eval = GetNode < FStateTreeEvaluatorBase > ( EvalIndex ) ;
2021-09-28 13:33:00 -04:00
STATETREE_LOG ( Verbose , TEXT ( " %*s Notify Evaluator '%s' " ) , Index * UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * Eval . Name . ToString ( ) ) ;
2022-04-05 03:20:57 -04:00
Eval . StateCompleted ( * this , Exec . LastTickStatus , Exec . ActiveStates ) ;
2021-09-28 13:33:00 -04:00
}
}
}
2022-04-05 03:20:57 -04:00
void FStateTreeExecutionContext : : TickEvaluatorsForSelect ( FStateTreeInstanceData & InstanceData , const FStateTreeHandle CurrentState , const EStateTreeEvaluationType EvalType , const float DeltaTime )
2021-09-28 13:33:00 -04:00
{
2022-04-05 03:20:57 -04:00
if ( CurrentState = = FStateTreeHandle : : Succeeded | | CurrentState = = FStateTreeHandle : : Failed | | CurrentState = = FStateTreeHandle : : Invalid )
2021-09-28 13:33:00 -04:00
{
return ;
}
2022-04-05 03:20:57 -04:00
FStateTreeActiveStates Branch ;
FStateTreeHandle StateHandle = CurrentState ;
while ( StateHandle . IsValid ( ) )
2021-09-28 13:33:00 -04:00
{
2022-04-05 03:20:57 -04:00
if ( ! Branch . PushFront ( StateHandle ) )
{
break ;
}
StateHandle = StateTree - > States [ StateHandle . Index ] . Parent ;
}
TickEvaluators ( InstanceData , Branch , EvalType , DeltaTime ) ;
}
void FStateTreeExecutionContext : : TickEvaluators ( FStateTreeInstanceData & InstanceData , const FStateTreeActiveStates & ActiveStates , const EStateTreeEvaluationType EvalType , const float DeltaTime )
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_TickEvaluators ) ;
for ( int32 Index = 0 ; Index < ActiveStates . Num ( ) ; Index + + )
{
const FStateTreeHandle StateHandle = ActiveStates [ Index ] ;
if ( VisitedStates [ StateHandle . Index ] )
2021-09-28 13:33:00 -04:00
{
// Already ticked this frame.
continue ;
}
2022-04-05 09:44:28 -04:00
const FCompactStateTreeState & State = StateTree - > States [ StateHandle . Index ] ;
2021-09-28 13:33:00 -04:00
2022-04-28 03:54:07 -04:00
if ( State . Type = = EStateTreeStateType : : Linked )
{
UpdateLinkedStateParameters ( InstanceData , State ) ;
}
else if ( State . Type = = EStateTreeStateType : : Subtree )
{
UpdateSubtreeStateParameters ( InstanceData , State ) ;
}
2022-04-05 03:20:57 -04:00
STATETREE_CLOG ( State . EvaluatorsNum > 0 , Verbose , TEXT ( " %*sTicking Evaluators of state '%s' %s " ) , Index * UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * DebugGetStatePath ( ActiveStates , Index ) , * UEnum : : GetValueAsString ( EvalType ) ) ;
2021-09-28 13:33:00 -04:00
// Tick evaluators
for ( int32 EvalIndex = 0 ; EvalIndex < int32 ( State . EvaluatorsNum ) ; EvalIndex + + )
{
2022-02-24 08:19:23 -05:00
const FStateTreeEvaluatorBase & Eval = GetNode < FStateTreeEvaluatorBase > ( int32 ( State . EvaluatorsBegin ) + EvalIndex ) ;
const FStateTreeDataView EvalInstanceView = GetInstanceData ( InstanceData , Eval . bInstanceIsObject , Eval . InstanceIndex ) ;
2021-11-12 05:49:31 -05:00
DataViews [ Eval . DataViewIndex ] = EvalInstanceView ;
2021-09-28 13:33:00 -04:00
// Copy bound properties.
if ( Eval . BindingsBatch . IsValid ( ) )
{
2021-11-12 05:49:31 -05:00
StateTree - > PropertyBindings . CopyTo ( DataViews , Eval . BindingsBatch . Index , EvalInstanceView ) ;
2021-09-28 13:33:00 -04:00
}
STATETREE_LOG ( Verbose , TEXT ( " %*s Evaluate: '%s' " ) , Index * UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * Eval . Name . ToString ( ) ) ;
2022-02-24 08:19:23 -05:00
{
QUICK_SCOPE_CYCLE_COUNTER ( StateTree_Eval_Tick ) ;
Eval . Evaluate ( * this , EvalType , DeltaTime ) ;
}
2021-09-28 13:33:00 -04:00
}
2022-04-05 03:20:57 -04:00
VisitedStates [ StateHandle . Index ] = true ;
2021-09-28 13:33:00 -04:00
}
}
2022-04-05 03:20:57 -04:00
EStateTreeRunStatus FStateTreeExecutionContext : : TickTasks ( FStateTreeInstanceData & InstanceData , const FStateTreeExecutionState & Exec , const float DeltaTime )
2021-09-28 13:33:00 -04:00
{
2022-02-24 08:19:23 -05:00
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_TickTasks ) ;
2021-09-28 13:33:00 -04:00
2022-04-05 03:20:57 -04:00
if ( Exec . ActiveStates . IsEmpty ( ) )
2021-09-28 13:33:00 -04:00
{
return EStateTreeRunStatus : : Failed ;
}
EStateTreeRunStatus Result = EStateTreeRunStatus : : Running ;
int32 NumTotalTasks = 0 ;
2022-04-05 03:20:57 -04:00
for ( int32 Index = 0 ; Index < Exec . ActiveStates . Num ( ) & & Result ! = EStateTreeRunStatus : : Failed ; Index + + )
2021-09-28 13:33:00 -04:00
{
2022-04-05 03:20:57 -04:00
const FStateTreeHandle CurrentHandle = Exec . ActiveStates [ Index ] ;
2022-04-05 09:44:28 -04:00
const FCompactStateTreeState & State = StateTree - > States [ CurrentHandle . Index ] ;
2021-09-28 13:33:00 -04:00
2022-04-05 03:20:57 -04:00
STATETREE_CLOG ( State . TasksNum > 0 , Verbose , TEXT ( " %*sTicking Tasks of state '%s' " ) , Index * UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * DebugGetStatePath ( Exec . ActiveStates , Index ) ) ;
2021-09-28 13:33:00 -04:00
2022-04-28 03:54:07 -04:00
if ( State . Type = = EStateTreeStateType : : Linked )
{
UpdateLinkedStateParameters ( InstanceData , State ) ;
}
else if ( State . Type = = EStateTreeStateType : : Subtree )
{
UpdateSubtreeStateParameters ( InstanceData , State ) ;
}
2021-09-28 13:33:00 -04:00
// Tick Tasks
for ( int32 TaskIndex = 0 ; TaskIndex < int32 ( State . TasksNum ) ; TaskIndex + + )
{
2022-02-24 08:19:23 -05:00
const FStateTreeTaskBase & Task = GetNode < FStateTreeTaskBase > ( int32 ( State . TasksBegin ) + TaskIndex ) ;
const FStateTreeDataView TaskInstanceView = GetInstanceData ( InstanceData , Task . bInstanceIsObject , Task . InstanceIndex ) ;
2021-11-12 05:49:31 -05:00
DataViews [ Task . DataViewIndex ] = TaskInstanceView ;
2021-09-28 13:33:00 -04:00
// Copy bound properties.
if ( Task . BindingsBatch . IsValid ( ) )
{
2021-11-12 05:49:31 -05:00
StateTree - > PropertyBindings . CopyTo ( DataViews , Task . BindingsBatch . Index , TaskInstanceView ) ;
2021-09-28 13:33:00 -04:00
}
STATETREE_LOG ( Verbose , TEXT ( " %*s Tick: '%s' " ) , Index * UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * Task . Name . ToString ( ) ) ;
2022-02-24 08:19:23 -05:00
{
QUICK_SCOPE_CYCLE_COUNTER ( StateTree_Task_Tick ) ;
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_Task_Tick ) ;
2021-09-28 13:33:00 -04:00
2022-02-24 08:19:23 -05:00
const EStateTreeRunStatus TaskResult = Task . Tick ( * this , DeltaTime ) ;
// TODO: Add more control over which states can control the failed/succeeded result.
if ( TaskResult ! = EStateTreeRunStatus : : Running )
{
Result = TaskResult ;
}
if ( TaskResult = = EStateTreeRunStatus : : Failed )
{
break ;
}
2021-09-28 13:33:00 -04:00
}
}
NumTotalTasks + = State . TasksNum ;
}
if ( NumTotalTasks = = 0 )
{
// No tasks, done ticking.
Result = EStateTreeRunStatus : : Succeeded ;
}
return Result ;
}
2022-02-24 08:19:23 -05:00
bool FStateTreeExecutionContext : : TestAllConditions ( FStateTreeInstanceData & InstanceData , const uint32 ConditionsOffset , const uint32 ConditionsNum )
2021-09-28 13:33:00 -04:00
{
2022-02-24 08:19:23 -05:00
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_TestConditions ) ;
2021-09-28 13:33:00 -04:00
2022-03-28 04:48:50 -04:00
if ( ConditionsNum = = 0 )
2021-09-28 13:33:00 -04:00
{
2022-03-28 04:48:50 -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 ( uint32 Index = 0 ; Index < ConditionsNum ; Index + + )
{
const FStateTreeConditionBase & Cond = GetNode < FStateTreeConditionBase > ( ConditionsOffset + Index ) ;
2022-02-24 08:19:23 -05:00
const FStateTreeDataView CondInstanceView = GetInstanceData ( InstanceData , Cond . bInstanceIsObject , Cond . InstanceIndex ) ;
2021-11-12 05:49:31 -05:00
DataViews [ Cond . DataViewIndex ] = CondInstanceView ;
2021-09-28 13:33:00 -04:00
2021-11-12 05:49:31 -05:00
// Copy bound properties.
if ( Cond . BindingsBatch . IsValid ( ) )
2021-09-28 13:33:00 -04:00
{
2022-04-28 03:54:07 -04:00
if ( ! StateTree - > PropertyBindings . CopyTo ( DataViews , Cond . BindingsBatch . Index , CondInstanceView ) )
{
// If the source data cannot be accessed, the whole expression evaluates to false.
Values [ 0 ] = false ;
break ;
}
2021-09-28 13:33:00 -04:00
}
2022-03-28 04:48:50 -04:00
const bool bValue = Cond . TestCondition ( * this ) ;
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 + + )
2021-09-28 13:33:00 -04:00
{
2022-03-28 04:48:50 -04:00
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 ;
2021-09-28 13:33:00 -04:00
}
}
2022-03-28 04:48:50 -04:00
return Values [ 0 ] ;
2021-09-28 13:33:00 -04:00
}
2022-04-05 03:20:57 -04:00
bool FStateTreeExecutionContext : : TriggerTransitions ( FStateTreeInstanceData & InstanceData , FStateTreeTransitionResult & OutTransition )
2021-09-28 13:33:00 -04:00
{
2022-02-24 08:19:23 -05:00
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_TriggerTransition ) ;
FStateTreeExecutionState & Exec = GetExecState ( InstanceData ) ;
2021-09-28 13:33:00 -04:00
EStateTreeTransitionEvent Event = EStateTreeTransitionEvent : : OnCondition ;
2022-04-05 03:20:57 -04:00
if ( Exec . LastTickStatus = = EStateTreeRunStatus : : Succeeded )
2021-09-28 13:33:00 -04:00
{
Event = EStateTreeTransitionEvent : : OnSucceeded ;
}
2022-04-05 03:20:57 -04:00
else if ( Exec . LastTickStatus = = EStateTreeRunStatus : : Failed )
2021-09-28 13:33:00 -04:00
{
Event = EStateTreeTransitionEvent : : OnFailed ;
}
// Walk towards root and check all transitions along the way.
2022-04-05 03:20:57 -04:00
for ( int32 StateIndex = Exec . ActiveStates . Num ( ) - 1 ; StateIndex > = 0 ; StateIndex - - )
2021-09-28 13:33:00 -04:00
{
2022-04-05 09:44:28 -04:00
const FCompactStateTreeState & State = StateTree - > States [ Exec . ActiveStates [ StateIndex ] . Index ] ;
2022-04-05 03:20:57 -04:00
2021-11-09 12:24:26 -05:00
for ( uint8 i = 0 ; i < State . TransitionsNum ; i + + )
2021-09-28 13:33:00 -04:00
{
// All transition conditions must pass
const int16 TransitionIndex = State . TransitionsBegin + i ;
2022-04-05 09:44:28 -04:00
const FCompactStateTransition & Transition = StateTree - > Transitions [ TransitionIndex ] ;
2022-02-24 08:19:23 -05:00
if ( EnumHasAllFlags ( Transition . Event , Event ) & & TestAllConditions ( InstanceData , Transition . ConditionsBegin , Transition . ConditionsNum ) )
2021-09-28 13:33:00 -04:00
{
// If a transition has delay, we stop testing other transitions, but the transition will not pass the condition until the delay time passes.
if ( Transition . GateDelay > 0 )
{
if ( Exec . GatedTransitionIndex ! = TransitionIndex )
{
Exec . GatedTransitionIndex = TransitionIndex ;
Exec . GatedTransitionTime = FMath : : RandRange ( 0.0f , Transition . GateDelay * 0.1f ) ; // TODO: we need variance too.
BeginGatedTransition ( Exec ) ;
2022-04-05 03:20:57 -04:00
STATETREE_LOG ( Verbose , TEXT ( " Gated transition triggered from '%s' (%s) -> '%s' %.1fs " ) , * GetSafeStateName ( Exec . ActiveStates . Last ( ) ) , * State . Name . ToString ( ) , * GetSafeStateName ( Transition . State ) , Exec . GatedTransitionTime ) ;
2021-09-28 13:33:00 -04:00
}
// Keep on updating current state, until we have tried to trigger
if ( Exec . GatedTransitionTime > 0.0f )
{
2022-04-05 03:20:57 -04:00
return false ;
2021-09-28 13:33:00 -04:00
}
2022-04-05 03:20:57 -04:00
STATETREE_LOG ( Verbose , TEXT ( " Passed gated transition from '%s' (%s) -> '%s' " ) , * GetSafeStateName ( Exec . ActiveStates . Last ( ) ) , * State . Name . ToString ( ) , * GetSafeStateName ( Transition . State ) ) ;
2021-09-28 13:33:00 -04:00
}
if ( Transition . Type = = EStateTreeTransitionType : : GotoState | | Transition . Type = = EStateTreeTransitionType : : NextState )
{
2022-04-05 03:20:57 -04:00
OutTransition . CurrentActiveStates = Exec . ActiveStates ;
OutTransition . TargetState = Transition . State ;
OutTransition . NextActiveStates . Reset ( ) ;
if ( SelectState ( InstanceData , Transition . State , OutTransition . NextActiveStates ) )
2021-09-28 13:33:00 -04:00
{
2022-04-05 03:20:57 -04:00
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 ;
2021-09-28 13:33:00 -04:00
}
}
else if ( Transition . Type = = EStateTreeTransitionType : : NotSet )
{
// NotSet is no-operation, but can be used to mask a transition at parent state. Returning unset keeps updating current state.
2022-04-05 03:20:57 -04:00
return false ;
2021-09-28 13:33:00 -04:00
}
else if ( Transition . Type = = EStateTreeTransitionType : : Succeeded )
{
2022-04-05 03:20:57 -04:00
STATETREE_LOG ( Verbose , TEXT ( " Stop tree execution from state '%s' (%s): Succeeded " ) , * GetSafeStateName ( Exec . ActiveStates . Last ( ) ) , * State . Name . ToString ( ) ) ;
OutTransition . CurrentActiveStates = Exec . ActiveStates ;
OutTransition . TargetState = FStateTreeHandle : : Succeeded ;
OutTransition . NextActiveStates = FStateTreeActiveStates ( FStateTreeHandle : : Succeeded ) ;
return true ;
2021-09-28 13:33:00 -04:00
}
else
{
2022-04-05 03:20:57 -04:00
STATETREE_LOG ( Verbose , TEXT ( " Stop tree execution from state '%s' (%s): Failed " ) , * GetSafeStateName ( Exec . ActiveStates . Last ( ) ) , * State . Name . ToString ( ) ) ;
OutTransition . CurrentActiveStates = Exec . ActiveStates ;
OutTransition . TargetState = FStateTreeHandle : : Failed ;
OutTransition . NextActiveStates = FStateTreeActiveStates ( FStateTreeHandle : : Failed ) ;
return true ;
2021-09-28 13:33:00 -04:00
}
}
else if ( Exec . GatedTransitionIndex = = TransitionIndex )
{
// If the current transition was gated transition, reset it if the condition failed.
Exec . GatedTransitionIndex = INDEX_NONE ;
Exec . GatedTransitionTime = 0.0f ;
}
}
}
2022-04-05 03:20:57 -04:00
if ( Exec . LastTickStatus ! = EStateTreeRunStatus : : Running )
2021-09-28 13:33:00 -04:00
{
2022-04-05 03:20:57 -04:00
// Could not trigger completion transition, jump back to start.
static const FStateTreeHandle RootState = FStateTreeHandle ( 0 ) ;
OutTransition . TargetState = RootState ;
return SelectState ( InstanceData , RootState , OutTransition . NextActiveStates ) ;
2021-09-28 13:33:00 -04:00
}
// No transition triggered, keep on updating current state.
2022-04-05 03:20:57 -04:00
return false ;
2021-09-28 13:33:00 -04:00
}
2022-04-05 03:20:57 -04:00
bool FStateTreeExecutionContext : : SelectState ( FStateTreeInstanceData & InstanceData , const FStateTreeHandle NextState , FStateTreeActiveStates & OutNewActiveState )
{
const FStateTreeExecutionState & Exec = GetExecState ( InstanceData ) ;
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 < FStateTreeHandle , FStateTreeActiveStates : : MaxStates > InBetweenStates ;
int32 NumInBetweenStates = 0 ;
int32 CommonActiveAncestorIndex = INDEX_NONE ;
// Walk towards the root from current state.
FStateTreeHandle 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'. " ) ,
ANSI_TO_TCHAR ( __FUNCTION__ ) , * GetSafeStateName ( NextState ) , * GetStateStatusString ( Exec ) , * GetNameSafe ( Owner ) , * GetNameSafe ( StateTree ) ) ;
return false ;
}
CurrState = StateTree - > States [ CurrState . Index ] . Parent ;
}
// 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'. " ) ,
ANSI_TO_TCHAR ( __FUNCTION__ ) , * GetSafeStateName ( NextState ) , * GetStateStatusString ( Exec ) , * GetNameSafe ( Owner ) , * GetNameSafe ( StateTree ) ) ;
return false ;
}
return SelectStateInternal ( InstanceData , NextState , OutNewActiveState ) ;
}
bool FStateTreeExecutionContext : : SelectStateInternal ( FStateTreeInstanceData & InstanceData , const FStateTreeHandle NextState , FStateTreeActiveStates & OutNewActiveState )
2021-09-28 13:33:00 -04:00
{
2022-02-24 08:19:23 -05:00
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_SelectState ) ;
2022-04-05 03:20:57 -04:00
const FStateTreeExecutionState & Exec = GetExecState ( InstanceData ) ;
2021-09-28 13:33:00 -04:00
if ( ! NextState . IsValid ( ) )
{
// Trying to select non-existing state.
2022-04-05 03:20:57 -04:00
STATETREE_LOG ( Error , TEXT ( " %s: Trying to select invalid state from '%s'. '%s' using StateTree '%s'. " ) ,
ANSI_TO_TCHAR ( __FUNCTION__ ) , * GetStateStatusString ( Exec ) , * GetNameSafe ( Owner ) , * GetNameSafe ( StateTree ) ) ;
return false ;
2021-09-28 13:33:00 -04:00
}
2022-04-05 09:44:28 -04:00
const FCompactStateTreeState & State = StateTree - > States [ NextState . Index ] ;
2021-09-28 13:33:00 -04:00
// Make sure all the evaluators for the target state are up to date.
2022-04-05 03:20:57 -04:00
TickEvaluatorsForSelect ( InstanceData , NextState , EStateTreeEvaluationType : : PreSelect , 0.0f ) ;
2021-09-28 13:33:00 -04:00
// Check that the state can be entered
2022-02-24 08:19:23 -05:00
if ( TestAllConditions ( InstanceData , State . EnterConditionsBegin , State . EnterConditionsNum ) )
2021-09-28 13:33:00 -04:00
{
2022-04-05 03:20:57 -04:00
if ( ! OutNewActiveState . Push ( NextState ) )
2021-09-28 13:33:00 -04:00
{
2022-04-05 03:20:57 -04:00
STATETREE_LOG ( Error , TEXT ( " %s: Reached max execution depth when trying to select state %s from '%s'. '%s' using StateTree '%s'. " ) ,
ANSI_TO_TCHAR ( __FUNCTION__ ) , * GetSafeStateName ( NextState ) , * GetStateStatusString ( Exec ) , * GetNameSafe ( Owner ) , * GetNameSafe ( StateTree ) ) ;
return false ;
}
if ( State . LinkedState . IsValid ( ) )
{
// If State is linked, proceed to the linked state.
if ( SelectStateInternal ( InstanceData , State . LinkedState , OutNewActiveState ) )
{
// Selection succeeded
return true ;
}
}
else if ( State . HasChildren ( ) )
{
// If the state has children, proceed to select children.
2021-09-28 13:33:00 -04:00
for ( uint16 ChildState = State . ChildrenBegin ; ChildState < State . ChildrenEnd ; ChildState = StateTree - > States [ ChildState ] . GetNextSibling ( ) )
{
2022-04-05 03:20:57 -04:00
if ( SelectStateInternal ( InstanceData , FStateTreeHandle ( ChildState ) , OutNewActiveState ) )
2021-09-28 13:33:00 -04:00
{
// Selection succeeded
2022-04-05 03:20:57 -04:00
return true ;
2021-09-28 13:33:00 -04:00
}
}
}
else
{
// Select this state.
2022-04-05 03:20:57 -04:00
return true ;
2021-09-28 13:33:00 -04:00
}
2022-04-05 03:20:57 -04:00
OutNewActiveState . Pop ( ) ;
2021-09-28 13:33:00 -04:00
}
2022-04-05 03:20:57 -04:00
2021-09-28 13:33:00 -04:00
// Nothing got selected.
2022-04-05 03:20:57 -04:00
return false ;
2021-09-28 13:33:00 -04:00
}
FString FStateTreeExecutionContext : : GetSafeStateName ( const FStateTreeHandle State ) const
{
check ( StateTree ) ;
if ( State = = FStateTreeHandle : : Invalid )
{
return TEXT ( " (State Invalid) " ) ;
}
else if ( State = = FStateTreeHandle : : Succeeded )
{
return TEXT ( " (State Succeeded) " ) ;
}
else if ( State = = FStateTreeHandle : : Failed )
{
return TEXT ( " (State Failed) " ) ;
}
else if ( StateTree - > States . IsValidIndex ( State . Index ) )
{
return * StateTree - > States [ State . Index ] . Name . ToString ( ) ;
}
return TEXT ( " (Unknown) " ) ;
}
2022-04-12 15:55:39 -04:00
FString FStateTreeExecutionContext : : DebugGetStatePath ( const FStateTreeActiveStates & ActiveStates , const int32 ActiveStateIndex ) const
2021-09-28 13:33:00 -04:00
{
FString StatePath ;
2022-04-05 03:20:57 -04:00
if ( ! ensureMsgf ( ActiveStates . IsValidIndex ( ActiveStateIndex ) , TEXT ( " Provided index must be valid " ) ) )
2021-09-28 13:33:00 -04:00
{
return StatePath ;
}
for ( int32 i = 0 ; i < = ActiveStateIndex ; i + + )
{
2022-04-05 09:44:28 -04:00
const FCompactStateTreeState & State = StateTree - > States [ ActiveStates [ i ] . Index ] ;
2021-09-28 13:33:00 -04:00
StatePath . Appendf ( TEXT ( " %s%s " ) , i = = 0 ? TEXT ( " " ) : TEXT ( " . " ) , * State . Name . ToString ( ) ) ;
}
return StatePath ;
}
2022-04-05 03:20:57 -04:00
FString FStateTreeExecutionContext : : GetStateStatusString ( const FStateTreeExecutionState & ExecState ) const
2021-09-28 13:33:00 -04:00
{
2022-04-05 03:20:57 -04:00
return GetSafeStateName ( ExecState . ActiveStates . Last ( ) ) + TEXT ( " : " ) + UEnum : : GetDisplayValueAsText ( ExecState . LastTickStatus ) . ToString ( ) ;
2021-09-28 13:33:00 -04:00
}
2022-02-24 08:19:23 -05:00
EStateTreeRunStatus FStateTreeExecutionContext : : GetLastTickStatus ( const FStateTreeInstanceData * ExternalInstanceData ) const
2021-09-28 13:33:00 -04:00
{
2022-02-24 08:19:23 -05:00
const FStateTreeExecutionState & Exec = GetExecState ( SelectInstanceData ( ExternalInstanceData ) ) ;
2021-09-28 13:33:00 -04:00
return Exec . LastTickStatus ;
}
2022-04-04 13:53:17 -04:00
FString FStateTreeExecutionContext : : GetInstanceDescription ( ) const
{
return Owner ! = nullptr ? FString : : Printf ( TEXT ( " %s: " ) , * Owner - > GetName ( ) ) : TEXT ( " " ) ;
}
2022-04-05 03:20:57 -04:00
const FStateTreeActiveStates & FStateTreeExecutionContext : : GetActiveStates ( const FStateTreeInstanceData * ExternalInstanceData ) const
{
const FStateTreeExecutionState & Exec = GetExecState ( SelectInstanceData ( ExternalInstanceData ) ) ;
return Exec . ActiveStates ;
}
2021-09-28 13:33:00 -04:00
# if WITH_GAMEPLAY_DEBUGGER
2022-02-24 08:19:23 -05:00
FString FStateTreeExecutionContext : : GetDebugInfoString ( const FStateTreeInstanceData * ExternalInstanceData ) const
2021-09-28 13:33:00 -04:00
{
if ( ! StateTree )
{
return FString ( TEXT ( " No StateTree asset. " ) ) ;
}
2022-02-24 08:19:23 -05:00
const FStateTreeInstanceData & InstanceData = SelectInstanceData ( ExternalInstanceData ) ;
2022-03-23 08:06:00 -04:00
checkSlow ( InstanceData . GetLayout ( ) = = StateTree - > GetInstanceDataDefaultValue ( ) . GetLayout ( ) ) ;
2022-02-24 08:19:23 -05:00
const FStateTreeExecutionState & Exec = GetExecState ( InstanceData ) ;
2021-09-28 13:33:00 -04:00
FString DebugString = FString : : Printf ( TEXT ( " StateTree (asset: '%s') \n " ) , * GetNameSafe ( StateTree ) ) ;
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 " ) ;
}
// Active States
DebugString + = TEXT ( " Current State: \n " ) ;
2022-04-05 03:20:57 -04:00
for ( int32 Index = 0 ; Index < Exec . ActiveStates . Num ( ) ; Index + + )
2021-09-28 13:33:00 -04:00
{
2022-04-05 03:20:57 -04:00
FStateTreeHandle Handle = Exec . ActiveStates [ Index ] ;
2021-09-28 13:33:00 -04:00
if ( Handle . IsValid ( ) )
{
2022-04-05 09:44:28 -04:00
const FCompactStateTreeState & State = StateTree - > States [ Handle . Index ] ;
2021-09-28 13:33:00 -04:00
DebugString + = FString : : Printf ( TEXT ( " [%s] \n " ) , * State . Name . ToString ( ) ) ;
if ( State . TasksNum > 0 )
{
DebugString + = TEXT ( " \n Tasks: \n " ) ;
for ( int32 j = 0 ; j < int32 ( State . TasksNum ) ; j + + )
{
2022-02-24 08:19:23 -05:00
const FStateTreeTaskBase & Task = GetNode < FStateTreeTaskBase > ( int32 ( State . TasksBegin ) + j ) ;
2021-09-28 13:33:00 -04:00
Task . AppendDebugInfoString ( DebugString , * this ) ;
}
}
if ( State . EvaluatorsNum > 0 )
{
DebugString + = TEXT ( " \n Evaluators: \n " ) ;
for ( int32 EvalIndex = 0 ; EvalIndex < int32 ( State . EvaluatorsNum ) ; EvalIndex + + )
{
2022-02-24 08:19:23 -05:00
const FStateTreeEvaluatorBase & Eval = GetNode < FStateTreeEvaluatorBase > ( int32 ( State . EvaluatorsBegin ) + EvalIndex ) ;
2021-09-28 13:33:00 -04:00
Eval . AppendDebugInfoString ( DebugString , * this ) ;
}
}
}
}
return DebugString ;
}
# endif // WITH_GAMEPLAY_DEBUGGER
# if WITH_STATETREE_DEBUG
2022-02-24 08:19:23 -05:00
void FStateTreeExecutionContext : : DebugPrintInternalLayout ( const FStateTreeInstanceData * ExternalInstanceData )
2021-09-28 13:33:00 -04:00
{
LOG_SCOPE_VERBOSITY_OVERRIDE ( LogStateTree , ELogVerbosity : : Log ) ;
if ( StateTree = = nullptr )
{
UE_LOG ( LogStateTree , Log , TEXT ( " No StateTree asset. " ) ) ;
return ;
}
FString DebugString = FString : : Printf ( TEXT ( " StateTree (asset: '%s') \n " ) , * GetNameSafe ( StateTree ) ) ;
2021-11-12 05:49:31 -05:00
// Tree items (e.g. tasks, evaluators, conditions)
2022-02-24 08:19:23 -05:00
DebugString + = FString : : Printf ( TEXT ( " \n Items(%d) \n " ) , StateTree - > Nodes . Num ( ) ) ;
for ( const FInstancedStruct & Node : StateTree - > Nodes )
2021-09-28 13:33:00 -04:00
{
2022-02-24 08:19:23 -05:00
DebugString + = FString : : Printf ( TEXT ( " %s \n " ) , Node . IsValid ( ) ? * Node . GetScriptStruct ( ) - > GetName ( ) : TEXT ( " null " ) ) ;
2021-09-28 13:33:00 -04:00
}
2022-02-24 08:19:23 -05:00
// Instance InstanceData data (e.g. tasks, evaluators, conditions)
2021-11-12 05:49:31 -05:00
DebugString + = FString : : Printf ( TEXT ( " \n Instance Data(%d) \n " ) , StateTree - > Instances . Num ( ) ) ;
for ( const FInstancedStruct & Data : StateTree - > Instances )
{
DebugString + = FString : : Printf ( TEXT ( " %s \n " ) , Data . IsValid ( ) ? * Data . GetScriptStruct ( ) - > GetName ( ) : TEXT ( " null " ) ) ;
}
2022-02-24 08:19:23 -05:00
// Instance InstanceData offsets (e.g. tasks, evaluators, conditions)
2022-03-23 08:06:00 -04:00
if ( const FStateTreeInstanceDataLayout * InstanceLayout = StateTree - > GetInstanceDataDefaultValue ( ) . GetLayout ( ) . Get ( ) )
2021-09-28 13:33:00 -04:00
{
2022-02-24 08:19:23 -05:00
DebugString + = FString : : Printf ( TEXT ( " \n Instance Data Offsets(%d) \n [ %-40s | %-6s ] \n " ) , InstanceLayout - > Num ( ) , TEXT ( " Name " ) , TEXT ( " Offset " ) ) ;
for ( int32 Index = 0 ; Index < InstanceLayout - > Num ( ) ; Index + + )
{
const FStateTreeInstanceDataLayout : : FLayoutItem & Item = InstanceLayout - > GetItem ( Index ) ;
DebugString + = FString : : Printf ( TEXT ( " | %-40s | %6d | \n " ) , Item . ScriptStruct ? * Item . ScriptStruct - > GetName ( ) : TEXT ( " null " ) , Item . Offset ) ;
}
2021-09-28 13:33:00 -04:00
}
2021-11-12 05:49:31 -05:00
// External data (e.g. fragments, subsystems)
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 )
2021-09-28 13:33:00 -04:00
{
2021-11-12 05:49:31 -05:00
DebugString + = FString : : Printf ( TEXT ( " | %-40s | %8s | %5d | \n " ) , Desc . Struct ? * Desc . Struct - > GetName ( ) : TEXT ( " null " ) , * UEnum : : GetValueAsString ( Desc . Requirement ) , Desc . Handle . DataViewIndex ) ;
2021-09-28 13:33:00 -04:00
}
// Bindings
StateTree - > PropertyBindings . DebugPrintInternalLayout ( DebugString ) ;
// Transitions
DebugString + = FString : : Printf ( TEXT ( " \n Transitions(%d) \n [ %-3s | %15s | %-40s | %-40s | %-8s ] \n " ) , StateTree - > Transitions . Num ( )
, TEXT ( " Idx " ) , TEXT ( " State " ) , TEXT ( " Transition Type " ) , TEXT ( " Transition Event " ) , TEXT ( " Num Cond " ) ) ;
2022-04-05 09:44:28 -04:00
for ( const FCompactStateTransition & Transition : StateTree - > Transitions )
2021-09-28 13:33:00 -04:00
{
DebugString + = FString : : Printf ( TEXT ( " | %3d | %15s | %-40s | %-40s | %8d | \n " ) ,
Transition . ConditionsBegin , * Transition . State . Describe ( ) ,
2021-10-27 06:11:44 -04:00
* UEnum : : GetValueAsString ( Transition . Type ) ,
* UEnum : : GetValueAsString ( Transition . Event ) ,
2021-09-28 13:33:00 -04:00
Transition . ConditionsNum ) ;
}
2021-11-12 05:49:31 -05:00
// DataViews
DebugString + = FString : : Printf ( TEXT ( " \n DataViews(%d) \n " ) , DataViews . Num ( ) ) ;
for ( const FStateTreeDataView & DataView : DataViews )
2021-09-28 13:33:00 -04:00
{
2021-11-12 05:49:31 -05:00
DebugString + = FString : : Printf ( TEXT ( " [%s] \n " ) , DataView . IsValid ( ) ? * DataView . GetStruct ( ) - > GetName ( ) : TEXT ( " null " ) ) ;
2021-09-28 13:33:00 -04:00
}
// 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 " ) ,
StateTree - > States . Num ( ) ,
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-04-05 09:44:28 -04:00
for ( const FCompactStateTreeState & State : StateTree - > States )
2021-09-28 13:33:00 -04:00
{
2022-04-05 03:20:57 -04:00
DebugString + = FString : : Printf ( TEXT ( " | %-30s | %15s | %5s [%3d:%-3d[ | %9s %4d %4d %4d %4d | %3s %4d %4d %4d %4d \n " ) ,
2021-09-28 13:33:00 -04:00
* State . Name . ToString ( ) , * State . Parent . Describe ( ) ,
TEXT ( " " ) , State . ChildrenBegin , State . ChildrenEnd ,
TEXT ( " " ) , State . EnterConditionsBegin , State . TransitionsBegin , State . TasksBegin , State . EvaluatorsBegin ,
2022-04-05 03:20:57 -04:00
TEXT ( " " ) , State . EnterConditionsNum , State . TransitionsNum , State . TasksNum , State . EvaluatorsNum ) ;
2021-09-28 13:33:00 -04:00
}
2021-11-12 05:49:31 -05:00
DebugString + = FString : : Printf ( TEXT ( " \n States \n [ %-30s | %-12s | %-30s | %15s | %10s ] \n " ) ,
TEXT ( " State " ) , TEXT ( " Type " ) , TEXT ( " Name " ) , TEXT ( " Bindings " ) , TEXT ( " Struct Idx " ) ) ;
2022-04-05 09:44:28 -04:00
for ( const FCompactStateTreeState & State : StateTree - > States )
2021-09-28 13:33:00 -04:00
{
// Evaluators
if ( State . EvaluatorsNum )
{
for ( int32 EvalIndex = 0 ; EvalIndex < State . EvaluatorsNum ; EvalIndex + + )
{
2022-02-24 08:19:23 -05:00
const FStateTreeEvaluatorBase & Eval = GetNode < FStateTreeEvaluatorBase > ( State . EvaluatorsBegin + EvalIndex ) ;
2021-09-28 13:33:00 -04:00
DebugString + = FString : : Printf ( TEXT ( " | %-30s | %-12s | %-30s | %15s | %10d | \n " ) , * State . Name . ToString ( ) ,
2021-11-12 05:49:31 -05:00
TEXT ( " Evaluator " ) , * Eval . Name . ToString ( ) , * Eval . BindingsBatch . Describe ( ) , Eval . DataViewIndex ) ;
2021-09-28 13:33:00 -04:00
}
}
2021-11-24 04:26:29 -05:00
// Tasks
if ( State . TasksNum )
{
for ( int32 TaskIndex = 0 ; TaskIndex < State . TasksNum ; TaskIndex + + )
{
2022-02-24 08:19:23 -05:00
const FStateTreeTaskBase & Task = GetNode < FStateTreeTaskBase > ( State . TasksBegin + TaskIndex ) ;
2021-11-24 04:26:29 -05:00
DebugString + = FString : : Printf ( TEXT ( " | %-30s | %-12s | %-30s | %15s | %10d | \n " ) , * State . Name . ToString ( ) ,
TEXT ( " Task " ) , * Task . Name . ToString ( ) , * Task . BindingsBatch . Describe ( ) , Task . DataViewIndex ) ;
}
}
2021-09-28 13:33:00 -04:00
}
UE_LOG ( LogStateTree , Log , TEXT ( " %s " ) , * DebugString ) ;
}
2022-04-28 02:19:09 -04:00
int32 FStateTreeExecutionContext : : GetStateChangeCount ( const FStateTreeInstanceData * ExternalInstanceData ) const
{
const FStateTreeInstanceData & InstanceData = SelectInstanceData ( ExternalInstanceData ) ;
checkSlow ( InstanceData . GetLayout ( ) = = StateTree - > GetInstanceDataDefaultValue ( ) . GetLayout ( ) ) ;
const FStateTreeExecutionState & Exec = GetExecState ( InstanceData ) ;
return Exec . StateChangeCount ;
}
# endif // WITH_STATETREE_DEBUG
2022-02-24 08:19:23 -05:00
FString FStateTreeExecutionContext : : GetActiveStateName ( const FStateTreeInstanceData * ExternalInstanceData ) const
2021-09-28 13:33:00 -04:00
{
if ( ! StateTree )
{
return FString ( TEXT ( " <None> " ) ) ;
}
2022-04-05 03:20:57 -04:00
2022-02-24 08:19:23 -05:00
const FStateTreeInstanceData & InstanceData = SelectInstanceData ( ExternalInstanceData ) ;
2022-03-23 08:06:00 -04:00
checkSlow ( InstanceData . GetLayout ( ) = = StateTree - > GetInstanceDataDefaultValue ( ) . GetLayout ( ) ) ;
2022-02-24 08:19:23 -05:00
const FStateTreeExecutionState & Exec = GetExecState ( InstanceData ) ;
2021-09-28 13:33:00 -04:00
FString FullStateName ;
// Active States
2022-04-05 03:20:57 -04:00
for ( int32 Index = 0 ; Index < Exec . ActiveStates . Num ( ) ; Index + + )
2021-09-28 13:33:00 -04:00
{
2022-04-05 03:20:57 -04:00
const FStateTreeHandle Handle = Exec . ActiveStates [ Index ] ;
2021-09-28 13:33:00 -04:00
if ( Handle . IsValid ( ) )
{
2022-04-05 09:44:28 -04:00
const FCompactStateTreeState & State = StateTree - > States [ Handle . Index ] ;
2022-04-05 03:20:57 -04:00
bool bIsLinked = false ;
2021-09-28 13:33:00 -04:00
if ( Index > 0 )
{
FullStateName + = TEXT ( " \n " ) ;
2022-04-05 03:20:57 -04:00
bIsLinked = Exec . ActiveStates [ Index - 1 ] ! = State . Parent ;
2021-09-28 13:33:00 -04:00
}
FullStateName + = FString : : Printf ( TEXT ( " %*s- " ) , Index * 3 , TEXT ( " " ) ) ; // Indent
FullStateName + = * State . Name . ToString ( ) ;
2022-04-05 03:20:57 -04:00
if ( bIsLinked )
{
FullStateName + = TEXT ( " > " ) ;
}
2021-09-28 13:33:00 -04:00
}
}
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-04-05 03:20:57 -04:00
TArray < FName > FStateTreeExecutionContext : : GetActiveStateNames ( const FStateTreeInstanceData * ExternalInstanceData ) const
{
TArray < FName > Result ;
if ( ! StateTree )
{
return Result ;
}
const FStateTreeInstanceData & InstanceData = SelectInstanceData ( ExternalInstanceData ) ;
checkSlow ( InstanceData . GetLayout ( ) = = StateTree - > GetInstanceDataDefaultValue ( ) . GetLayout ( ) ) ;
const FStateTreeExecutionState & Exec = GetExecState ( InstanceData ) ;
// Active States
for ( int32 Index = 0 ; Index < Exec . ActiveStates . Num ( ) ; Index + + )
{
const FStateTreeHandle Handle = Exec . ActiveStates [ Index ] ;
if ( Handle . IsValid ( ) )
{
2022-04-05 09:44:28 -04:00
const FCompactStateTreeState & State = StateTree - > States [ Handle . Index ] ;
2022-04-05 03:20:57 -04:00
Result . Add ( State . Name ) ;
}
}
return Result ;
}
2021-09-28 13:33:00 -04:00
# undef STATETREE_LOG
# undef STATETREE_CLOG