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"
# include "CoreMinimal.h"
# include "Engine/World.h"
# include "VisualLogger/VisualLogger.h"
# include "ProfilingDebugging/CsvProfiler.h"
# include "Algo/Reverse.h"
# include "Logging/LogScopedVerbosityOverride.h"
2022-02-24 08:19:23 -05:00
# include "ProfilingDebugging/CsvProfiler.h"
2021-09-28 13:33:00 -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
{
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-02-24 08:19:23 -05:00
InitInstanceData ( InOwner , * StateTree , InternalInstanceData ) ;
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
// 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-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-02-24 08:19:23 -05:00
bool FStateTreeExecutionContext : : InitInstanceData ( UObject & InOwner , const UStateTree & InStateTree , FStateTreeInstanceData & OutInstanceData )
{
OutInstanceData . CopyFrom ( InStateTree . InstanceDataDefaultValue ) ;
check ( OutInstanceData . IsValid ( ) ) ;
FStateTreeExecutionState & Exec = GetExecState ( OutInstanceData ) ;
2021-11-24 04:26:29 -05:00
Exec . InstanceObjects . Reset ( ) ;
2022-02-24 08:19:23 -05:00
for ( const UObject * Instance : InStateTree . InstanceObjects )
2021-11-24 04:26:29 -05:00
{
Exec . InstanceObjects . Add ( DuplicateObject ( Instance , & InOwner ) ) ;
}
return true ;
}
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 ) ;
checkSlow ( InstanceData . GetLayout ( ) = = StateTree - > InstanceDataDefaultValue . GetLayout ( ) ) ;
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 )
{
const FStateTreeTransitionResult Transition ( FStateTreeStateStatus ( Exec . CurrentState , Exec . LastTickStatus ) , 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 ;
Exec . CurrentState = FStateTreeHandle : : Invalid ;
Exec . LastTickStatus = EStateTreeRunStatus : : Unset ;
2021-11-15 03:46:57 -05:00
// Select new Starting from root.
VisitedStates . Init ( false , StateTree - > States . Num ( ) ) ;
static const FStateTreeHandle RootState = FStateTreeHandle ( 0 ) ;
2022-02-24 08:19:23 -05:00
const FStateTreeTransitionResult Transition = SelectState ( InstanceData , FStateTreeStateStatus ( FStateTreeHandle : : Invalid ) , RootState , RootState , 0 ) ;
2021-11-15 03:46:57 -05:00
// Handle potential transition.
if ( Transition . Next . IsValid ( ) )
{
if ( Transition . Next = = FStateTreeHandle : : Succeeded | | Transition . Next = = FStateTreeHandle : : 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'. " ) ,
ANSI_TO_TCHAR ( __FUNCTION__ ) , Transition . Next = = FStateTreeHandle : : Succeeded ? TEXT ( " succeeded " ) : TEXT ( " failed " ) , * GetNameSafe ( Owner ) , * GetNameSafe ( StateTree ) ) ;
Exec . TreeRunStatus = Transition . Next = = FStateTreeHandle : : Succeeded ? EStateTreeRunStatus : : Succeeded : EStateTreeRunStatus : : Failed ;
}
else
{
// 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-11-15 03:46:57 -05:00
// Report state completed immediately.
if ( Exec . LastTickStatus ! = EStateTreeRunStatus : : Running )
{
2022-02-24 08:19:23 -05:00
StateCompleted ( InstanceData , Exec . CurrentState , Exec . LastTickStatus ) ;
2021-11-15 03:46:57 -05:00
}
}
}
if ( ! Exec . CurrentState . IsValid ( ) )
{
// 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 ) ;
checkSlow ( InstanceData . GetLayout ( ) = = StateTree - > InstanceDataDefaultValue . GetLayout ( ) ) ;
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.
if ( Exec . CurrentState . IsValid ( ) & & ( Exec . CurrentState ! = FStateTreeHandle : : Succeeded | | Exec . CurrentState ! = FStateTreeHandle : : Failed ) )
2021-09-28 13:33:00 -04:00
{
2021-12-09 05:26:00 -05:00
// Transition to Succeeded state.
2021-09-28 13:33:00 -04:00
const FStateTreeTransitionResult Transition ( FStateTreeStateStatus ( Exec . CurrentState , Exec . LastTickStatus ) , FStateTreeHandle : : Succeeded ) ;
2022-02-24 08:19:23 -05:00
ExitState ( InstanceData , Transition ) ;
2021-12-09 05:26:00 -05:00
Exec . CurrentState = FStateTreeHandle : : Succeeded ;
2021-09-28 13:33:00 -04:00
}
2021-12-09 05:26:00 -05:00
Exec . TreeRunStatus = Exec . CurrentState = = FStateTreeHandle : : Succeeded ? EStateTreeRunStatus : : Succeeded : EStateTreeRunStatus : : Failed ;
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 ) ;
checkSlow ( InstanceData . GetLayout ( ) = = StateTree - > InstanceDataDefaultValue . GetLayout ( ) ) ;
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-02-24 08:19:23 -05:00
TickEvaluators ( InstanceData , Exec . CurrentState , EStateTreeEvaluationType : : Tick , DeltaTime ) ;
2021-11-15 03:46:57 -05:00
// Tick tasks on active states.
2022-02-24 08:19:23 -05:00
Exec . LastTickStatus = TickTasks ( InstanceData , Exec . CurrentState , DeltaTime ) ;
2021-11-15 03:46:57 -05:00
// Report state completed immediately.
if ( Exec . LastTickStatus ! = EStateTreeRunStatus : : Running )
{
2022-02-24 08:19:23 -05:00
StateCompleted ( InstanceData , Exec . CurrentState , Exec . LastTickStatus ) ;
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.
static const int32 MaxIterations = 5 ;
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-02-24 08:19:23 -05:00
FStateTreeTransitionResult Transition = TriggerTransitions ( InstanceData , FStateTreeStateStatus ( Exec . CurrentState , Exec . LastTickStatus ) , 0 ) ;
2021-09-28 13:33:00 -04:00
// Handle potential transition.
if ( Transition . Next . IsValid ( ) )
{
2022-02-24 08:19:23 -05:00
ExitState ( InstanceData , Transition ) ;
2021-09-28 13:33:00 -04:00
if ( Transition . Next = = FStateTreeHandle : : Succeeded | | Transition . Next = = FStateTreeHandle : : Failed )
{
// Transition to a terminal state (succeeded/failed), or default transition failed.
Exec . TreeRunStatus = Transition . Next = = FStateTreeHandle : : Succeeded ? EStateTreeRunStatus : : Succeeded : EStateTreeRunStatus : : Failed ;
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-02-24 08:19:23 -05:00
StateCompleted ( InstanceData , Exec . CurrentState , Exec . LastTickStatus ) ;
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
}
if ( ! Exec . CurrentState . IsValid ( ) )
{
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
if ( ! Transition . Next . IsValid ( ) )
{
return EStateTreeRunStatus : : Failed ;
}
2022-02-24 08:19:23 -05:00
FStateTreeExecutionState & Exec = GetExecState ( InstanceData ) ;
2021-12-09 05:26:00 -05:00
2021-09-28 13:33:00 -04:00
// Activate evaluators along the active branch.
TStaticArray < FStateTreeHandle , 32 > States ;
const int32 NumStates = GetActiveStates ( Transition . Next , States ) ;
TStaticArray < FStateTreeHandle , 32 > PrevStates ;
const int32 NumPrevStates = GetActiveStates ( Transition . Source . State , PrevStates ) ;
// Pad prev states with invalid states if it's shorter.
for ( int32 Index = NumPrevStates ; Index < NumStates ; Index + + )
{
PrevStates [ Index ] = FStateTreeHandle : : Invalid ;
}
// 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 ;
2021-12-09 05:26:00 -05:00
Exec . EnterStateFailedTaskIndex = INDEX_NONE ;
Exec . CurrentState = FStateTreeHandle : : Failed ;
for ( int32 Index = 0 ; Index < NumStates & & Result ! = EStateTreeRunStatus : : Failed ; Index + + )
2021-09-28 13:33:00 -04:00
{
const FStateTreeHandle CurrentHandle = States [ Index ] ;
const FBakedStateTreeState & State = StateTree - > States [ CurrentHandle . Index ] ;
bOnTargetBranch = bOnTargetBranch | | CurrentHandle = = Transition . Target ;
const bool bWasActive = PrevStates [ Index ] = = CurrentHandle ;
if ( bWasActive & & ! bOnTargetBranch )
{
2021-12-09 05:26:00 -05:00
// States which will keep on being active and were not part of the transition will not get enter/exit state.
2021-11-12 05:49:31 -05:00
// Must update data views.
2021-09-28 13:33:00 -04:00
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 ) ;
DataViews [ Eval . DataViewIndex ] = GetInstanceData ( InstanceData , Eval . bInstanceIsObject , Eval . InstanceIndex ) ;
2021-09-28 13:33:00 -04:00
}
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 ) ;
DataViews [ Task . DataViewIndex ] = GetInstanceData ( InstanceData , Task . bInstanceIsObject , Task . InstanceIndex ) ;
2021-09-28 13:33:00 -04:00
}
continue ;
}
CurrentTransition . Current = CurrentHandle ;
const EStateTreeStateChangeType ChangeType = bWasActive ? EStateTreeStateChangeType : : Sustained : EStateTreeStateChangeType : : Changed ;
STATETREE_LOG ( Log , TEXT ( " %*sEnter state '%s' %s " ) , Index * UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * DebugGetStatePath ( States , Index ) , * StaticEnum < EStateTreeStateChangeType > ( ) - > GetValueAsString ( ChangeType ) ) ;
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
}
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
}
STATETREE_LOG ( Verbose , TEXT ( " %*s Notify Task '%s' " ) , Index * UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * Task . Name . ToString ( ) ) ;
2022-02-24 08:19:23 -05:00
2021-09-28 13:33:00 -04:00
{
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 ;
Exec . CurrentState = CurrentHandle ;
Result = Status ;
break ;
}
2021-09-28 13:33:00 -04:00
}
}
}
2021-12-09 05:26:00 -05:00
if ( Result ! = EStateTreeRunStatus : : Failed )
{
Exec . CurrentState = Transition . Next ;
}
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 ) ;
2021-09-28 13:33:00 -04:00
if ( ! Transition . Source . State . IsValid ( ) )
{
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 ;
// Deactivate evaluators along the active branch.
TStaticArray < FStateTreeHandle , 32 > States ;
const int32 NumStates = GetActiveStates ( Transition . Source . State , States ) ;
TStaticArray < FStateTreeHandle , 32 > NextStates ;
const int32 NumNextStates = GetActiveStates ( Transition . Next , NextStates ) ;
// Pad next states with invalid states if it's shorter.
for ( int32 Index = NumNextStates ; Index < NumStates ; Index + + )
{
NextStates [ Index ] = FStateTreeHandle : : Invalid ;
}
// 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 ;
// It would be more symmetrical, if the evals, tasks and states were executed in reverse order, but we need
// to do it like this because of the property copies.
for ( int32 Index = 0 ; Index < NumStates ; Index + + )
{
const FStateTreeHandle CurrentHandle = States [ Index ] ;
const FStateTreeHandle NextHandle = NextStates [ Index ] ;
const FBakedStateTreeState & State = StateTree - > States [ CurrentHandle . Index ] ;
const bool bRemainsActive = NextHandle = = States [ Index ] ;
bOnTargetBranch = bOnTargetBranch | | NextHandle = = Transition . Target ;
if ( bRemainsActive & & ! bOnTargetBranch )
{
2021-12-09 05:26:00 -05:00
// States which will keep on being active and were not part of the transition will not get enter/exit state.
2021-09-28 13:33:00 -04:00
// Must update item views.
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 ) ;
DataViews [ Eval . DataViewIndex ] = GetInstanceData ( InstanceData , Eval . bInstanceIsObject , Eval . InstanceIndex ) ;
2021-09-28 13:33:00 -04:00
}
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 ) ;
DataViews [ Task . DataViewIndex ] = GetInstanceData ( InstanceData , Task . bInstanceIsObject , Task . InstanceIndex ) ;
2021-09-28 13:33:00 -04:00
}
continue ;
}
CurrentTransition . Current = CurrentHandle ;
const EStateTreeStateChangeType ChangeType = bRemainsActive ? EStateTreeStateChangeType : : Sustained : EStateTreeStateChangeType : : Changed ;
STATETREE_LOG ( Log , TEXT ( " %*sExit state '%s' %s " ) , Index * UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * DebugGetStatePath ( States , Index ) , * StaticEnum < EStateTreeStateChangeType > ( ) - > GetValueAsString ( ChangeType ) ) ;
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
}
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_ExitState ) ;
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_Eval_ExitState ) ;
Eval . ExitState ( * this , ChangeType , CurrentTransition ) ;
}
2021-09-28 13:33:00 -04:00
}
// Deactivate 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
{
2021-12-09 05:26:00 -05:00
// Call task completed only if EnterState() was called. This ensures that we have matching calls to Enter/ExitState.
// 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 ) ;
const FStateTreeDataView TaskInstanceView = GetInstanceData ( InstanceData , Task . bInstanceIsObject , Task . InstanceIndex ) ;
2021-12-09 05:26:00 -05:00
DataViews [ Task . DataViewIndex ] = TaskInstanceView ;
2021-09-28 13:33:00 -04:00
2021-12-09 05:26:00 -05:00
// Copy bound properties.
if ( Task . BindingsBatch . IsValid ( ) )
{
StateTree - > PropertyBindings . CopyTo ( DataViews , Task . BindingsBatch . Index , TaskInstanceView ) ;
}
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-02-24 08:19:23 -05:00
void FStateTreeExecutionContext : : StateCompleted ( FStateTreeInstanceData & InstanceData , const FStateTreeHandle CurrentState , const EStateTreeRunStatus CompletionStatus )
2021-09-28 13:33:00 -04:00
{
2022-02-24 08:19:23 -05:00
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_StateCompleted ) ;
2021-09-28 13:33:00 -04:00
if ( ! CurrentState . IsValid ( ) )
{
return ;
}
TStaticArray < FStateTreeHandle , 32 > States ;
const int32 NumStates = GetActiveStates ( CurrentState , States ) ;
2022-02-24 08:19:23 -05:00
FStateTreeExecutionState & Exec = GetExecState ( InstanceData ) ;
2021-12-09 05:26:00 -05:00
2021-09-28 13:33:00 -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 = NumStates - 1 ; Index > = 0 ; Index - - )
{
const FStateTreeHandle CurrentHandle = States [ Index ] ;
const FBakedStateTreeState & State = StateTree - > States [ CurrentHandle . Index ] ;
STATETREE_LOG ( Verbose , TEXT ( " %*sState Completed '%s' %s " ) , Index * UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * DebugGetStatePath ( States , Index ) , * StaticEnum < EStateTreeRunStatus > ( ) - > GetValueAsString ( CompletionStatus ) ) ;
// 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 ( ) ) ;
Task . StateCompleted ( * this , CompletionStatus , CurrentState ) ;
}
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 ( ) ) ;
Eval . StateCompleted ( * this , CompletionStatus , CurrentState ) ;
}
}
}
2022-02-24 08:19:23 -05:00
void FStateTreeExecutionContext : : TickEvaluators ( FStateTreeInstanceData & InstanceData , const FStateTreeHandle CurrentState , const EStateTreeEvaluationType EvalType , 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_TickEvaluators ) ;
2021-09-28 13:33:00 -04:00
if ( ! CurrentState . IsValid ( ) )
{
return ;
}
TStaticArray < FStateTreeHandle , 32 > States ;
const int32 NumStates = GetActiveStates ( CurrentState , States ) ;
for ( int32 Index = 0 ; Index < NumStates ; Index + + )
{
const FStateTreeHandle CurrentHandle = States [ Index ] ;
if ( VisitedStates [ CurrentHandle . Index ] )
{
// Already ticked this frame.
continue ;
}
const FBakedStateTreeState & State = StateTree - > States [ CurrentHandle . Index ] ;
STATETREE_CLOG ( State . EvaluatorsNum > 0 , Verbose , TEXT ( " %*sTicking Evaluators of state '%s' %s " ) , Index * UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * DebugGetStatePath ( States , Index ) , * StaticEnum < EStateTreeEvaluationType > ( ) - > GetValueAsString ( EvalType ) ) ;
// 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
}
VisitedStates [ CurrentHandle . Index ] = true ;
}
}
2022-02-24 08:19:23 -05:00
EStateTreeRunStatus FStateTreeExecutionContext : : TickTasks ( FStateTreeInstanceData & InstanceData , const FStateTreeHandle CurrentState , 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
if ( ! CurrentState . IsValid ( ) )
{
return EStateTreeRunStatus : : Failed ;
}
TStaticArray < FStateTreeHandle , 32 > States ;
const int32 NumStates = GetActiveStates ( CurrentState , States ) ;
EStateTreeRunStatus Result = EStateTreeRunStatus : : Running ;
int32 NumTotalTasks = 0 ;
for ( int32 Index = 0 ; Index < NumStates & & Result ! = EStateTreeRunStatus : : Failed ; Index + + )
{
const FStateTreeHandle CurrentHandle = States [ Index ] ;
const FBakedStateTreeState & State = StateTree - > States [ CurrentHandle . Index ] ;
STATETREE_CLOG ( State . TasksNum > 0 , Verbose , TEXT ( " %*sTicking Tasks of state '%s' " ) , Index * UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * DebugGetStatePath ( States , Index ) ) ;
// 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
for ( uint32 i = 0 ; i < ConditionsNum ; i + + )
{
2022-02-24 08:19:23 -05:00
const FStateTreeConditionBase & Cond = GetNode < FStateTreeConditionBase > ( ConditionsOffset + i ) ;
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
{
2021-11-12 05:49:31 -05:00
StateTree - > PropertyBindings . CopyTo ( DataViews , Cond . BindingsBatch . Index , CondInstanceView ) ;
2021-09-28 13:33:00 -04:00
}
2021-11-12 05:49:31 -05:00
if ( ! Cond . TestCondition ( * this ) )
2021-09-28 13:33:00 -04:00
{
return false ;
}
}
return true ;
}
2022-02-24 08:19:23 -05:00
FStateTreeTransitionResult FStateTreeExecutionContext : : TriggerTransitions ( FStateTreeInstanceData & InstanceData , const FStateTreeStateStatus CurrentStatus , const int32 Depth )
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 ;
if ( CurrentStatus . RunStatus = = EStateTreeRunStatus : : Succeeded )
{
Event = EStateTreeTransitionEvent : : OnSucceeded ;
}
else if ( CurrentStatus . RunStatus = = EStateTreeRunStatus : : Failed )
{
Event = EStateTreeTransitionEvent : : OnFailed ;
}
// Walk towards root and check all transitions along the way.
for ( FStateTreeHandle Handle = CurrentStatus . State ; Handle . IsValid ( ) ; Handle = StateTree - > States [ Handle . Index ] . Parent )
{
const FBakedStateTreeState & State = StateTree - > States [ Handle . Index ] ;
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 ;
const FBakedStateTransition & 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 ) ;
STATETREE_LOG ( Verbose , TEXT ( " Gated transition triggered from '%s' (%s) -> '%s' %.1fs " ) , * GetSafeStateName ( CurrentStatus . State ) , * State . Name . ToString ( ) , * GetSafeStateName ( Transition . State ) , Exec . GatedTransitionTime ) ;
}
// Keep on updating current state, until we have tried to trigger
if ( Exec . GatedTransitionTime > 0.0f )
{
return FStateTreeTransitionResult ( CurrentStatus , FStateTreeHandle : : Invalid ) ;
}
STATETREE_LOG ( Verbose , TEXT ( " Passed gated transition from '%s' (%s) -> '%s' " ) , * GetSafeStateName ( CurrentStatus . State ) , * State . Name . ToString ( ) , * GetSafeStateName ( Transition . State ) ) ;
}
if ( Transition . Type = = EStateTreeTransitionType : : GotoState | | Transition . Type = = EStateTreeTransitionType : : NextState )
{
2022-02-24 08:19:23 -05:00
FStateTreeTransitionResult Result = SelectState ( InstanceData , CurrentStatus , Transition . State , Transition . State , Depth + 1 ) ;
2021-09-28 13:33:00 -04:00
if ( Result . Next . IsValid ( ) )
{
STATETREE_LOG ( Verbose , TEXT ( " Transition on state '%s' (%s) -[%s]-> state '%s' " ) , * GetSafeStateName ( CurrentStatus . State ) , * State . Name . ToString ( ) , * GetSafeStateName ( Result . Target ) , * GetSafeStateName ( Result . Next ) ) ;
return Result ;
}
}
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.
return FStateTreeTransitionResult ( CurrentStatus , FStateTreeHandle : : Invalid ) ;
}
else if ( Transition . Type = = EStateTreeTransitionType : : Succeeded )
{
STATETREE_LOG ( Verbose , TEXT ( " Stop tree execution from state '%s' (%s): Succeeded " ) , * GetSafeStateName ( CurrentStatus . State ) , * State . Name . ToString ( ) ) ;
return FStateTreeTransitionResult ( CurrentStatus , FStateTreeHandle : : Succeeded ) ;
}
else
{
STATETREE_LOG ( Verbose , TEXT ( " Stop tree execution from state '%s' (%s): Failed " ) , * GetSafeStateName ( CurrentStatus . State ) , * State . Name . ToString ( ) ) ;
return FStateTreeTransitionResult ( CurrentStatus , FStateTreeHandle : : Failed ) ;
}
}
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 ;
}
}
}
if ( CurrentStatus . RunStatus ! = EStateTreeRunStatus : : Running )
{
STATETREE_LOG ( Error , TEXT ( " %s: Default transitions on state '%s' could not be triggered. '%s' using StateTree '%s'. " ) ,
ANSI_TO_TCHAR ( __FUNCTION__ ) , * GetSafeStateName ( CurrentStatus . State ) , * GetNameSafe ( Owner ) , * GetNameSafe ( StateTree ) ) ;
return FStateTreeTransitionResult ( CurrentStatus , FStateTreeHandle : : Failed ) ;
}
// No transition triggered, keep on updating current state.
return FStateTreeTransitionResult ( CurrentStatus , FStateTreeHandle : : Invalid ) ;
}
2022-02-24 08:19:23 -05:00
FStateTreeTransitionResult FStateTreeExecutionContext : : SelectState ( FStateTreeInstanceData & InstanceData , const FStateTreeStateStatus InitialStateStatus , const FStateTreeHandle InitialTargetState , const FStateTreeHandle NextState , const int Depth )
2021-09-28 13:33:00 -04:00
{
2022-02-24 08:19:23 -05:00
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_SelectState ) ;
2021-09-28 13:33:00 -04:00
if ( ! NextState . IsValid ( ) )
{
// Trying to select non-existing state.
STATETREE_LOG ( Error , TEXT ( " %s: Trying to select invalid state from '%s' via '%s. '%s' using StateTree '%s'. " ) ,
ANSI_TO_TCHAR ( __FUNCTION__ ) , * GetStateStatusString ( InitialStateStatus ) , * GetSafeStateName ( InitialTargetState ) , * GetNameSafe ( Owner ) , * GetNameSafe ( StateTree ) ) ;
return FStateTreeTransitionResult ( InitialStateStatus , InitialTargetState , FStateTreeHandle : : Invalid ) ;
}
const FBakedStateTreeState & State = StateTree - > States [ NextState . Index ] ;
// Make sure all the evaluators for the target state are up to date.
2022-02-24 08:19:23 -05:00
TickEvaluators ( 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
{
// If the state has children, proceed to select children.
if ( State . HasChildren ( ) )
{
for ( uint16 ChildState = State . ChildrenBegin ; ChildState < State . ChildrenEnd ; ChildState = StateTree - > States [ ChildState ] . GetNextSibling ( ) )
{
2022-02-24 08:19:23 -05:00
const FStateTreeTransitionResult ChildResult = SelectState ( InstanceData , InitialStateStatus , InitialTargetState , FStateTreeHandle ( ChildState ) , Depth + 1 ) ;
2021-09-28 13:33:00 -04:00
if ( ChildResult . Next . IsValid ( ) )
{
// Selection succeeded
return ChildResult ;
}
}
}
else
{
// Select this state.
return FStateTreeTransitionResult ( InitialStateStatus , InitialTargetState , NextState ) ;
}
}
// Nothing got selected.
return FStateTreeTransitionResult ( InitialStateStatus , InitialTargetState , FStateTreeHandle : : Invalid ) ;
}
int32 FStateTreeExecutionContext : : GetActiveStates ( const FStateTreeHandle StateHandle , TStaticArray < FStateTreeHandle , 32 > & OutStateHandles ) const
{
if ( StateHandle = = FStateTreeHandle : : Succeeded | | StateHandle = = FStateTreeHandle : : Failed | | StateHandle = = FStateTreeHandle : : Invalid )
{
return 0 ;
}
int32 NumStates = 0 ;
FStateTreeHandle CurrentHandle = StateHandle ;
while ( CurrentHandle . IsValid ( ) & & NumStates < OutStateHandles . Num ( ) )
{
OutStateHandles [ NumStates + + ] = CurrentHandle ;
CurrentHandle = StateTree - > States [ CurrentHandle . Index ] . Parent ;
}
Algo : : Reverse ( OutStateHandles . GetData ( ) , NumStates ) ;
return NumStates ;
}
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) " ) ;
}
FString FStateTreeExecutionContext : : DebugGetStatePath ( const TArrayView < FStateTreeHandle > ActiveStateHandles , const int32 ActiveStateIndex ) const
{
FString StatePath ;
if ( ! ensureMsgf ( ActiveStateHandles . IsValidIndex ( ActiveStateIndex ) , TEXT ( " Provided index must be valid " ) ) )
{
return StatePath ;
}
for ( int32 i = 0 ; i < = ActiveStateIndex ; i + + )
{
const FBakedStateTreeState & State = StateTree - > States [ ActiveStateHandles [ i ] . Index ] ;
StatePath . Appendf ( TEXT ( " %s%s " ) , i = = 0 ? TEXT ( " " ) : TEXT ( " . " ) , * State . Name . ToString ( ) ) ;
}
return StatePath ;
}
FString FStateTreeExecutionContext : : GetStateStatusString ( const FStateTreeStateStatus StateStatus ) const
{
// Invalid (null) state in status refers to the whole tree.
const FString StateName = GetSafeStateName ( StateStatus . State ) ;
switch ( StateStatus . RunStatus )
{
case EStateTreeRunStatus : : Unset :
return StateName + TEXT ( " Unset " ) ;
case EStateTreeRunStatus : : Running :
return StateName + TEXT ( " Running " ) ;
case EStateTreeRunStatus : : Succeeded :
return StateName + TEXT ( " Succeeded " ) ;
case EStateTreeRunStatus : : Failed :
return StateName + TEXT ( " Failed " ) ;
default :
return StateName + TEXT ( " (Unknown) " ) ;
}
}
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-02-24 08:19:23 -05:00
void FStateTreeExecutionContext : : AddStructReferencedObjects ( FReferenceCollector & Collector ) const
2021-11-24 04:26:29 -05:00
{
if ( StorageType = = EStateTreeStorage : : Internal )
{
2022-02-24 08:19:23 -05:00
AddStructReferencedObjects ( & InternalInstanceData , Collector ) ;
2021-11-24 04:26:29 -05:00
}
}
2022-02-24 08:19:23 -05:00
void FStateTreeExecutionContext : : AddStructReferencedObjects ( const FStateTreeInstanceData * InstanceData , FReferenceCollector & Collector ) const
2021-11-24 04:26:29 -05:00
{
2022-02-24 08:19:23 -05:00
if ( InstanceData & & InstanceData - > IsValid ( ) )
2021-11-24 04:26:29 -05:00
{
2022-02-24 08:19:23 -05:00
const FStateTreeExecutionState & Exec = GetExecState ( * InstanceData ) ;
for ( const UObject * Object : Exec . InstanceObjects )
{
Collector . AddReferencedObject ( Object ) ;
}
InstanceData - > AddStructReferencedObjects ( Collector ) ;
2021-11-24 04:26:29 -05:00
}
}
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 ) ;
checkSlow ( InstanceData . GetLayout ( ) = = StateTree - > InstanceDataDefaultValue . GetLayout ( ) ) ;
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
TStaticArray < FStateTreeHandle , 32 > ActiveStates ;
const int32 NumStates = GetActiveStates ( Exec . CurrentState , ActiveStates ) ;
DebugString + = TEXT ( " Current State: \n " ) ;
for ( int32 Index = 0 ; Index < NumStates ; Index + + )
{
FStateTreeHandle Handle = ActiveStates [ Index ] ;
if ( Handle . IsValid ( ) )
{
const FBakedStateTreeState & State = StateTree - > States [ Handle . Index ] ;
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)
if ( const FStateTreeInstanceDataLayout * InstanceLayout = StateTree - > InstanceDataDefaultValue . 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 " ) ) ;
for ( const FBakedStateTransition & Transition : StateTree - > Transitions )
{
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 " )
) ;
for ( const FBakedStateTreeState & State : StateTree - > States )
{
DebugString + = FString : : Printf ( TEXT ( " | %-30s | %15s | %5s [%3d:%-3d[ | %9s %4d %4d %4d %4d | %3s %4d %4d %4d %4d | %11s %-16s %-40s %-16s %-40s | \n " ) ,
* State . Name . ToString ( ) , * State . Parent . Describe ( ) ,
TEXT ( " " ) , State . ChildrenBegin , State . ChildrenEnd ,
TEXT ( " " ) , State . EnterConditionsBegin , State . TransitionsBegin , State . TasksBegin , State . EvaluatorsBegin ,
TEXT ( " " ) , State . EnterConditionsNum , State . TransitionsNum , State . TasksNum , State . EvaluatorsNum ,
TEXT ( " " ) , * State . StateDoneTransitionState . Describe ( ) , * StaticEnum < EStateTreeTransitionType > ( ) - > GetValueAsString ( State . StateDoneTransitionType ) ,
* State . StateFailedTransitionState . Describe ( ) , * StaticEnum < EStateTreeTransitionType > ( ) - > GetValueAsString ( State . StateFailedTransitionType ) ) ;
}
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 " ) ) ;
2021-09-28 13:33:00 -04:00
for ( const FBakedStateTreeState & State : StateTree - > States )
{
// 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-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-02-24 08:19:23 -05:00
const FStateTreeInstanceData & InstanceData = SelectInstanceData ( ExternalInstanceData ) ;
checkSlow ( InstanceData . GetLayout ( ) = = StateTree - > InstanceDataDefaultValue . GetLayout ( ) ) ;
const FStateTreeExecutionState & Exec = GetExecState ( InstanceData ) ;
2021-09-28 13:33:00 -04:00
FString FullStateName ;
// Active States
TStaticArray < FStateTreeHandle , 32 > ActiveStates ;
const int32 NumStates = GetActiveStates ( Exec . CurrentState , ActiveStates ) ;
for ( int32 Index = 0 ; Index < NumStates ; Index + + )
{
FStateTreeHandle Handle = ActiveStates [ Index ] ;
if ( Handle . IsValid ( ) )
{
const FBakedStateTreeState & State = StateTree - > States [ Handle . Index ] ;
if ( Index > 0 )
{
FullStateName + = TEXT ( " \n " ) ;
}
FullStateName + = FString : : Printf ( TEXT ( " %*s- " ) , Index * 3 , TEXT ( " " ) ) ; // Indent
FullStateName + = * State . Name . ToString ( ) ;
}
}
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 ;
}
# endif // WITH_STATETREE_DEBUG
# undef STATETREE_LOG
# undef STATETREE_CLOG