2022-05-31 04:51:18 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "StateTreeExecutionContext.h"
# include "StateTreeTaskBase.h"
# include "StateTreeEvaluatorBase.h"
# include "StateTreeConditionBase.h"
2024-06-13 22:57:49 -04:00
# include "StateTreeConsiderationBase.h"
2024-06-04 03:56:38 -04:00
# include "StateTreePropertyFunctionBase.h"
2024-03-28 05:18:19 -04:00
# include "StateTreeReference.h"
2022-05-31 04:51:18 -04:00
# include "Containers/StaticArray.h"
2023-03-14 13:35:46 -04:00
# include "Debugger/StateTreeTrace.h"
# include "Debugger/StateTreeTraceTypes.h"
2023-05-23 10:46:16 -04:00
# include "Misc/ScopeExit.h"
2022-05-31 04:51:18 -04:00
# include "VisualLogger/VisualLogger.h"
# include "ProfilingDebugging/CsvProfiler.h"
# include "Logging/LogScopedVerbosityOverride.h"
2024-04-12 06:04:01 -04:00
# include "Engine/World.h"
2024-05-27 15:15:55 -04:00
# define STATETREE_LOG(Verbosity, Format, ...) UE_VLOG_ALWAYS_UELOG(GetOwner(), LogStateTree, Verbosity, TEXT("%s: ") Format, *GetInstanceDescription(), ##__VA_ARGS__)
# define STATETREE_CLOG(Condition, Verbosity, Format, ...) UE_CVLOG_ALWAYS_UELOG((Condition), GetOwner(), LogStateTree, Verbosity, TEXT("%s: ") Format, *GetInstanceDescription(), ##__VA_ARGS__)
2022-05-31 04:51:18 -04:00
2024-04-22 11:34:53 -04:00
# define STATETREE_LOG_AND_TRACE(LogVerbosity, TraceVerbosity, Format, ...) \
2024-05-27 15:15:55 -04:00
UE_VLOG_ALWAYS_UELOG ( GetOwner ( ) , LogStateTree , LogVerbosity , TEXT ( " %s: " ) Format , * GetInstanceDescription ( ) , # # __VA_ARGS__ ) ; \
2024-04-22 11:34:53 -04:00
STATETREE_TRACE_LOG_EVENT ( TraceVerbosity , Format , # # __VA_ARGS__ )
2023-09-22 09:53:34 -04:00
2024-05-02 11:47:26 -04:00
# if WITH_STATETREE_TRACE
2023-05-29 10:13:21 -04:00
# define ID_NAME PREPROCESSOR_JOIN(InstanceId,__LINE__) \
# define STATETREE_TRACE_SCOPED_PHASE(Phase) \
FStateTreeInstanceDebugId ID_NAME = GetInstanceDebugId ( ) ; \
2023-08-04 14:55:23 -04:00
TRACE_STATETREE_PHASE_EVENT ( ID_NAME , Phase , EStateTreeTraceEventType : : Push , FStateTreeStateHandle : : Invalid ) \
ON_SCOPE_EXIT { TRACE_STATETREE_PHASE_EVENT ( ID_NAME , Phase , EStateTreeTraceEventType : : Pop , FStateTreeStateHandle : : Invalid ) }
2023-05-29 10:13:21 -04:00
2023-08-04 14:55:23 -04:00
# define STATETREE_TRACE_SCOPED_STATE(StateHandle) \
2023-05-29 10:13:21 -04:00
FStateTreeInstanceDebugId ID_NAME = GetInstanceDebugId ( ) ; \
2023-08-04 14:55:23 -04:00
TRACE_STATETREE_PHASE_EVENT ( ID_NAME , EStateTreeUpdatePhase : : Unset , EStateTreeTraceEventType : : Push , StateHandle ) \
ON_SCOPE_EXIT { TRACE_STATETREE_PHASE_EVENT ( ID_NAME , EStateTreeUpdatePhase : : Unset , EStateTreeTraceEventType : : Pop , StateHandle ) }
# define STATETREE_TRACE_SCOPED_STATE_PHASE(StateHandle, Phase) \
FStateTreeInstanceDebugId ID_NAME = GetInstanceDebugId ( ) ; \
TRACE_STATETREE_PHASE_EVENT ( ID_NAME , Phase , EStateTreeTraceEventType : : Push , StateHandle ) \
ON_SCOPE_EXIT { TRACE_STATETREE_PHASE_EVENT ( ID_NAME , Phase , EStateTreeTraceEventType : : Pop , StateHandle ) }
2023-05-29 10:13:21 -04:00
# define STATETREE_TRACE_INSTANCE_EVENT(EventType) TRACE_STATETREE_INSTANCE_EVENT(GetInstanceDebugId(), GetStateTree(), *GetInstanceDescription(), EventType);
2023-12-13 10:21:13 -05:00
# define STATETREE_TRACE_INSTANCE_FRAME_EVENT(InstanceDebugId, Frame) TRACE_STATETREE_INSTANCE_FRAME_EVENT(InstanceDebugId, Frame);
# define STATETREE_TRACE_PHASE_BEGIN(Phase) TRACE_STATETREE_PHASE_EVENT(GetInstanceDebugId(), Phase, EStateTreeTraceEventType::Push, FStateTreeStateHandle::Invalid)
# define STATETREE_TRACE_PHASE_END(Phase) TRACE_STATETREE_PHASE_EVENT(GetInstanceDebugId(), Phase, EStateTreeTraceEventType::Pop, FStateTreeStateHandle::Invalid)
# define STATETREE_TRACE_ACTIVE_STATES_EVENT(ActiveFrames) TRACE_STATETREE_ACTIVE_STATES_EVENT(GetInstanceDebugId(), ActiveFrames);
2024-04-22 11:34:53 -04:00
# define STATETREE_TRACE_LOG_EVENT(TraceVerbosity, Format, ...) TRACE_STATETREE_LOG_EVENT(GetInstanceDebugId(), TraceVerbosity, Format, ##__VA_ARGS__)
2023-12-13 10:21:13 -05:00
# define STATETREE_TRACE_STATE_EVENT(StateHandle, EventType) TRACE_STATETREE_STATE_EVENT(GetInstanceDebugId(), StateHandle, EventType);
2023-05-29 10:13:21 -04:00
# define STATETREE_TRACE_TASK_EVENT(Index, DataView, EventType, Status) TRACE_STATETREE_TASK_EVENT(GetInstanceDebugId(), FStateTreeIndex16(Index), DataView, EventType, Status);
2023-08-07 18:06:40 -04:00
# define STATETREE_TRACE_EVALUATOR_EVENT(Index, DataView, EventType) TRACE_STATETREE_EVALUATOR_EVENT(GetInstanceDebugId(), FStateTreeIndex16(Index), DataView, EventType);
2023-11-22 04:08:33 -05:00
# define STATETREE_TRACE_CONDITION_EVENT(Index, DataView, EventType) TRACE_STATETREE_CONDITION_EVENT(GetInstanceDebugId(), FStateTreeIndex16(Index), DataView, EventType);
2023-08-04 14:55:23 -04:00
# define STATETREE_TRACE_TRANSITION_EVENT(Source, EventType) TRACE_STATETREE_TRANSITION_EVENT(GetInstanceDebugId(), Source, EventType);
2023-04-19 13:26:23 -04:00
# else
2023-05-29 10:13:21 -04:00
# define STATETREE_TRACE_SCOPED_PHASE(Phase)
2023-08-04 14:55:23 -04:00
# define STATETREE_TRACE_SCOPED_STATE(StateHandle)
# define STATETREE_TRACE_SCOPED_STATE_PHASE(StateHandle, Phase)
2023-05-29 10:13:21 -04:00
# define STATETREE_TRACE_INSTANCE_EVENT(EventType)
2023-12-13 10:21:13 -05:00
# define STATETREE_TRACE_INSTANCE_FRAME_EVENT(InstanceDebugId, Frame)
# define STATETREE_TRACE_PHASE_BEGIN(Phase)
# define STATETREE_TRACE_PHASE_END(Phase)
# define STATETREE_TRACE_ACTIVE_STATES_EVENT(ActiveFrames)
2024-04-22 11:34:53 -04:00
# define STATETREE_TRACE_LOG_EVENT(TraceVerbosity, Format, ...)
2023-05-23 10:46:16 -04:00
# define STATETREE_TRACE_STATE_EVENT(StateHandle, EventType)
2023-04-19 13:26:23 -04:00
# define STATETREE_TRACE_TASK_EVENT(Index, DataView, EventType, Status)
2023-08-07 18:06:40 -04:00
# define STATETREE_TRACE_EVALUATOR_EVENT(Index, DataView, EventType)
2023-05-23 10:46:16 -04:00
# define STATETREE_TRACE_CONDITION_EVENT(Index, DataView, EventType)
2023-08-04 14:55:23 -04:00
# define STATETREE_TRACE_TRANSITION_EVENT(Source, EventType)
2024-05-02 11:47:26 -04:00
# endif // WITH_STATETREE_TRACE
2023-04-19 13:26:23 -04:00
2022-05-31 04:51:18 -04:00
namespace UE : : StateTree
{
2022-09-01 09:06:53 -04:00
constexpr int32 DebugIndentSize = 2 ; // Debug printing indent for hierarchical data.
} ; // UE::StateTree
2022-05-31 04:51:18 -04:00
2023-12-13 10:21:13 -05:00
FStateTreeExecutionContext : : FCurrentlyProcessedFrameScope : : FCurrentlyProcessedFrameScope ( FStateTreeExecutionContext & InContext , const FStateTreeExecutionFrame * CurrentParentFrame , const FStateTreeExecutionFrame & CurrentFrame ) : Context ( InContext )
{
check ( CurrentFrame . StateTree ) ;
FStateTreeInstanceStorage * SharedInstanceDataStorage = & CurrentFrame . StateTree - > GetSharedInstanceData ( ) - > GetMutableStorage ( ) ;
SavedFrame = Context . CurrentlyProcessedFrame ;
SavedParentFrame = Context . CurrentlyProcessedParentFrame ;
SavedSharedInstanceDataStorage = Context . CurrentlyProcessedSharedInstanceStorage ;
Context . CurrentlyProcessedFrame = & CurrentFrame ;
Context . CurrentlyProcessedParentFrame = CurrentParentFrame ;
Context . CurrentlyProcessedSharedInstanceStorage = SharedInstanceDataStorage ;
STATETREE_TRACE_INSTANCE_FRAME_EVENT ( Context . GetInstanceDebugId ( ) , Context . CurrentlyProcessedFrame ) ;
}
FStateTreeExecutionContext : : FCurrentlyProcessedFrameScope : : ~ FCurrentlyProcessedFrameScope ( )
{
Context . CurrentlyProcessedFrame = SavedFrame ;
Context . CurrentlyProcessedParentFrame = SavedParentFrame ;
Context . CurrentlyProcessedSharedInstanceStorage = SavedSharedInstanceDataStorage ;
if ( Context . CurrentlyProcessedFrame )
{
STATETREE_TRACE_INSTANCE_FRAME_EVENT ( Context . GetInstanceDebugId ( ) , Context . CurrentlyProcessedFrame ) ;
}
}
2024-06-10 15:59:27 -04:00
FStateTreeExecutionContext : : FStateTreeExecutionContext ( UObject & InOwner , const UStateTree & InStateTree , FStateTreeInstanceData & InInstanceData , const FOnCollectStateTreeExternalData & InCollectExternalDataDelegate , const EStateTreeRecordTransitions RecordTransitions )
2022-09-23 20:02:42 -04:00
: Owner ( InOwner )
2023-11-22 04:08:33 -05:00
, RootStateTree ( InStateTree )
2022-09-23 20:02:42 -04:00
, InstanceData ( InInstanceData )
2023-12-13 06:34:27 -05:00
, CollectExternalDataDelegate ( InCollectExternalDataDelegate )
2022-05-31 04:51:18 -04:00
{
2022-09-23 20:02:42 -04:00
if ( InStateTree . IsReadyToRun ( ) )
{
// Initialize data views for all possible items.
2023-12-13 06:34:27 -05:00
ContextAndExternalDataViews . SetNum ( RootStateTree . GetNumContextDataViews ( ) ) ;
2023-01-23 12:48:04 -05:00
2023-11-22 04:08:33 -05:00
InstanceDataStorage = & InstanceData . GetMutableStorage ( ) ;
check ( InstanceDataStorage ) ;
2024-04-12 06:04:01 -04:00
EventQueue = InstanceData . GetSharedMutableEventQueue ( ) ;
2024-06-10 15:59:27 -04:00
bRecordTransitions = RecordTransitions = = EStateTreeRecordTransitions : : Yes ;
2022-09-23 20:02:42 -04:00
}
else
{
2023-04-12 07:59:16 -04:00
STATETREE_LOG ( Warning , TEXT ( " %hs: StateTree asset is not valid ('%s' using StateTree '%s') " ) ,
2023-11-22 04:08:33 -05:00
__FUNCTION__ , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & RootStateTree ) ) ;
2022-09-23 20:02:42 -04:00
}
2022-05-31 04:51:18 -04:00
}
2024-03-20 08:28:58 -04:00
FStateTreeExecutionContext : : FStateTreeExecutionContext ( const FStateTreeExecutionContext & InContextToCopy , const UStateTree & InStateTree , FStateTreeInstanceData & InInstanceData )
: FStateTreeExecutionContext ( InContextToCopy . Owner , InStateTree , InInstanceData , InContextToCopy . CollectExternalDataDelegate )
{
2024-04-03 07:16:44 -04:00
LinkedStateTreeOverrides = InContextToCopy . LinkedStateTreeOverrides ;
2024-03-20 08:28:58 -04:00
const bool bIsSameSchema = RootStateTree . GetSchema ( ) - > GetClass ( ) = = InContextToCopy . GetStateTree ( ) - > GetSchema ( ) - > GetClass ( ) ;
if ( bIsSameSchema )
{
for ( const FStateTreeExternalDataDesc & TargetDataDesc : GetContextDataDescs ( ) )
{
const int32 TargetIndex = TargetDataDesc . Handle . DataHandle . GetIndex ( ) ;
ContextAndExternalDataViews [ TargetIndex ] = InContextToCopy . ContextAndExternalDataViews [ TargetIndex ] ;
}
2024-04-12 06:04:01 -04:00
EventQueue = InstanceData . GetSharedMutableEventQueue ( ) ;
2024-03-20 08:28:58 -04:00
}
else
{
STATETREE_LOG ( Error , TEXT ( " %hs: '%s' using StateTree '%s' trying to run subtree '%s' but their schemas don't match " ) ,
__FUNCTION__ , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( InContextToCopy . GetStateTree ( ) ) , * GetFullNameSafe ( & RootStateTree ) ) ;
}
}
2022-05-31 04:51:18 -04:00
FStateTreeExecutionContext : : ~ FStateTreeExecutionContext ( )
{
2023-12-13 06:34:27 -05:00
// Mark external data indices as invalid
2024-08-09 09:48:26 -04:00
FStateTreeExecutionState & Exec = InstanceData . GetMutableStorage ( ) . GetMutableExecutionState ( ) ;
2023-12-13 06:34:27 -05:00
for ( FStateTreeExecutionFrame & Frame : Exec . ActiveFrames )
{
Frame . ExternalDataBaseIndex = { } ;
}
2022-05-31 04:51:18 -04:00
}
2023-12-13 06:34:27 -05:00
void FStateTreeExecutionContext : : SetCollectExternalDataCallback ( const FOnCollectStateTreeExternalData & Callback )
{
CollectExternalDataDelegate = Callback ;
}
2024-03-28 05:18:19 -04:00
void FStateTreeExecutionContext : : SetLinkedStateTreeOverrides ( const FStateTreeReferenceOverrides * InLinkedStateTreeOverrides )
{
LinkedStateTreeOverrides = InLinkedStateTreeOverrides ;
}
const FStateTreeReference * FStateTreeExecutionContext : : GetLinkedStateTreeOverrideForTag ( const FGameplayTag StateTag ) const
{
if ( ! LinkedStateTreeOverrides )
{
return nullptr ;
}
for ( const FStateTreeReferenceOverrideItem & Item : LinkedStateTreeOverrides - > GetOverrideItems ( ) )
{
if ( Item . GetStateTag ( ) = = StateTag )
{
return & Item . GetStateTreeReference ( ) ;
}
}
return nullptr ;
}
2023-12-13 06:34:27 -05:00
bool FStateTreeExecutionContext : : AreContextDataViewsValid ( ) const
2022-09-01 09:06:53 -04:00
{
2022-09-23 20:02:42 -04:00
if ( ! IsValid ( ) )
{
return false ;
}
2022-09-01 09:06:53 -04:00
bool bResult = true ;
2023-12-13 06:34:27 -05:00
2023-11-22 04:08:33 -05:00
for ( const FStateTreeExternalDataDesc & DataDesc : RootStateTree . GetContextDataDescs ( ) )
2022-09-01 09:06:53 -04:00
{
2023-12-13 06:34:27 -05:00
const FStateTreeDataView & DataView = ContextAndExternalDataViews [ DataDesc . Handle . DataHandle . GetIndex ( ) ] ;
2022-09-01 09:06:53 -04:00
2024-04-17 07:49:42 -04:00
// Required items must have valid pointer of the expected type.
if ( DataDesc . Requirement = = EStateTreeExternalDataRequirement : : Required )
2022-09-01 09:06:53 -04:00
{
2024-04-17 07:49:42 -04:00
if ( ! DataView . IsValid ( ) | | ! DataDesc . IsCompatibleWith ( DataView ) )
{
bResult = false ;
break ;
}
}
else // Optional items must have the expected type if they are set.
{
if ( DataView . IsValid ( ) & & ! DataDesc . IsCompatibleWith ( DataView ) )
{
bResult = false ;
break ;
}
2022-09-01 09:06:53 -04:00
}
}
return bResult ;
}
2023-12-13 06:34:27 -05:00
bool FStateTreeExecutionContext : : SetContextDataByName ( const FName Name , FStateTreeDataView DataView )
{
const FStateTreeExternalDataDesc * Desc = RootStateTree . GetContextDataDescs ( ) . FindByPredicate ( [ & Name ] ( const FStateTreeExternalDataDesc & Desc )
{
return Desc . Name = = Name ;
} ) ;
if ( Desc )
{
ContextAndExternalDataViews [ Desc - > Handle . DataHandle . GetIndex ( ) ] = DataView ;
return true ;
}
return false ;
}
2024-04-23 14:17:24 -04:00
EStateTreeRunStatus FStateTreeExecutionContext : : Start ( const FInstancedPropertyBag * InitialParameters , int32 RandomSeed )
2022-05-31 04:51:18 -04:00
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_Start ) ;
2022-09-23 20:02:42 -04:00
if ( ! IsValid ( ) )
2022-05-31 04:51:18 -04:00
{
2023-04-12 07:59:16 -04:00
STATETREE_LOG ( Warning , TEXT ( " %hs: StateTree context is not initialized properly ('%s' using StateTree '%s') " ) ,
2023-11-22 04:08:33 -05:00
__FUNCTION__ , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & RootStateTree ) ) ;
2022-05-31 04:51:18 -04:00
return EStateTreeRunStatus : : Failed ;
}
2024-01-30 03:18:31 -05:00
FStateTreeExecutionState & Exec = GetExecState ( ) ;
if ( ! ensureMsgf ( Exec . CurrentPhase = = EStateTreeUpdatePhase : : Unset , TEXT ( " %hs can't be called while already in %s ('%s' using StateTree '%s'). " ) ,
__FUNCTION__ , * UEnum : : GetDisplayValueAsText ( Exec . CurrentPhase ) . ToString ( ) , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & RootStateTree ) ) )
2023-09-22 09:53:34 -04:00
{
2024-01-30 03:18:31 -05:00
return EStateTreeRunStatus : : Failed ;
}
2023-09-22 09:53:34 -04:00
2024-01-30 03:18:31 -05:00
// Stop if still running previous state.
if ( Exec . TreeRunStatus = = EStateTreeRunStatus : : Running )
{
Stop ( ) ;
2023-09-22 09:53:34 -04:00
}
2022-11-23 09:22:14 -05:00
2023-01-10 15:44:28 -05:00
// Initialize instance data. No active states yet, so we'll initialize the evals and global tasks.
InstanceData . Reset ( ) ;
2023-11-22 04:08:33 -05:00
2024-01-22 05:38:03 -05:00
if ( ! InitialParameters | | ! SetGlobalParameters ( * InitialParameters ) )
{
SetGlobalParameters ( RootStateTree . GetDefaultParameters ( ) ) ;
}
2023-11-22 04:08:33 -05:00
// Initialize for the init frame.
FStateTreeExecutionFrame & InitFrame = Exec . ActiveFrames . AddDefaulted_GetRef ( ) ;
InitFrame . StateTree = & RootStateTree ;
InitFrame . RootState = FStateTreeStateHandle : : Root ;
InitFrame . ActiveStates = { } ;
InitFrame . bIsGlobalFrame = true ;
UpdateInstanceData ( { } , Exec . ActiveFrames ) ;
2024-04-23 14:17:24 -04:00
Exec . RandomStream . Initialize ( RandomSeed = = - 1 ? FPlatformTime : : Cycles ( ) : RandomSeed ) ;
2022-09-07 17:12:18 -04:00
2023-12-13 06:34:27 -05:00
if ( ! CollectActiveExternalData ( ) )
{
STATETREE_LOG ( Warning , TEXT ( " %hs: Failed to collect external data ('%s' using StateTree '%s') " ) ,
__FUNCTION__ , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & RootStateTree ) ) ;
return EStateTreeRunStatus : : Failed ;
}
2023-05-29 10:13:21 -04:00
// Must sent instance creation event first
STATETREE_TRACE_INSTANCE_EVENT ( EStateTreeTraceEventType : : Push ) ;
// Set scoped phase only for properly initialized context with valid Instance data
// since we need it to output the InstanceId
STATETREE_TRACE_SCOPED_PHASE ( EStateTreeUpdatePhase : : StartTree ) ;
2023-09-22 09:53:34 -04:00
2024-05-27 15:15:55 -04:00
STATETREE_LOG ( VeryVerbose , TEXT ( " %hs: Starting State Tree %s on owner '%s'. " ) ,
__FUNCTION__ , * GetFullNameSafe ( & RootStateTree ) , * GetNameSafe ( & Owner ) ) ;
2023-09-22 09:53:34 -04:00
// From this point any calls to Stop should be deferred.
2023-11-22 04:08:33 -05:00
Exec . CurrentPhase = EStateTreeUpdatePhase : : StartTree ;
2023-09-22 09:53:34 -04:00
2023-01-10 15:44:28 -05:00
// Start evaluators and global tasks. Fail the execution if any global task fails.
FStateTreeIndex16 LastInitializedTaskIndex ;
2023-06-05 06:33:07 -04:00
const EStateTreeRunStatus GlobalTasksRunStatus = StartEvaluatorsAndGlobalTasks ( LastInitializedTaskIndex ) ;
2023-09-22 09:53:34 -04:00
if ( GlobalTasksRunStatus = = EStateTreeRunStatus : : Running )
2023-01-10 15:44:28 -05:00
{
2023-09-22 09:53:34 -04:00
// First tick.
// Tasks are not ticked here, since their behavior is that EnterState() (called above) is treated as a tick.
TickEvaluatorsAndGlobalTasks ( 0.0f , /*bTickGlobalTasks*/ false ) ;
2022-05-31 04:51:18 -04:00
2023-09-22 09:53:34 -04:00
// Initialize to unset running state.
2023-11-22 04:08:33 -05:00
Exec . TreeRunStatus = EStateTreeRunStatus : : Running ;
Exec . LastTickStatus = EStateTreeRunStatus : : Unset ;
2022-05-31 04:51:18 -04:00
2023-09-22 09:53:34 -04:00
static const FStateTreeStateHandle RootState = FStateTreeStateHandle ( 0 ) ;
2022-05-31 04:51:18 -04:00
2024-03-11 04:54:44 -04:00
FStateSelectionResult StateSelectionResult ;
if ( SelectState ( InitFrame , RootState , StateSelectionResult ) )
2022-05-31 04:51:18 -04:00
{
2024-03-11 04:54:44 -04:00
check ( StateSelectionResult . ContainsFrames ( ) ) ;
if ( StateSelectionResult . GetSelectedFrames ( ) . Last ( ) . ActiveStates . Last ( ) . IsCompletionState ( ) )
2022-05-31 04:51:18 -04:00
{
2023-11-22 04:08:33 -05:00
// Transition to a terminal state (succeeded/failed).
2023-09-22 09:53:34 -04:00
STATETREE_LOG ( Warning , TEXT ( " %hs: Tree %s at StateTree start on '%s' using StateTree '%s'. " ) ,
2024-03-11 04:54:44 -04:00
__FUNCTION__ , StateSelectionResult . GetSelectedFrames ( ) . Last ( ) . ActiveStates . Last ( ) = = FStateTreeStateHandle : : Succeeded ? TEXT ( " succeeded " ) : TEXT ( " failed " ) , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & RootStateTree ) ) ;
Exec . TreeRunStatus = StateSelectionResult . GetSelectedFrames ( ) . Last ( ) . ActiveStates . Last ( ) . ToCompletionStatus ( ) ;
2023-09-22 09:53:34 -04:00
}
else
{
// Enter state tasks can fail/succeed, treat it same as tick.
FStateTreeTransitionResult Transition ;
Transition . TargetState = RootState ;
2023-11-22 04:08:33 -05:00
Transition . CurrentRunStatus = Exec . LastTickStatus ;
2024-03-11 04:54:44 -04:00
Transition . NextActiveFrames = StateSelectionResult . GetSelectedFrames ( ) ; // Enter state will update Exec.ActiveFrames.
Transition . NextActiveFrameEvents = StateSelectionResult . GetFramesStateSelectionEvents ( ) ;
2023-09-22 09:53:34 -04:00
const EStateTreeRunStatus LastTickStatus = EnterState ( Transition ) ;
2023-11-22 04:08:33 -05:00
Exec . LastTickStatus = LastTickStatus ;
2023-09-22 09:53:34 -04:00
// Report state completed immediately.
2023-11-22 04:08:33 -05:00
if ( Exec . LastTickStatus ! = EStateTreeRunStatus : : Running )
2023-09-22 09:53:34 -04:00
{
StateCompleted ( ) ;
}
2022-05-31 04:51:18 -04:00
}
}
2023-11-22 04:08:33 -05:00
if ( Exec . LastTickStatus = = EStateTreeRunStatus : : Unset )
2023-09-22 09:53:34 -04:00
{
// Should not happen. This may happen if initial state could not be selected.
STATETREE_LOG ( Error , TEXT ( " %hs: Failed to select initial state on '%s' using StateTree '%s'. This should not happen, check that the StateTree logic can always select a state at start. " ) ,
2023-11-22 04:08:33 -05:00
__FUNCTION__ , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & RootStateTree ) ) ;
Exec . TreeRunStatus = EStateTreeRunStatus : : Failed ;
2023-09-22 09:53:34 -04:00
}
}
else
2022-05-31 04:51:18 -04:00
{
2023-09-22 09:53:34 -04:00
StopEvaluatorsAndGlobalTasks ( GlobalTasksRunStatus , LastInitializedTaskIndex ) ;
STATETREE_LOG ( VeryVerbose , TEXT ( " %hs: Global tasks completed the StateTree %s on start in status '%s'. " ) ,
2023-11-22 04:08:33 -05:00
__FUNCTION__ , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & RootStateTree ) , * UEnum : : GetDisplayValueAsText ( GlobalTasksRunStatus ) . ToString ( ) ) ;
2023-09-22 09:53:34 -04:00
2023-11-22 04:08:33 -05:00
// No active states or global tasks anymore, reset frames.
Exec . ActiveFrames . Reset ( ) ;
2023-09-22 09:53:34 -04:00
// We are not considered as running yet so we only set the status without requiring a stop.
2023-11-22 04:08:33 -05:00
Exec . TreeRunStatus = GlobalTasksRunStatus ;
2022-05-31 04:51:18 -04:00
}
2023-09-22 09:53:34 -04:00
// Reset phase since we are now safe to stop.
2023-11-22 04:08:33 -05:00
Exec . CurrentPhase = EStateTreeUpdatePhase : : Unset ;
2023-09-22 09:53:34 -04:00
// Use local for resulting run state since Stop will reset the instance data.
2023-11-22 04:08:33 -05:00
EStateTreeRunStatus Result = Exec . TreeRunStatus ;
2023-09-22 09:53:34 -04:00
2023-11-22 04:08:33 -05:00
if ( Exec . RequestedStop ! = EStateTreeRunStatus : : Unset )
2023-09-22 09:53:34 -04:00
{
2024-04-22 11:34:53 -04:00
STATETREE_LOG_AND_TRACE ( VeryVerbose , Log , TEXT ( " Processing Deferred Stop " ) ) ;
2023-11-22 04:08:33 -05:00
Result = Stop ( Exec . RequestedStop ) ;
2023-09-22 09:53:34 -04:00
}
return Result ;
2022-05-31 04:51:18 -04:00
}
2023-09-22 09:53:34 -04:00
EStateTreeRunStatus FStateTreeExecutionContext : : Stop ( EStateTreeRunStatus CompletionStatus )
2022-05-31 04:51:18 -04:00
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_Stop ) ;
2022-09-23 20:02:42 -04:00
if ( ! IsValid ( ) )
2022-05-31 04:51:18 -04:00
{
2023-04-12 07:59:16 -04:00
STATETREE_LOG ( Warning , TEXT ( " %hs: StateTree context is not initialized properly ('%s' using StateTree '%s') " ) ,
2023-11-22 04:08:33 -05:00
__FUNCTION__ , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & RootStateTree ) ) ;
2022-05-31 04:51:18 -04:00
return EStateTreeRunStatus : : Failed ;
}
2022-11-23 09:22:14 -05:00
2023-12-13 06:34:27 -05:00
if ( ! CollectActiveExternalData ( ) )
{
STATETREE_LOG ( Warning , TEXT ( " %hs: Failed to collect external data ('%s' using StateTree '%s') " ) ,
__FUNCTION__ , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & RootStateTree ) ) ;
return EStateTreeRunStatus : : Failed ;
}
2023-05-29 10:13:21 -04:00
// Set scoped phase only for properly initialized context with valid Instance data
// since we need it to output the InstanceId
STATETREE_TRACE_SCOPED_PHASE ( EStateTreeUpdatePhase : : StopTree ) ;
2023-09-22 09:53:34 -04:00
// Make sure that we return a valid completion status (i.e. Succeeded, Failed or Stopped)
if ( CompletionStatus = = EStateTreeRunStatus : : Unset
| | CompletionStatus = = EStateTreeRunStatus : : Running )
{
CompletionStatus = EStateTreeRunStatus : : Stopped ;
}
FStateTreeExecutionState & Exec = GetExecState ( ) ;
// A reentrant call to Stop or a call from Start or Tick must be deferred.
if ( Exec . CurrentPhase ! = EStateTreeUpdatePhase : : Unset )
{
2024-04-22 11:34:53 -04:00
STATETREE_LOG_AND_TRACE ( VeryVerbose , Log , TEXT ( " Deferring Stop at end of %s " ) , * UEnum : : GetDisplayValueAsText ( Exec . CurrentPhase ) . ToString ( ) ) ;
2023-09-22 09:53:34 -04:00
Exec . RequestedStop = CompletionStatus ;
return EStateTreeRunStatus : : Running ;
}
// No need to clear on exit since we reset all the instance data before leaving the function.
Exec . CurrentPhase = EStateTreeUpdatePhase : : StopTree ;
2023-01-10 15:44:28 -05:00
EStateTreeRunStatus Result = Exec . TreeRunStatus ;
2022-05-31 04:51:18 -04:00
// Exit states if still in some valid state.
2023-06-05 06:33:07 -04:00
if ( Exec . TreeRunStatus = = EStateTreeRunStatus : : Running )
2022-05-31 04:51:18 -04:00
{
// Transition to Succeeded state.
FStateTreeTransitionResult Transition ;
2023-06-05 06:33:07 -04:00
Transition . TargetState = FStateTreeStateHandle : : FromCompletionStatus ( CompletionStatus ) ;
2024-06-25 02:37:11 -04:00
Transition . CurrentRunStatus = CompletionStatus ;
2023-11-24 02:48:28 -05:00
ExitState ( Transition ) ;
2022-05-31 04:51:18 -04:00
2023-11-22 04:08:33 -05:00
// No active states or global tasks anymore, reset frames.
Exec . ActiveFrames . Reset ( ) ;
2023-06-05 06:33:07 -04:00
Result = CompletionStatus ;
2022-05-31 04:51:18 -04:00
}
2023-03-14 13:35:46 -04:00
// Trace before resetting the instance data since it is required to provide all the event information
2023-12-13 10:21:13 -05:00
STATETREE_TRACE_ACTIVE_STATES_EVENT ( { } ) ;
2023-05-29 10:13:21 -04:00
STATETREE_TRACE_INSTANCE_EVENT ( EStateTreeTraceEventType : : Pop ) ;
2023-03-14 13:35:46 -04:00
2022-05-31 04:51:18 -04:00
// Destruct all allocated instance data (does not shrink the buffer). This will invalidate Exec too.
InstanceData . Reset ( ) ;
2024-04-12 06:04:01 -04:00
// External data needs to be recollected if this exec context is reused.
bActiveExternalDataCollected = false ;
2022-05-31 04:51:18 -04:00
return Result ;
}
2024-04-12 06:04:01 -04:00
EStateTreeRunStatus FStateTreeExecutionContext : : TickPrelude ( )
2022-05-31 04:51:18 -04:00
{
2022-09-23 20:02:42 -04:00
if ( ! IsValid ( ) )
2022-05-31 04:51:18 -04:00
{
2023-04-12 07:59:16 -04:00
STATETREE_LOG ( Warning , TEXT ( " %hs: StateTree context is not initialized properly ('%s' using StateTree '%s') " ) ,
2023-11-22 04:08:33 -05:00
__FUNCTION__ , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & RootStateTree ) ) ;
2022-05-31 04:51:18 -04:00
return EStateTreeRunStatus : : Failed ;
}
2022-09-23 20:02:42 -04:00
2023-12-13 06:34:27 -05:00
if ( ! CollectActiveExternalData ( ) )
{
STATETREE_LOG ( Warning , TEXT ( " %hs: Failed to collect external data ('%s' using StateTree '%s') " ) ,
__FUNCTION__ , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & RootStateTree ) ) ;
return EStateTreeRunStatus : : Failed ;
}
2023-11-22 04:08:33 -05:00
FStateTreeExecutionState & Exec = GetExecState ( ) ;
2023-09-22 09:53:34 -04:00
// No ticking if the tree is done or stopped.
2023-11-22 04:08:33 -05:00
if ( Exec . TreeRunStatus ! = EStateTreeRunStatus : : Running )
2022-05-31 04:51:18 -04:00
{
2023-11-22 04:08:33 -05:00
return Exec . TreeRunStatus ;
2022-05-31 04:51:18 -04:00
}
2023-11-22 04:08:33 -05:00
if ( ! ensureMsgf ( Exec . CurrentPhase = = EStateTreeUpdatePhase : : Unset , TEXT ( " %hs can't be called while already in %s ('%s' using StateTree '%s'). " ) ,
__FUNCTION__ , * UEnum : : GetDisplayValueAsText ( Exec . CurrentPhase ) . ToString ( ) , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & RootStateTree ) ) )
2023-09-22 09:53:34 -04:00
{
return EStateTreeRunStatus : : Failed ;
}
// From this point any calls to Stop should be deferred.
2023-11-22 04:08:33 -05:00
Exec . CurrentPhase = EStateTreeUpdatePhase : : TickStateTree ;
2024-04-12 06:04:01 -04:00
return EStateTreeRunStatus : : Running ;
}
EStateTreeRunStatus FStateTreeExecutionContext : : TickPostlude ( )
{
FStateTreeExecutionState & Exec = GetExecState ( ) ;
// Reset phase since we are now safe to stop.
Exec . CurrentPhase = EStateTreeUpdatePhase : : Unset ;
// Use local for resulting run state since Stop will reset the instance data.
EStateTreeRunStatus Result = Exec . TreeRunStatus ;
2023-09-22 09:53:34 -04:00
2024-04-12 06:04:01 -04:00
if ( Exec . RequestedStop ! = EStateTreeRunStatus : : Unset )
{
2024-04-22 11:34:53 -04:00
STATETREE_LOG_AND_TRACE ( VeryVerbose , Log , TEXT ( " Processing Deferred Stop " ) ) ;
2024-04-12 06:04:01 -04:00
Result = Stop ( Exec . RequestedStop ) ;
}
return Result ;
}
EStateTreeRunStatus FStateTreeExecutionContext : : Tick ( const float DeltaTime )
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_Tick ) ;
const EStateTreeRunStatus PreludeResult = TickPrelude ( ) ;
if ( PreludeResult ! = EStateTreeRunStatus : : Running )
{
return PreludeResult ;
}
TickUpdateTasksInternal ( DeltaTime ) ;
TickTriggerTransitionsInternal ( ) ;
return TickPostlude ( ) ;
}
EStateTreeRunStatus FStateTreeExecutionContext : : TickUpdateTasks ( const float DeltaTime )
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_Tick ) ;
const EStateTreeRunStatus PreludeResult = TickPrelude ( ) ;
if ( PreludeResult ! = EStateTreeRunStatus : : Running )
{
return PreludeResult ;
}
TickUpdateTasksInternal ( DeltaTime ) ;
return TickPostlude ( ) ;
}
EStateTreeRunStatus FStateTreeExecutionContext : : TickTriggerTransitions ( )
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_Tick ) ;
const EStateTreeRunStatus PreludeResult = TickPrelude ( ) ;
if ( PreludeResult ! = EStateTreeRunStatus : : Running )
{
return PreludeResult ;
}
TickTriggerTransitionsInternal ( ) ;
return TickPostlude ( ) ;
}
void FStateTreeExecutionContext : : TickUpdateTasksInternal ( const float DeltaTime )
{
FStateTreeExecutionState & Exec = GetExecState ( ) ;
2023-09-22 09:53:34 -04:00
2022-12-02 07:57:31 -05:00
// Update the delayed transitions.
2023-11-22 04:08:33 -05:00
for ( FStateTreeTransitionDelayedState & DelayedState : Exec . DelayedTransitions )
2022-05-31 04:51:18 -04:00
{
2022-12-02 07:57:31 -05:00
DelayedState . TimeLeft - = DeltaTime ;
2022-05-31 04:51:18 -04:00
}
2023-01-23 12:48:04 -05:00
2023-01-10 15:44:28 -05:00
// Tick global evaluators and tasks.
const EStateTreeRunStatus EvalAndGlobalTaskStatus = TickEvaluatorsAndGlobalTasks ( DeltaTime ) ;
2023-09-22 09:53:34 -04:00
if ( EvalAndGlobalTaskStatus = = EStateTreeRunStatus : : Running )
2023-01-10 15:44:28 -05:00
{
2023-11-22 04:08:33 -05:00
if ( Exec . LastTickStatus = = EStateTreeRunStatus : : Running )
2022-05-31 04:51:18 -04:00
{
2023-09-22 09:53:34 -04:00
// Tick tasks on active states.
2023-11-22 04:08:33 -05:00
Exec . LastTickStatus = TickTasks ( DeltaTime ) ;
2023-01-23 12:48:04 -05:00
2022-05-31 04:51:18 -04:00
// Report state completed immediately.
2023-11-22 04:08:33 -05:00
if ( Exec . LastTickStatus ! = EStateTreeRunStatus : : Running )
2022-05-31 04:51:18 -04:00
{
2022-09-23 20:02:42 -04:00
StateCompleted ( ) ;
2022-05-31 04:51:18 -04:00
}
}
}
2023-09-22 09:53:34 -04:00
else
2022-05-31 04:51:18 -04:00
{
2024-04-22 11:34:53 -04:00
STATETREE_TRACE_LOG_EVENT ( Log , TEXT ( " Global tasks completed (%s), stopping the tree " ) , * UEnum : : GetDisplayValueAsText ( EvalAndGlobalTaskStatus ) . ToString ( ) ) ;
2023-11-22 04:08:33 -05:00
Exec . RequestedStop = EvalAndGlobalTaskStatus ;
2022-05-31 04:51:18 -04:00
}
2024-04-12 06:04:01 -04:00
}
2022-05-31 04:51:18 -04:00
2024-04-12 06:04:01 -04:00
void FStateTreeExecutionContext : : TickTriggerTransitionsInternal ( )
{
FStateTreeExecutionState & Exec = GetExecState ( ) ;
2022-09-01 09:06:53 -04:00
2024-04-12 06:04:01 -04:00
// If stop is requested, do not try to trigger transitions.
2023-11-22 04:08:33 -05:00
if ( Exec . RequestedStop ! = EStateTreeRunStatus : : Unset )
2023-09-22 09:53:34 -04:00
{
2024-04-12 06:04:01 -04:00
return ;
2023-09-22 09:53:34 -04:00
}
2024-04-12 06:04:01 -04:00
// The state selection is repeated up to MaxIteration time. This allows failed EnterState() to potentially find a new state immediately.
// This helps event driven StateTrees to not require another event/tick to find a suitable state.
static constexpr int32 MaxIterations = 5 ;
for ( int32 Iter = 0 ; Iter < MaxIterations ; Iter + + )
{
// Trigger conditional transitions or state succeed/failed transitions. First tick transition is handled here too.
if ( TriggerTransitions ( ) )
{
STATETREE_TRACE_SCOPED_PHASE ( EStateTreeUpdatePhase : : ApplyTransitions ) ;
STATETREE_TRACE_TRANSITION_EVENT ( NextTransitionSource , EStateTreeTraceEventType : : OnTransition ) ;
NextTransitionSource . Reset ( ) ;
ExitState ( NextTransition ) ;
// Tree succeeded or failed.
if ( NextTransition . TargetState . IsCompletionState ( ) )
{
// Transition to a terminal state (succeeded/failed), or default transition failed.
Exec . TreeRunStatus = NextTransition . TargetState . ToCompletionStatus ( ) ;
// Stop evaluators and global tasks.
StopEvaluatorsAndGlobalTasks ( Exec . TreeRunStatus ) ;
// No active states or global tasks anymore, reset frames.
Exec . ActiveFrames . Reset ( ) ;
break ;
}
// Enter state tasks can fail/succeed, treat it same as tick.
const EStateTreeRunStatus LastTickStatus = EnterState ( NextTransition ) ;
NextTransition . Reset ( ) ;
Exec . LastTickStatus = LastTickStatus ;
// Report state completed immediately.
if ( Exec . LastTickStatus ! = EStateTreeRunStatus : : Running )
{
StateCompleted ( ) ;
}
}
// Stop as soon as have found a running state.
if ( Exec . LastTickStatus = = EStateTreeRunStatus : : Running )
{
break ;
}
}
2022-05-31 04:51:18 -04:00
}
2022-12-16 05:45:13 -05:00
EStateTreeRunStatus FStateTreeExecutionContext : : GetStateTreeRunStatus ( ) const
{
if ( ! IsValid ( ) )
{
2023-04-12 07:59:16 -04:00
STATETREE_LOG ( Warning , TEXT ( " %hs: StateTree context is not initialized properly ('%s' using StateTree '%s') " ) ,
2023-11-22 04:08:33 -05:00
__FUNCTION__ , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & RootStateTree ) ) ;
2022-12-16 05:45:13 -05:00
return EStateTreeRunStatus : : Failed ;
}
2023-01-12 09:00:45 -05:00
if ( const FStateTreeExecutionState * Exec = InstanceData . GetExecutionState ( ) )
2022-12-16 05:45:13 -05:00
{
2023-01-12 09:00:45 -05:00
return Exec - > TreeRunStatus ;
2022-12-16 05:45:13 -05:00
}
2023-01-12 09:00:45 -05:00
return EStateTreeRunStatus : : Failed ;
2022-12-16 05:45:13 -05:00
}
2022-11-01 15:11:19 -04:00
void FStateTreeExecutionContext : : SendEvent ( const FGameplayTag Tag , const FConstStructView Payload , const FName Origin ) const
2022-09-01 09:06:53 -04:00
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_SendEvent ) ;
2022-09-23 20:02:42 -04:00
if ( ! IsValid ( ) )
2022-09-01 09:06:53 -04:00
{
2023-04-12 07:59:16 -04:00
STATETREE_LOG ( Warning , TEXT ( " %hs: StateTree context is not initialized properly ('%s' using StateTree '%s') " ) ,
2023-11-22 04:08:33 -05:00
__FUNCTION__ , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & RootStateTree ) ) ;
2022-09-01 09:06:53 -04:00
return ;
}
2022-09-23 20:02:42 -04:00
2024-04-22 11:34:53 -04:00
STATETREE_LOG_AND_TRACE ( Verbose , Log , TEXT ( " Send Event '%s' " ) , * Tag . ToString ( ) ) ;
2022-11-03 14:21:53 -04:00
2024-04-12 06:04:01 -04:00
FStateTreeEventQueue & LocalEventQueue = InstanceData . GetMutableEventQueue ( ) ;
LocalEventQueue . SendEvent ( & Owner , Tag , Payload , Origin ) ;
2022-09-01 09:06:53 -04:00
}
2023-01-23 12:48:04 -05:00
void FStateTreeExecutionContext : : RequestTransition ( const FStateTreeTransitionRequest & Request )
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_RequestTransition ) ;
if ( ! IsValid ( ) )
{
2023-04-12 07:59:16 -04:00
STATETREE_LOG ( Warning , TEXT ( " %hs: StateTree context is not initialized properly ('%s' using StateTree '%s') " ) ,
2023-11-22 04:08:33 -05:00
__FUNCTION__ , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & RootStateTree ) ) ;
2023-01-23 12:48:04 -05:00
return ;
}
2023-11-22 04:08:33 -05:00
FStateTreeExecutionState & Exec = GetExecState ( ) ;
2023-01-23 12:48:04 -05:00
if ( bAllowDirectTransitions )
{
2023-11-22 04:08:33 -05:00
checkf ( CurrentlyProcessedFrame , TEXT ( " Expecting CurrentlyProcessedFrame to be valid when called during TriggerTransitions(). " ) ) ;
STATETREE_LOG ( Verbose , TEXT ( " Request transition to '%s' at priority %s " ) , * GetSafeStateName ( * CurrentlyProcessedFrame , Request . TargetState ) , * UEnum : : GetDisplayValueAsText ( Request . Priority ) . ToString ( ) ) ;
if ( RequestTransition ( * CurrentlyProcessedFrame , Request . TargetState , Request . Priority ) )
2023-08-04 14:55:23 -04:00
{
NextTransitionSource = FStateTreeTransitionSource ( EStateTreeTransitionSourceType : : ExternalRequest , Request . TargetState , Request . Priority ) ;
}
2023-01-23 12:48:04 -05:00
}
else
{
2023-11-22 04:08:33 -05:00
const FStateTreeExecutionFrame * RootFrame = & Exec . ActiveFrames [ 0 ] ;
if ( CurrentlyProcessedFrame )
{
RootFrame = CurrentlyProcessedFrame ;
}
if ( ! RootFrame )
{
STATETREE_LOG ( Warning , TEXT ( " %hs: RequestTransition called on %s using StateTree %s without active state. Start() must be called before requesting transition. " ) ,
__FUNCTION__ , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & RootStateTree ) ) ;
return ;
}
STATETREE_LOG ( Verbose , TEXT ( " Request transition to '%s' at priority %s " ) , * GetSafeStateName ( * RootFrame , Request . TargetState ) , * UEnum : : GetDisplayValueAsText ( Request . Priority ) . ToString ( ) ) ;
2023-01-23 12:48:04 -05:00
FStateTreeTransitionRequest RequestWithSource = Request ;
2023-11-22 04:08:33 -05:00
RequestWithSource . SourceStateTree = RootFrame - > StateTree ;
RequestWithSource . SourceRootState = RootFrame - > ActiveStates [ 0 ] ;
2023-01-23 12:48:04 -05:00
RequestWithSource . SourceState = CurrentlyProcessedState ;
2023-11-22 04:08:33 -05:00
2023-01-23 12:48:04 -05:00
InstanceData . AddTransitionRequest ( & Owner , RequestWithSource ) ;
}
}
2024-05-02 11:47:26 -04:00
# if WITH_STATETREE_TRACE
2023-03-14 13:35:46 -04:00
FStateTreeInstanceDebugId FStateTreeExecutionContext : : GetInstanceDebugId ( ) const
{
FStateTreeInstanceDebugId & InstanceDebugId = GetExecState ( ) . InstanceDebugId ;
if ( ! InstanceDebugId . IsValid ( ) )
{
2023-03-16 11:43:35 -04:00
static std : : atomic < uint32 > SerialNumber = 0 ;
InstanceDebugId = FStateTreeInstanceDebugId ( GetTypeHash ( GetInstanceDescription ( ) ) , + + SerialNumber ) ;
2023-03-14 13:35:46 -04:00
}
return InstanceDebugId ;
}
2024-05-02 11:47:26 -04:00
# endif // WITH_STATETREE_TRACE
2023-03-14 13:35:46 -04:00
2023-11-22 04:08:33 -05:00
void FStateTreeExecutionContext : : UpdateInstanceData ( TConstArrayView < FStateTreeExecutionFrame > CurrentActiveFrames , TArrayView < FStateTreeExecutionFrame > NextActiveFrames )
2022-09-01 09:06:53 -04:00
{
2023-11-22 04:08:33 -05:00
// Estimate how many new instance data items we might have.
int32 EstimatedNumStructs = 0 ;
for ( int32 FrameIndex = 0 ; FrameIndex < NextActiveFrames . Num ( ) ; FrameIndex + + )
2022-05-31 04:51:18 -04:00
{
2023-11-22 04:08:33 -05:00
const FStateTreeExecutionFrame & NextFrame = NextActiveFrames [ FrameIndex ] ;
if ( NextFrame . bIsGlobalFrame )
2022-05-31 04:51:18 -04:00
{
2023-11-22 04:08:33 -05:00
EstimatedNumStructs + = NextFrame . StateTree - > NumGlobalInstanceData ;
2022-05-31 04:51:18 -04:00
}
2023-11-22 04:08:33 -05:00
// States
for ( int32 StateIndex = 0 ; StateIndex < NextFrame . ActiveStates . Num ( ) ; StateIndex + + )
2022-05-31 04:51:18 -04:00
{
2023-11-22 04:08:33 -05:00
const FStateTreeStateHandle StateHandle = NextFrame . ActiveStates [ StateIndex ] ;
const FCompactStateTreeState & State = NextFrame . StateTree - > States [ StateHandle . Index ] ;
EstimatedNumStructs + = State . InstanceDataNum ;
2023-11-17 11:42:17 -05:00
}
}
2023-11-17 07:41:08 -05:00
2023-11-22 04:08:33 -05:00
TArray < FConstStructView , TConcurrentLinearArrayAllocator < FDefaultBlockAllocationTag > > InstanceStructs ;
InstanceStructs . Reserve ( EstimatedNumStructs ) ;
2023-11-17 11:42:17 -05:00
2023-11-30 07:03:20 -05:00
TArray < FInstancedStruct * , TConcurrentLinearArrayAllocator < FDefaultBlockAllocationTag > > TempInstanceStructs ;
TempInstanceStructs . Reserve ( EstimatedNumStructs ) ;
2024-08-22 11:41:23 -04:00
TArray < FCompactStateTreeParameters , TFixedAllocator < FStateSelectionResult : : MaxExecutionFrames > > TempParams ;
2023-11-30 07:03:20 -05:00
TArrayView < FStateTreeTemporaryInstanceData > TempInstances = InstanceDataStorage - > GetMutableTemporaryInstances ( ) ;
auto FindInstanceTempData = [ & TempInstances ] ( const FStateTreeExecutionFrame & Frame , FStateTreeDataHandle DataHandle )
{
FStateTreeTemporaryInstanceData * TempData = TempInstances . FindByPredicate ( [ & Frame , & DataHandle ] ( const FStateTreeTemporaryInstanceData & Data )
{
return Data . StateTree = = Frame . StateTree & & Data . RootState = = Frame . RootState & & Data . DataHandle = = DataHandle ;
} ) ;
return TempData ? & TempData - > Instance : nullptr ;
} ;
2023-11-22 04:08:33 -05:00
// Find next instance data sources and find common/existing section of instance data at start.
int32 CurrentGlobalInstanceIndexBase = 0 ;
int32 NumCommonInstanceData = 0 ;
const UStruct * NextStateParameterDataStruct = nullptr ;
2023-11-30 07:03:20 -05:00
FStateTreeDataHandle NextStateParameterDataHandle = FStateTreeDataHandle : : Invalid ;
2023-11-22 04:08:33 -05:00
2024-01-22 05:38:03 -05:00
FStateTreeDataHandle CurrentGlobalParameterDataHandle = FStateTreeDataHandle ( EStateTreeDataSourceType : : GlobalParameterData ) ;
2023-11-30 07:03:20 -05:00
2023-11-22 04:08:33 -05:00
bool bAreCommon = true ;
for ( int32 FrameIndex = 0 ; FrameIndex < NextActiveFrames . Num ( ) ; FrameIndex + + )
{
const bool bIsCurrentFrameValid = CurrentActiveFrames . IsValidIndex ( FrameIndex )
& & CurrentActiveFrames [ FrameIndex ] . IsSameFrame ( NextActiveFrames [ FrameIndex ] ) ;
bAreCommon & = bIsCurrentFrameValid ;
const FStateTreeExecutionFrame * CurrentFrame = bIsCurrentFrameValid ? & CurrentActiveFrames [ FrameIndex ] : nullptr ;
FStateTreeExecutionFrame & NextFrame = NextActiveFrames [ FrameIndex ] ;
check ( NextFrame . StateTree ) ;
if ( NextFrame . bIsGlobalFrame )
2023-11-17 11:42:17 -05:00
{
2023-11-30 07:03:20 -05:00
// Handle global tree parameters
if ( NextStateParameterDataHandle . IsValid ( ) )
{
// Point to the parameter block set by linked state.
check ( NextStateParameterDataStruct = = NextFrame . StateTree - > GetDefaultParameters ( ) . GetPropertyBagStruct ( ) ) ;
CurrentGlobalParameterDataHandle = NextStateParameterDataHandle ;
NextStateParameterDataHandle = FStateTreeDataHandle : : Invalid ; // Mark as used.
}
2023-11-22 04:08:33 -05:00
// Global Evals
const int32 BaseIndex = InstanceStructs . Num ( ) ;
CurrentGlobalInstanceIndexBase = BaseIndex ;
InstanceStructs . AddDefaulted ( NextFrame . StateTree - > NumGlobalInstanceData ) ;
2023-11-30 07:03:20 -05:00
TempInstanceStructs . AddZeroed ( NextFrame . StateTree - > NumGlobalInstanceData ) ;
2023-11-22 04:08:33 -05:00
for ( int32 EvalIndex = NextFrame . StateTree - > EvaluatorsBegin ; EvalIndex < ( NextFrame . StateTree - > EvaluatorsBegin + NextFrame . StateTree - > EvaluatorsNum ) ; EvalIndex + + )
2023-11-17 11:42:17 -05:00
{
2023-11-22 04:08:33 -05:00
const FStateTreeEvaluatorBase & Eval = NextFrame . StateTree - > Nodes [ EvalIndex ] . Get < const FStateTreeEvaluatorBase > ( ) ;
const FConstStructView EvalInstanceData = NextFrame . StateTree - > DefaultInstanceData . GetStruct ( Eval . InstanceTemplateIndex . Get ( ) ) ;
InstanceStructs [ BaseIndex + Eval . InstanceDataHandle . GetIndex ( ) ] = EvalInstanceData ;
2023-11-30 07:03:20 -05:00
if ( ! bAreCommon )
{
TempInstanceStructs [ BaseIndex + Eval . InstanceDataHandle . GetIndex ( ) ] = FindInstanceTempData ( NextFrame , Eval . InstanceDataHandle ) ;
}
2023-11-17 11:42:17 -05:00
}
2023-11-22 04:08:33 -05:00
// Global tasks
for ( int32 TaskIndex = NextFrame . StateTree - > GlobalTasksBegin ; TaskIndex < ( NextFrame . StateTree - > GlobalTasksBegin + NextFrame . StateTree - > GlobalTasksNum ) ; TaskIndex + + )
2023-11-17 11:42:17 -05:00
{
2023-11-22 04:08:33 -05:00
const FStateTreeTaskBase & Task = NextFrame . StateTree - > Nodes [ TaskIndex ] . Get < const FStateTreeTaskBase > ( ) ;
const FConstStructView TaskInstanceData = NextFrame . StateTree - > DefaultInstanceData . GetStruct ( Task . InstanceTemplateIndex . Get ( ) ) ;
InstanceStructs [ BaseIndex + Task . InstanceDataHandle . GetIndex ( ) ] = TaskInstanceData ;
2023-11-30 07:03:20 -05:00
if ( ! bAreCommon )
{
TempInstanceStructs [ BaseIndex + Task . InstanceDataHandle . GetIndex ( ) ] = FindInstanceTempData ( NextFrame , Task . InstanceDataHandle ) ;
}
2023-11-22 04:08:33 -05:00
}
if ( bAreCommon )
{
NumCommonInstanceData = InstanceStructs . Num ( ) ;
2023-11-17 11:42:17 -05:00
}
}
2023-11-22 04:08:33 -05:00
// States
const int32 BaseIndex = InstanceStructs . Num ( ) ;
2023-11-30 07:03:20 -05:00
NextFrame . GlobalParameterDataHandle = CurrentGlobalParameterDataHandle ;
2023-11-22 04:08:33 -05:00
NextFrame . GlobalInstanceIndexBase = FStateTreeIndex16 ( CurrentGlobalInstanceIndexBase ) ;
NextFrame . ActiveInstanceIndexBase = FStateTreeIndex16 ( BaseIndex ) ;
for ( int32 StateIndex = 0 ; StateIndex < NextFrame . ActiveStates . Num ( ) ; StateIndex + + )
2023-11-17 11:42:17 -05:00
{
2023-11-22 04:08:33 -05:00
// Check if the next state is still same as current state, GetStateSafe() will return invalid state if passed out of bounds index.
bAreCommon = bAreCommon & & ( CurrentFrame & & CurrentFrame - > ActiveStates . GetStateSafe ( StateIndex ) = = NextFrame . ActiveStates [ StateIndex ] ) ;
const FStateTreeStateHandle StateHandle = NextFrame . ActiveStates [ StateIndex ] ;
const FCompactStateTreeState & State = NextFrame . StateTree - > States [ StateHandle . Index ] ;
InstanceStructs . AddDefaulted ( State . InstanceDataNum ) ;
2023-11-30 07:03:20 -05:00
TempInstanceStructs . AddZeroed ( State . InstanceDataNum ) ;
2023-11-22 04:08:33 -05:00
2024-04-22 07:02:22 -04:00
bool bCanHaveTempData = false ;
2023-12-05 06:59:39 -05:00
if ( State . Type = = EStateTreeStateType : : Subtree )
2023-11-22 04:08:33 -05:00
{
check ( State . ParameterDataHandle . IsValid ( ) ) ;
2023-12-05 06:59:39 -05:00
check ( State . ParameterTemplateIndex . IsValid ( ) ) ;
2023-11-22 04:08:33 -05:00
const FConstStructView ParamsInstanceData = NextFrame . StateTree - > DefaultInstanceData . GetStruct ( State . ParameterTemplateIndex . Get ( ) ) ;
2023-11-30 07:03:20 -05:00
if ( ! NextStateParameterDataHandle . IsValid ( ) )
2023-11-22 04:08:33 -05:00
{
// Parameters are not set by a linked state, create instance data.
InstanceStructs [ BaseIndex + State . ParameterDataHandle . GetIndex ( ) ] = ParamsInstanceData ;
2023-11-30 07:03:20 -05:00
NextFrame . StateParameterDataHandle = State . ParameterDataHandle ;
2024-04-22 07:02:22 -04:00
bCanHaveTempData = true ;
2023-11-22 04:08:33 -05:00
}
else
{
// Point to the parameter block set by linked state.
2023-11-30 07:03:20 -05:00
const FCompactStateTreeParameters * Params = ParamsInstanceData . GetPtr < const FCompactStateTreeParameters > ( ) ;
const UStruct * StateParameterDataStruct = Params ? Params - > Parameters . GetPropertyBagStruct ( ) : nullptr ;
check ( NextStateParameterDataStruct = = StateParameterDataStruct ) ;
NextFrame . StateParameterDataHandle = NextStateParameterDataHandle ;
NextStateParameterDataHandle = FStateTreeDataHandle : : Invalid ; // Mark as used.
2024-04-22 07:02:22 -04:00
// This state will not instantiate parameter data, so we don't care about the temp data either.
bCanHaveTempData = false ;
2023-11-22 04:08:33 -05:00
}
}
2023-12-05 06:59:39 -05:00
else
{
if ( State . ParameterTemplateIndex . IsValid ( ) )
{
// Linked state's instance data is the parameters.
check ( State . ParameterDataHandle . IsValid ( ) ) ;
2024-03-28 05:18:19 -04:00
const FCompactStateTreeParameters * Params = nullptr ;
if ( FInstancedStruct * TempParamsInstanceData = FindInstanceTempData ( NextFrame , State . ParameterDataHandle ) )
{
// If we have temp data for the parameters, then setup the instance data with just a type, so that we can steal the temp data below (TempInstanceStructs).
// We expect overridden linked assets to hit this code path.
InstanceStructs [ BaseIndex + State . ParameterDataHandle . GetIndex ( ) ] = FConstStructView ( TempParamsInstanceData - > GetScriptStruct ( ) ) ;
Params = TempParamsInstanceData - > GetPtr < const FCompactStateTreeParameters > ( ) ;
2024-04-22 07:02:22 -04:00
bCanHaveTempData = true ;
2024-03-28 05:18:19 -04:00
}
else
{
2024-08-22 11:41:23 -04:00
// If not temp data, use the states or linked assets default values.
FConstStructView ParamsInstanceData ;
if ( State . Type = = EStateTreeStateType : : LinkedAsset )
{
if ( const FStateTreeReference * Override = GetLinkedStateTreeOverrideForTag ( State . Tag ) )
{
ParamsInstanceData = FConstStructView : : Make ( TempParams . Emplace_GetRef ( Override - > GetParameters ( ) ) ) ;
}
}
if ( ! ParamsInstanceData . IsValid ( ) )
{
ParamsInstanceData = NextFrame . StateTree - > DefaultInstanceData . GetStruct ( State . ParameterTemplateIndex . Get ( ) ) ;
}
2024-03-28 05:18:19 -04:00
InstanceStructs [ BaseIndex + State . ParameterDataHandle . GetIndex ( ) ] = ParamsInstanceData ;
Params = ParamsInstanceData . GetPtr < const FCompactStateTreeParameters > ( ) ;
2024-04-22 07:02:22 -04:00
bCanHaveTempData = true ;
2024-03-28 05:18:19 -04:00
}
2023-12-05 06:59:39 -05:00
if ( State . Type = = EStateTreeStateType : : Linked
| | State . Type = = EStateTreeStateType : : LinkedAsset )
{
// Store the index of the parameter data, so that we can point the linked state to it.
check ( State . ParameterDataHandle . GetSource ( ) = = EStateTreeDataSourceType : : StateParameterData ) ;
checkf ( ! NextStateParameterDataHandle . IsValid ( ) , TEXT ( " NextStateParameterDataIndex not should be set yet when we encounter a linked state. " ) ) ;
NextStateParameterDataHandle = State . ParameterDataHandle ;
NextStateParameterDataStruct = Params ? Params - > Parameters . GetPropertyBagStruct ( ) : nullptr ;
}
}
}
2024-04-22 07:02:22 -04:00
if ( ! bAreCommon & & bCanHaveTempData )
2023-11-30 07:03:20 -05:00
{
TempInstanceStructs [ BaseIndex + State . ParameterDataHandle . GetIndex ( ) ] = FindInstanceTempData ( NextFrame , State . ParameterDataHandle ) ;
}
2024-03-11 04:54:44 -04:00
if ( State . EventDataIndex . IsValid ( ) )
{
InstanceStructs [ BaseIndex + State . EventDataIndex . Get ( ) ] = FConstStructView ( FStateTreeSharedEvent : : StaticStruct ( ) ) ;
}
2023-11-22 04:08:33 -05:00
for ( int32 TaskIndex = State . TasksBegin ; TaskIndex < ( State . TasksBegin + State . TasksNum ) ; TaskIndex + + )
{
const FStateTreeTaskBase & Task = NextFrame . StateTree - > Nodes [ TaskIndex ] . Get < const FStateTreeTaskBase > ( ) ;
const FConstStructView TaskInstanceData = NextFrame . StateTree - > DefaultInstanceData . GetStruct ( Task . InstanceTemplateIndex . Get ( ) ) ;
InstanceStructs [ BaseIndex + Task . InstanceDataHandle . GetIndex ( ) ] = TaskInstanceData ;
2023-11-30 07:03:20 -05:00
if ( ! bAreCommon )
{
TempInstanceStructs [ BaseIndex + Task . InstanceDataHandle . GetIndex ( ) ] = FindInstanceTempData ( NextFrame , Task . InstanceDataHandle ) ;
}
2023-11-22 04:08:33 -05:00
}
if ( bAreCommon )
{
NumCommonInstanceData = InstanceStructs . Num ( ) ;
}
2023-11-17 11:42:17 -05:00
}
}
2023-11-22 04:08:33 -05:00
2022-05-31 04:51:18 -04:00
// Common section should match.
// @todo: put this behind a define when enough testing has been done.
2023-11-22 04:08:33 -05:00
for ( int32 Index = 0 ; Index < NumCommonInstanceData ; Index + + )
2022-05-31 04:51:18 -04:00
{
2023-11-22 04:08:33 -05:00
check ( Index < InstanceData . Num ( ) ) ;
FConstStructView ExistingInstanceDataView = InstanceData . GetStruct ( Index ) ;
FConstStructView NewInstanceDataView = InstanceStructs [ Index ] ;
check ( NewInstanceDataView . GetScriptStruct ( ) = = ExistingInstanceDataView . GetScriptStruct ( ) ) ;
const FStateTreeInstanceObjectWrapper * ExistingWrapper = ExistingInstanceDataView . GetPtr < const FStateTreeInstanceObjectWrapper > ( ) ;
const FStateTreeInstanceObjectWrapper * NewWrapper = ExistingInstanceDataView . GetPtr < const FStateTreeInstanceObjectWrapper > ( ) ;
if ( ExistingWrapper & & NewWrapper )
{
check ( ExistingWrapper - > InstanceObject & & NewWrapper - > InstanceObject ) ;
check ( ExistingWrapper - > InstanceObject - > GetClass ( ) = = NewWrapper - > InstanceObject - > GetClass ( ) ) ;
}
2022-05-31 04:51:18 -04:00
}
// Remove instance data that was not common.
2023-11-22 04:08:33 -05:00
InstanceData . ShrinkTo ( NumCommonInstanceData ) ;
2022-05-31 04:51:18 -04:00
// Add new instance data.
2023-11-30 07:03:20 -05:00
InstanceData . Append ( Owner ,
MakeArrayView ( InstanceStructs . GetData ( ) + NumCommonInstanceData , InstanceStructs . Num ( ) - NumCommonInstanceData ) ,
MakeArrayView ( TempInstanceStructs . GetData ( ) + NumCommonInstanceData , TempInstanceStructs . Num ( ) - NumCommonInstanceData ) ) ;
InstanceData . ResetTemporaryInstances ( ) ;
2022-05-31 04:51:18 -04:00
}
2024-03-11 04:54:44 -04:00
FStateTreeDataView FStateTreeExecutionContext : : GetDataView ( const FStateTreeExecutionFrame * ParentFrame , const FStateTreeExecutionFrame & CurrentFrame , const FStateTreeDataHandle Handle )
{
switch ( Handle . GetSource ( ) )
{
case EStateTreeDataSourceType : : ContextData :
check ( ! ContextAndExternalDataViews . IsEmpty ( ) )
return ContextAndExternalDataViews [ Handle . GetIndex ( ) ] ;
case EStateTreeDataSourceType : : ExternalData :
check ( ! ContextAndExternalDataViews . IsEmpty ( ) )
return ContextAndExternalDataViews [ CurrentFrame . ExternalDataBaseIndex . Get ( ) + Handle . GetIndex ( ) ] ;
case EStateTreeDataSourceType : : TransitionEvent :
{
if ( CurrentlyProcessedTransitionEvent )
{
// const_cast because events are read only, but we cannot express that in FStateTreeDataView.
return FStateTreeDataView ( FStructView : : Make ( * const_cast < FStateTreeEvent * > ( CurrentlyProcessedTransitionEvent ) ) ) ;
}
return nullptr ;
}
case EStateTreeDataSourceType : : StateEvent :
{
2024-04-25 08:50:27 -04:00
// If state selection is going, return FStateTreeEvent of the event currently captured by the state selection.
2024-03-11 04:54:44 -04:00
if ( CurrentlyProcessedStateSelectionEvents )
{
if ( const FCompactStateTreeState * State = CurrentFrame . StateTree - > GetStateFromHandle ( Handle . GetState ( ) ) )
{
2024-04-25 08:50:27 -04:00
// Events are read only, but we cannot express that in FStateTreeDataView.
if ( FStateTreeEvent * Event = CurrentlyProcessedStateSelectionEvents - > Events [ State - > Depth ] . GetMutable ( ) )
2024-03-11 04:54:44 -04:00
{
2024-04-25 08:50:27 -04:00
return FStateTreeDataView ( FStructView : : Make ( * Event ) ) ;
2024-03-11 04:54:44 -04:00
}
}
return { } ;
}
return GetDataViewFromInstanceStorage ( * InstanceDataStorage , CurrentlyProcessedSharedInstanceStorage , ParentFrame , CurrentFrame , Handle ) ;
}
default :
return GetDataViewFromInstanceStorage ( * InstanceDataStorage , CurrentlyProcessedSharedInstanceStorage , ParentFrame , CurrentFrame , Handle ) ;
}
}
2024-06-10 15:59:27 -04:00
EStateTreeRunStatus FStateTreeExecutionContext : : ForceTransition ( const FRecordedStateTreeTransitionResult & Transition )
{
2024-08-09 09:48:26 -04:00
if ( ! IsValid ( ) )
{
STATETREE_LOG ( Warning , TEXT ( " %hs: StateTree context is not initialized properly ('%s' using StateTree '%s') " ) ,
__FUNCTION__ , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & RootStateTree ) ) ;
return EStateTreeRunStatus : : Failed ;
}
2024-06-10 15:59:27 -04:00
FStateTreeTransitionResult TransitionResult = FStateTreeTransitionResult ( Transition ) ;
ExitState ( TransitionResult ) ;
return EnterState ( TransitionResult ) ;
}
2024-03-11 04:54:44 -04:00
FStateTreeDataView FStateTreeExecutionContext : : GetDataViewFromInstanceStorage ( FStateTreeInstanceStorage & InstanceDataStorage , FStateTreeInstanceStorage * CurrentlyProcessedSharedInstanceStorage , const FStateTreeExecutionFrame * ParentFrame , const FStateTreeExecutionFrame & CurrentFrame , const FStateTreeDataHandle Handle )
2023-11-22 04:08:33 -05:00
{
switch ( Handle . GetSource ( ) )
{
case EStateTreeDataSourceType : : None :
return { } ;
case EStateTreeDataSourceType : : GlobalInstanceData :
2024-01-30 03:18:31 -05:00
return InstanceDataStorage . GetMutableStruct ( CurrentFrame . GlobalInstanceIndexBase . Get ( ) + Handle . GetIndex ( ) ) ;
2023-11-22 04:08:33 -05:00
case EStateTreeDataSourceType : : GlobalInstanceDataObject :
2024-01-30 03:18:31 -05:00
return InstanceDataStorage . GetMutableObject ( CurrentFrame . GlobalInstanceIndexBase . Get ( ) + Handle . GetIndex ( ) ) ;
2023-11-22 04:08:33 -05:00
case EStateTreeDataSourceType : : ActiveInstanceData :
2024-01-30 03:18:31 -05:00
return InstanceDataStorage . GetMutableStruct ( CurrentFrame . ActiveInstanceIndexBase . Get ( ) + Handle . GetIndex ( ) ) ;
2023-11-22 04:08:33 -05:00
case EStateTreeDataSourceType : : ActiveInstanceDataObject :
2024-01-30 03:18:31 -05:00
return InstanceDataStorage . GetMutableObject ( CurrentFrame . ActiveInstanceIndexBase . Get ( ) + Handle . GetIndex ( ) ) ;
2023-11-22 04:08:33 -05:00
case EStateTreeDataSourceType : : SharedInstanceData :
check ( CurrentlyProcessedSharedInstanceStorage ) ;
return CurrentlyProcessedSharedInstanceStorage - > GetMutableStruct ( Handle . GetIndex ( ) ) ;
case EStateTreeDataSourceType : : SharedInstanceDataObject :
check ( CurrentlyProcessedSharedInstanceStorage ) ;
2023-11-24 04:04:27 -05:00
return CurrentlyProcessedSharedInstanceStorage - > GetMutableObject ( Handle . GetIndex ( ) ) ;
2023-11-22 04:08:33 -05:00
2023-11-30 07:03:20 -05:00
case EStateTreeDataSourceType : : GlobalParameterData :
2024-04-15 08:23:17 -04:00
// Defined in parent frame or is root state tree parameters
if ( ParentFrame )
2023-11-30 07:03:20 -05:00
{
2024-04-15 08:23:17 -04:00
return GetDataViewFromInstanceStorage ( InstanceDataStorage , CurrentlyProcessedSharedInstanceStorage , nullptr , * ParentFrame , CurrentFrame . GlobalParameterDataHandle ) ;
2023-11-30 07:03:20 -05:00
}
2024-04-15 08:23:17 -04:00
return InstanceDataStorage . GetMutableGlobalParameters ( ) ;
2023-11-30 07:03:20 -05:00
2023-11-22 04:08:33 -05:00
case EStateTreeDataSourceType : : SubtreeParameterData :
{
2023-11-30 07:03:20 -05:00
// Defined in parent frame.
2024-04-15 08:23:17 -04:00
if ( ParentFrame )
{
// Linked subtree, params defined in parent scope.
return GetDataViewFromInstanceStorage ( InstanceDataStorage , CurrentlyProcessedSharedInstanceStorage , nullptr , * ParentFrame , CurrentFrame . StateParameterDataHandle ) ;
}
// Standalone subtree, params define as state params.
FCompactStateTreeParameters & SubtreeParams = InstanceDataStorage . GetMutableStruct ( CurrentFrame . ActiveInstanceIndexBase . Get ( ) + Handle . GetIndex ( ) ) . Get < FCompactStateTreeParameters > ( ) ;
return SubtreeParams . Parameters . GetMutableValue ( ) ;
2023-11-22 04:08:33 -05:00
}
2023-12-05 06:59:39 -05:00
case EStateTreeDataSourceType : : StateParameterData :
2023-11-22 04:08:33 -05:00
{
2024-04-15 08:23:17 -04:00
FCompactStateTreeParameters & StateParams = InstanceDataStorage . GetMutableStruct ( CurrentFrame . ActiveInstanceIndexBase . Get ( ) + Handle . GetIndex ( ) ) . Get < FCompactStateTreeParameters > ( ) ;
return StateParams . Parameters . GetMutableValue ( ) ;
2023-11-22 04:08:33 -05:00
}
2024-03-11 04:54:44 -04:00
case EStateTreeDataSourceType : : StateEvent :
{
2024-04-25 08:50:27 -04:00
// Return FStateTreeEvent from shared event.
2024-03-11 04:54:44 -04:00
FStateTreeSharedEvent & SharedEvent = InstanceDataStorage . GetMutableStruct ( CurrentFrame . ActiveInstanceIndexBase . Get ( ) + Handle . GetIndex ( ) ) . Get < FStateTreeSharedEvent > ( ) ;
check ( SharedEvent . IsValid ( ) ) ;
2024-04-25 08:50:27 -04:00
// Events are read only, but we cannot express that in FStateTreeDataView.
return FStateTreeDataView ( FStructView : : Make ( * SharedEvent . GetMutable ( ) ) ) ;
2024-03-11 04:54:44 -04:00
}
case EStateTreeDataSourceType : : ContextData :
case EStateTreeDataSourceType : : ExternalData :
case EStateTreeDataSourceType : : TransitionEvent :
return { } ;
2023-11-22 04:08:33 -05:00
default :
checkf ( false , TEXT ( " Unhandle case %s " ) , * UEnum : : GetValueAsString ( Handle . GetSource ( ) ) ) ;
}
return { } ;
}
2024-02-29 03:19:31 -05:00
const FStateTreeExecutionFrame * FStateTreeExecutionContext : : FindFrame ( const UStateTree * StateTree , FStateTreeStateHandle RootState , TConstArrayView < FStateTreeExecutionFrame > Frames , const FStateTreeExecutionFrame * & OutParentFrame )
{
const int32 FrameIndex = Frames . IndexOfByPredicate ( [ & StateTree , RootState ] ( const FStateTreeExecutionFrame & Frame )
{
return Frame . StateTree = = StateTree & & Frame . RootState = = RootState ;
} ) ;
2024-03-11 04:54:44 -04:00
if ( FrameIndex = = INDEX_NONE )
2024-02-29 03:19:31 -05:00
{
OutParentFrame = nullptr ;
return nullptr ;
}
if ( FrameIndex > 0 )
{
OutParentFrame = & Frames [ FrameIndex - 1 ] ;
}
return & Frames [ FrameIndex ] ;
}
2023-11-30 07:03:20 -05:00
bool FStateTreeExecutionContext : : IsHandleSourceValid ( const FStateTreeExecutionFrame * ParentFrame , const FStateTreeExecutionFrame & CurrentFrame , const FStateTreeDataHandle Handle ) const
2023-11-22 04:08:33 -05:00
{
// Checks that the instance data is valid for specific handle types.
//
// The CurrentFrame may not be yet properly initialized, for that reason we need to check
// that the path to the handle makes sense (it's part of the active states) as well as that
// we actually have instance data for the handle (index is valid).
//
// The (base) indices can be invalid if the frame/state is not entered yet.
// For active instance data we need to check that the frame is initialized for a specific state,
// as well as that the instance data is initialized.
switch ( Handle . GetSource ( ) )
{
case EStateTreeDataSourceType : : None :
return true ;
case EStateTreeDataSourceType : : GlobalInstanceData :
case EStateTreeDataSourceType : : GlobalInstanceDataObject :
return CurrentFrame . GlobalInstanceIndexBase . IsValid ( )
& & InstanceDataStorage - > IsValidIndex ( CurrentFrame . GlobalInstanceIndexBase . Get ( ) + Handle . GetIndex ( ) ) ;
case EStateTreeDataSourceType : : ActiveInstanceData :
case EStateTreeDataSourceType : : ActiveInstanceDataObject :
return CurrentFrame . ActiveInstanceIndexBase . IsValid ( )
2024-03-27 11:51:13 -04:00
& & CurrentFrame . ActiveStates . Contains ( Handle . GetState ( ) , CurrentFrame . NumCurrentlyActiveStates )
2023-11-22 04:08:33 -05:00
& & InstanceDataStorage - > IsValidIndex ( CurrentFrame . ActiveInstanceIndexBase . Get ( ) + Handle . GetIndex ( ) ) ;
case EStateTreeDataSourceType : : SharedInstanceData :
case EStateTreeDataSourceType : : SharedInstanceDataObject :
return true ;
case EStateTreeDataSourceType : : ContextData :
return true ;
2023-12-13 06:34:27 -05:00
case EStateTreeDataSourceType : : ExternalData :
return CurrentFrame . ExternalDataBaseIndex . IsValid ( )
& & ContextAndExternalDataViews . IsValidIndex ( CurrentFrame . ExternalDataBaseIndex . Get ( ) + Handle . GetIndex ( ) ) ;
2023-11-30 07:03:20 -05:00
case EStateTreeDataSourceType : : GlobalParameterData :
return ParentFrame
? IsHandleSourceValid ( nullptr , * ParentFrame , CurrentFrame . GlobalParameterDataHandle )
: CurrentFrame . GlobalParameterDataHandle . IsValid ( ) ;
2023-11-22 04:08:33 -05:00
case EStateTreeDataSourceType : : SubtreeParameterData :
2024-04-15 08:23:17 -04:00
if ( ParentFrame )
{
2024-05-27 15:15:55 -04:00
// If the current subtree state is not instantiated yet, we cannot assume that the parameter data is instantiated in the parent frame either.
if ( ! CurrentFrame . ActiveInstanceIndexBase . IsValid ( ) )
{
return false ;
}
2024-04-15 08:23:17 -04:00
// Linked subtree, params defined in parent scope.
return IsHandleSourceValid ( nullptr , * ParentFrame , CurrentFrame . StateParameterDataHandle ) ;
}
// Standalone subtree, params define as state params.
return CurrentFrame . ActiveInstanceIndexBase . IsValid ( )
& & CurrentFrame . ActiveStates . Contains ( Handle . GetState ( ) , CurrentFrame . NumCurrentlyActiveStates )
& & InstanceDataStorage - > IsValidIndex ( CurrentFrame . ActiveInstanceIndexBase . Get ( ) + Handle . GetIndex ( ) ) ;
2023-11-22 04:08:33 -05:00
2023-12-05 06:59:39 -05:00
case EStateTreeDataSourceType : : StateParameterData :
2023-11-22 04:08:33 -05:00
return CurrentFrame . ActiveInstanceIndexBase . IsValid ( )
2024-03-27 11:51:13 -04:00
& & CurrentFrame . ActiveStates . Contains ( Handle . GetState ( ) , CurrentFrame . NumCurrentlyActiveStates )
2023-11-22 04:08:33 -05:00
& & InstanceDataStorage - > IsValidIndex ( CurrentFrame . ActiveInstanceIndexBase . Get ( ) + Handle . GetIndex ( ) ) ;
2024-03-11 04:54:44 -04:00
case EStateTreeDataSourceType : : TransitionEvent :
return CurrentlyProcessedTransitionEvent ! = nullptr ;
case EStateTreeDataSourceType : : StateEvent :
return CurrentlyProcessedStateSelectionEvents ! = nullptr
| | ( CurrentFrame . ActiveInstanceIndexBase . IsValid ( )
& & CurrentFrame . ActiveStates . Contains ( Handle . GetState ( ) )
& & InstanceDataStorage - > IsValidIndex ( CurrentFrame . ActiveInstanceIndexBase . Get ( ) + Handle . GetIndex ( ) ) ) ;
2023-11-22 04:08:33 -05:00
default :
checkf ( false , TEXT ( " Unhandle case %s " ) , * UEnum : : GetValueAsString ( Handle . GetSource ( ) ) ) ;
}
return false ;
}
2024-03-11 04:54:44 -04:00
FStateTreeDataView FStateTreeExecutionContext : : GetDataViewOrTemporary ( const FStateTreeExecutionFrame * ParentFrame , const FStateTreeExecutionFrame & CurrentFrame , const FStateTreeDataHandle Handle )
2023-11-30 07:03:20 -05:00
{
if ( IsHandleSourceValid ( ParentFrame , CurrentFrame , Handle ) )
{
return GetDataView ( ParentFrame , CurrentFrame , Handle ) ;
}
2023-11-22 04:08:33 -05:00
2023-11-30 07:03:20 -05:00
check ( InstanceDataStorage ) ;
switch ( Handle . GetSource ( ) )
{
case EStateTreeDataSourceType : : GlobalInstanceData :
case EStateTreeDataSourceType : : ActiveInstanceData :
return InstanceDataStorage - > GetMutableTemporaryStruct ( CurrentFrame , Handle ) ;
case EStateTreeDataSourceType : : GlobalInstanceDataObject :
case EStateTreeDataSourceType : : ActiveInstanceDataObject :
return InstanceDataStorage - > GetMutableTemporaryObject ( CurrentFrame , Handle ) ;
case EStateTreeDataSourceType : : GlobalParameterData :
if ( ParentFrame )
{
if ( FCompactStateTreeParameters * Params = InstanceDataStorage - > GetMutableTemporaryStruct ( * ParentFrame , CurrentFrame . GlobalParameterDataHandle ) . GetPtr < FCompactStateTreeParameters > ( ) )
{
return Params - > Parameters . GetMutableValue ( ) ;
}
}
break ;
case EStateTreeDataSourceType : : SubtreeParameterData :
if ( ParentFrame )
{
2024-04-15 08:23:17 -04:00
// Linked subtree, params defined in parent scope.
2023-11-30 07:03:20 -05:00
if ( FCompactStateTreeParameters * Params = InstanceDataStorage - > GetMutableTemporaryStruct ( * ParentFrame , CurrentFrame . StateParameterDataHandle ) . GetPtr < FCompactStateTreeParameters > ( ) )
{
return Params - > Parameters . GetMutableValue ( ) ;
}
}
2024-04-15 08:23:17 -04:00
// Standalone subtree, params define as state params.
if ( FCompactStateTreeParameters * Params = InstanceDataStorage - > GetMutableTemporaryStruct ( CurrentFrame , Handle ) . GetPtr < FCompactStateTreeParameters > ( ) )
{
return Params - > Parameters . GetMutableValue ( ) ;
}
2023-11-30 07:03:20 -05:00
break ;
2023-12-05 06:59:39 -05:00
case EStateTreeDataSourceType : : StateParameterData :
2024-04-15 08:23:17 -04:00
if ( FCompactStateTreeParameters * Params = InstanceDataStorage - > GetMutableTemporaryStruct ( CurrentFrame , Handle ) . GetPtr < FCompactStateTreeParameters > ( ) )
2023-12-05 06:59:39 -05:00
{
2024-04-15 08:23:17 -04:00
return Params - > Parameters . GetMutableValue ( ) ;
2023-12-05 06:59:39 -05:00
}
break ;
2023-11-30 07:03:20 -05:00
default :
return { } ;
}
return { } ;
}
FStateTreeDataView FStateTreeExecutionContext : : AddTemporaryInstance ( const FStateTreeExecutionFrame & Frame , const FStateTreeIndex16 OwnerNodeIndex , const FStateTreeDataHandle DataHandle , FConstStructView NewInstanceData )
{
check ( InstanceDataStorage ) ;
const FStructView NewInstance = InstanceDataStorage - > AddTemporaryInstance ( Owner , Frame , OwnerNodeIndex , DataHandle , NewInstanceData ) ;
if ( FStateTreeInstanceObjectWrapper * Wrapper = NewInstance . GetPtr < FStateTreeInstanceObjectWrapper > ( ) )
{
return FStateTreeDataView ( Wrapper - > InstanceObject ) ;
}
return NewInstance ;
}
2024-03-11 04:54:44 -04:00
bool FStateTreeExecutionContext : : CopyBatchOnActiveInstances ( const FStateTreeExecutionFrame * ParentFrame , const FStateTreeExecutionFrame & CurrentFrame , const FStateTreeDataView TargetView , const FStateTreeIndex16 BindingsBatch )
2023-11-22 04:08:33 -05:00
{
const FStateTreePropertyCopyBatch & Batch = CurrentFrame . StateTree - > PropertyBindings . GetBatch ( BindingsBatch ) ;
check ( TargetView . GetStruct ( ) = = Batch . TargetStruct . Struct ) ;
2024-06-04 03:56:38 -04:00
if ( Batch . PropertyFunctionsBegin ! = Batch . PropertyFunctionsEnd )
{
check ( Batch . PropertyFunctionsBegin . IsValid ( ) & & Batch . PropertyFunctionsEnd . IsValid ( ) ) ;
EvaluatePropertyFunctionsOnActiveInstances ( ParentFrame , CurrentFrame , Batch . PropertyFunctionsBegin , Batch . PropertyFunctionsEnd . Get ( ) - Batch . PropertyFunctionsBegin . Get ( ) ) ;
}
2023-11-22 04:08:33 -05:00
bool bSucceed = true ;
for ( const FStateTreePropertyCopy & Copy : CurrentFrame . StateTree - > PropertyBindings . GetBatchCopies ( Batch ) )
{
2023-11-30 07:03:20 -05:00
const FStateTreeDataView SourceView = GetDataView ( ParentFrame , CurrentFrame , Copy . SourceDataHandle ) ;
2023-11-22 04:08:33 -05:00
bSucceed & = CurrentFrame . StateTree - > PropertyBindings . CopyProperty ( Copy , SourceView , TargetView ) ;
}
return bSucceed ;
}
2024-03-11 04:54:44 -04:00
bool FStateTreeExecutionContext : : CopyBatchWithValidation ( const FStateTreeExecutionFrame * ParentFrame , const FStateTreeExecutionFrame & CurrentFrame , const FStateTreeDataView TargetView , const FStateTreeIndex16 BindingsBatch )
2023-11-22 04:08:33 -05:00
{
const FStateTreePropertyCopyBatch & Batch = CurrentFrame . StateTree - > PropertyBindings . GetBatch ( BindingsBatch ) ;
check ( TargetView . GetStruct ( ) = = Batch . TargetStruct . Struct ) ;
2024-06-04 03:56:38 -04:00
if ( Batch . PropertyFunctionsBegin ! = Batch . PropertyFunctionsEnd )
{
check ( Batch . PropertyFunctionsBegin . IsValid ( ) & & Batch . PropertyFunctionsEnd . IsValid ( ) ) ;
EvaluatePropertyFunctionsWithValidation ( ParentFrame , CurrentFrame , Batch . PropertyFunctionsBegin , Batch . PropertyFunctionsEnd . Get ( ) - Batch . PropertyFunctionsBegin . Get ( ) ) ;
}
2023-11-22 04:08:33 -05:00
bool bSucceed = true ;
for ( const FStateTreePropertyCopy & Copy : CurrentFrame . StateTree - > PropertyBindings . GetBatchCopies ( Batch ) )
{
2023-11-30 07:03:20 -05:00
const FStateTreeDataView SourceView = GetDataViewOrTemporary ( ParentFrame , CurrentFrame , Copy . SourceDataHandle ) ;
if ( ! SourceView . IsValid ( ) )
2023-11-22 04:08:33 -05:00
{
bSucceed = false ;
break ;
}
2023-11-30 07:03:20 -05:00
2023-11-22 04:08:33 -05:00
bSucceed & = CurrentFrame . StateTree - > PropertyBindings . CopyProperty ( Copy , SourceView , TargetView ) ;
}
return bSucceed ;
}
2023-12-13 06:34:27 -05:00
bool FStateTreeExecutionContext : : CollectActiveExternalData ( )
{
if ( bActiveExternalDataCollected )
{
return true ;
}
bool bAllExternalDataValid = true ;
FStateTreeExecutionState & Exec = GetExecState ( ) ;
const FStateTreeExecutionFrame * PrevFrame = nullptr ;
for ( FStateTreeExecutionFrame & Frame : Exec . ActiveFrames )
{
if ( PrevFrame & & PrevFrame - > StateTree = = Frame . StateTree )
{
Frame . ExternalDataBaseIndex = PrevFrame - > ExternalDataBaseIndex ;
}
else
{
Frame . ExternalDataBaseIndex = CollectExternalData ( Frame . StateTree ) ;
}
if ( ! Frame . ExternalDataBaseIndex . IsValid ( ) )
{
bAllExternalDataValid = false ;
}
PrevFrame = & Frame ;
}
if ( bAllExternalDataValid )
{
bActiveExternalDataCollected = true ;
}
return bAllExternalDataValid ;
}
FStateTreeIndex16 FStateTreeExecutionContext : : CollectExternalData ( const UStateTree * StateTree )
{
if ( ! StateTree )
{
return FStateTreeIndex16 : : Invalid ;
}
// If one of the active states share the same state tree, get the external data from there.
for ( const FCollectedExternalDataCache & Cache : CollectedExternalCache )
{
if ( Cache . StateTree = = StateTree )
{
return Cache . BaseIndex ;
}
}
const TConstArrayView < FStateTreeExternalDataDesc > ExternalDataDescs = StateTree - > GetExternalDataDescs ( ) ;
const int32 BaseIndex = ContextAndExternalDataViews . Num ( ) ;
const int32 NumDescs = ExternalDataDescs . Num ( ) ;
FStateTreeIndex16 Result ( BaseIndex ) ;
if ( NumDescs > 0 )
{
ContextAndExternalDataViews . AddDefaulted ( NumDescs ) ;
const TArrayView < FStateTreeDataView > DataViews = MakeArrayView ( ContextAndExternalDataViews . GetData ( ) + BaseIndex , NumDescs ) ;
if ( ensureMsgf ( CollectExternalDataDelegate . IsBound ( ) , TEXT ( " The StateTree asset has external data, expecting CollectExternalData delegate to be provided. " ) ) )
{
if ( ! CollectExternalDataDelegate . Execute ( * this , StateTree , StateTree - > GetExternalDataDescs ( ) , DataViews ) )
{
// The caller is responsible for error reporting.
return FStateTreeIndex16 : : Invalid ;
}
}
// Check that the data is valid and present.
for ( int32 Index = 0 ; Index < NumDescs ; Index + + )
{
const FStateTreeExternalDataDesc & DataDesc = ExternalDataDescs [ Index ] ;
const FStateTreeDataView & DataView = ContextAndExternalDataViews [ BaseIndex + Index ] ;
if ( DataDesc . Requirement = = EStateTreeExternalDataRequirement : : Required )
{
// Required items must have valid pointer of the expected type.
if ( ! DataView . IsValid ( ) | | ! DataDesc . IsCompatibleWith ( DataView ) )
{
Result = FStateTreeIndex16 : : Invalid ;
break ;
}
}
else
{
// Optional items must have same type if they are set.
if ( DataView . IsValid ( ) & & ! DataDesc . IsCompatibleWith ( DataView ) )
{
Result = FStateTreeIndex16 : : Invalid ;
break ;
}
}
}
}
if ( ! Result . IsValid ( ) )
{
// Rollback
ContextAndExternalDataViews . SetNum ( BaseIndex ) ;
}
// Cached both succeeded and failed attempts.
CollectedExternalCache . Add ( { StateTree , Result } ) ;
return FStateTreeIndex16 ( Result ) ;
}
2024-01-22 05:38:03 -05:00
bool FStateTreeExecutionContext : : SetGlobalParameters ( const FInstancedPropertyBag & Parameters )
{
if ( ensureMsgf ( RootStateTree . GetDefaultParameters ( ) . GetPropertyBagStruct ( ) = = Parameters . GetPropertyBagStruct ( ) ,
TEXT ( " Parameters must be of the same struct type. Make sure to migrate the provided parameters to the same type as the StateTree default parameters. " ) ) )
{
InstanceDataStorage - > SetGlobalParameters ( Parameters ) ;
return true ;
}
return false ;
}
2024-03-11 04:54:44 -04:00
void FStateTreeExecutionContext : : CaptureNewStateEvents ( TConstArrayView < FStateTreeExecutionFrame > PrevFrames , TConstArrayView < FStateTreeExecutionFrame > NewFrames , TArrayView < FStateTreeFrameStateSelectionEvents > FramesStateSelectionEvents )
{
// Mark the events from delayed transitions as in use, so that each State will receive unique copy of the event struct.
TArray < FStateTreeSharedEvent , TInlineAllocator < 16 > > EventsInUse ;
for ( const FStateTreeTransitionDelayedState & DelayedTransition : GetExecState ( ) . DelayedTransitions )
{
if ( DelayedTransition . CapturedEvent . IsValid ( ) )
{
EventsInUse . Add ( DelayedTransition . CapturedEvent ) ;
}
}
for ( int32 FrameIndex = 0 ; FrameIndex < NewFrames . Num ( ) ; + + FrameIndex )
{
const FStateTreeExecutionFrame & NewFrame = NewFrames [ FrameIndex ] ;
// Find states that are unique to the new frame.
TConstArrayView < FStateTreeStateHandle > UniqueStates = NewFrame . ActiveStates . States ;
if ( PrevFrames . IsValidIndex ( FrameIndex ) )
{
const FStateTreeExecutionFrame & PrevFrame = PrevFrames [ FrameIndex ] ;
if ( PrevFrame . RootState = = NewFrame . RootState
& & PrevFrame . StateTree = = NewFrame . StateTree )
{
for ( int32 StateIndex = 0 ; StateIndex < NewFrame . ActiveStates . Num ( ) ; + + StateIndex )
{
2024-06-05 07:40:52 -04:00
if ( ! PrevFrame . ActiveStates . IsValidIndex ( StateIndex ) | | PrevFrame . ActiveStates [ StateIndex ] ! = NewFrame . ActiveStates [ StateIndex ] )
2024-03-11 04:54:44 -04:00
{
UniqueStates = TConstArrayView < FStateTreeStateHandle > ( & NewFrame . ActiveStates [ StateIndex ] , NewFrame . ActiveStates . Num ( ) - StateIndex ) ;
break ;
}
}
}
}
// Capture events for the new states.
for ( const FStateTreeStateHandle StateHandle : UniqueStates )
{
if ( const FCompactStateTreeState * State = NewFrame . StateTree - > GetStateFromHandle ( StateHandle ) )
{
if ( State - > EventDataIndex . IsValid ( ) )
{
FStateTreeSharedEvent & StateTreeEvent = InstanceDataStorage - > GetMutableStruct ( NewFrame . ActiveInstanceIndexBase . Get ( ) + State - > EventDataIndex . Get ( ) ) . Get < FStateTreeSharedEvent > ( ) ;
const FStateTreeSharedEvent & EventToCapture = FramesStateSelectionEvents [ FrameIndex ] . Events [ State - > Depth ] ;
if ( EventsInUse . Contains ( EventToCapture ) )
{
// Event is already spoken for, make a copy.
StateTreeEvent = FStateTreeSharedEvent ( * EventToCapture ) ;
}
else
{
// Event not in use, steal it.
StateTreeEvent = FramesStateSelectionEvents [ FrameIndex ] . Events [ State - > Depth ] ;
EventsInUse . Add ( EventToCapture ) ;
}
}
}
}
}
}
2023-11-22 04:08:33 -05:00
EStateTreeRunStatus FStateTreeExecutionContext : : EnterState ( FStateTreeTransitionResult & Transition )
2022-05-31 04:51:18 -04:00
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_EnterState ) ;
2023-11-22 04:08:33 -05:00
if ( Transition . NextActiveFrames . IsEmpty ( ) )
2022-05-31 04:51:18 -04:00
{
return EStateTreeRunStatus : : Failed ;
}
2023-11-17 11:42:17 -05:00
FStateTreeExecutionState & Exec = GetExecState ( ) ;
2024-06-10 15:59:27 -04:00
if ( bRecordTransitions )
{
RecordedTransitions . Add ( FRecordedStateTreeTransitionResult ( Transition ) ) ;
}
2023-11-22 04:08:33 -05:00
// Allocate new tasks.
UpdateInstanceData ( Exec . ActiveFrames , Transition . NextActiveFrames ) ;
2024-03-11 04:54:44 -04:00
CaptureNewStateEvents ( Exec . ActiveFrames , Transition . NextActiveFrames , Transition . NextActiveFrameEvents ) ;
2023-11-22 04:08:33 -05:00
Exec . StateChangeCount + + ;
Exec . CompletedFrameIndex = FStateTreeIndex16 : : Invalid ;
Exec . CompletedStateHandle = FStateTreeStateHandle : : Invalid ;
Exec . EnterStateFailedFrameIndex = FStateTreeIndex16 : : Invalid ; // This will make all tasks to be accepted.
Exec . EnterStateFailedTaskIndex = FStateTreeIndex16 : : Invalid ; // This will make all tasks to be accepted.
2022-05-31 04:51:18 -04:00
// On target branch means that the state is the target of current transition or child of it.
// States which were active before and will remain active, but are not on target branch will not get
// EnterState called. That is, a transition is handled as "replan from this state".
bool bOnTargetBranch = false ;
FStateTreeTransitionResult CurrentTransition = Transition ;
EStateTreeRunStatus Result = EStateTreeRunStatus : : Running ;
2023-11-22 04:08:33 -05:00
STATETREE_LOG ( Log , TEXT ( " Enter state '%s' (%d) " ) , * DebugGetStatePath ( Transition . NextActiveFrames ) , Exec . StateChangeCount ) ;
2023-12-13 10:21:13 -05:00
STATETREE_TRACE_PHASE_BEGIN ( EStateTreeUpdatePhase : : EnterStates ) ;
2022-11-03 14:21:53 -04:00
2023-11-22 04:08:33 -05:00
// The previous active frames are needed for state enter logic.
TArray < FStateTreeExecutionFrame , TConcurrentLinearArrayAllocator < FDefaultBlockAllocationTag > > PreviousActiveFrames ;
PreviousActiveFrames = Exec . ActiveFrames ;
// Reset the current active frames, new ones are added one by one.
Exec . ActiveFrames . Reset ( ) ;
for ( int32 FrameIndex = 0 ; FrameIndex < Transition . NextActiveFrames . Num ( ) & & Result ! = EStateTreeRunStatus : : Failed ; FrameIndex + + )
2022-05-31 04:51:18 -04:00
{
2023-11-22 04:08:33 -05:00
const FStateTreeExecutionFrame & NextFrame = Transition . NextActiveFrames [ FrameIndex ] ;
2023-05-29 10:13:21 -04:00
2023-11-30 07:03:20 -05:00
FStateTreeExecutionFrame * CurrentParentFrame = ! Exec . ActiveFrames . IsEmpty ( ) ? & Exec . ActiveFrames . Last ( ) : nullptr ;
2023-11-22 04:08:33 -05:00
FStateTreeExecutionFrame & CurrentFrame = Exec . ActiveFrames . Add_GetRef ( NextFrame ) ;
2023-11-30 07:03:20 -05:00
2023-11-22 04:08:33 -05:00
// We'll add new states one by one, so that active states contain only the states which have EnterState called.
CurrentFrame . ActiveStates . Reset ( ) ;
// Get previous active states, they are used to calculate transition type.
FStateTreeActiveStates PreviousActiveStates ;
if ( PreviousActiveFrames . IsValidIndex ( FrameIndex )
& & PreviousActiveFrames [ FrameIndex ] . IsSameFrame ( NextFrame ) )
2022-05-31 04:51:18 -04:00
{
2023-11-22 04:08:33 -05:00
PreviousActiveStates = PreviousActiveFrames [ FrameIndex ] . ActiveStates ;
2022-05-31 04:51:18 -04:00
}
2023-11-30 07:03:20 -05:00
FCurrentlyProcessedFrameScope FrameScope ( * this , CurrentParentFrame , CurrentFrame ) ;
2023-11-22 04:08:33 -05:00
const UStateTree * CurrentStateTree = NextFrame . StateTree ;
2023-08-15 14:21:54 -04:00
2023-11-22 04:08:33 -05:00
for ( int32 Index = 0 ; Index < NextFrame . ActiveStates . Num ( ) & & Result ! = EStateTreeRunStatus : : Failed ; Index + + )
2023-06-09 15:27:31 -04:00
{
2023-11-22 04:08:33 -05:00
const FStateTreeStateHandle CurrentHandle = NextFrame . ActiveStates [ Index ] ;
const FStateTreeStateHandle PreviousHandle = PreviousActiveStates . GetStateSafe ( Index ) ;
const FCompactStateTreeState & State = CurrentStateTree - > States [ CurrentHandle . Index ] ;
2023-06-09 15:27:31 -04:00
2023-11-22 04:08:33 -05:00
FCurrentlyProcessedStateScope StateScope ( * this , CurrentHandle ) ;
// Add only enabled States to the list of active States
if ( State . bEnabled & & ! CurrentFrame . ActiveStates . Push ( CurrentHandle ) )
2022-05-31 04:51:18 -04:00
{
2023-11-22 04:08:33 -05:00
STATETREE_LOG ( Error , TEXT ( " %hs: Reached max execution depth when trying to enter state '%s'. '%s' using StateTree '%s'. " ) ,
__FUNCTION__ , * GetStateStatusString ( Exec ) , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & RootStateTree ) ) ;
break ;
2022-05-31 04:51:18 -04:00
}
2024-03-27 11:51:13 -04:00
CurrentFrame . NumCurrentlyActiveStates = static_cast < uint8 > ( CurrentFrame . ActiveStates . Num ( ) ) ;
2022-05-31 04:51:18 -04:00
2023-11-30 07:03:20 -05:00
if ( State . Type = = EStateTreeStateType : : Linked
| | State . Type = = EStateTreeStateType : : LinkedAsset )
2023-06-05 13:12:19 -04:00
{
2023-11-22 04:08:33 -05:00
if ( State . ParameterDataHandle . IsValid ( )
& & State . ParameterBindingsBatch . IsValid ( ) )
2023-11-17 07:41:08 -05:00
{
2023-11-30 07:03:20 -05:00
const FStateTreeDataView StateParamsDataView = GetDataView ( CurrentParentFrame , CurrentFrame , State . ParameterDataHandle ) ;
CopyBatchOnActiveInstances ( CurrentParentFrame , CurrentFrame , StateParamsDataView , State . ParameterBindingsBatch ) ;
2023-11-22 04:08:33 -05:00
}
}
2024-07-31 06:55:37 -04:00
bOnTargetBranch | = CurrentFrame . StateTree = = Transition . SourceStateTree & & CurrentFrame . RootState = = Transition . SourceRootState & & CurrentHandle = = Transition . TargetState ;
2023-11-22 04:08:33 -05:00
const bool bWasActive = PreviousHandle = = CurrentHandle ;
// Do not enter a disabled State tasks but maintain property bindings
const bool bIsEnteringState = ( ! bWasActive | | bOnTargetBranch ) & & State . bEnabled ;
CurrentTransition . CurrentState = CurrentHandle ;
CurrentTransition . ChangeType = bWasActive ? EStateTreeStateChangeType : : Sustained : EStateTreeStateChangeType : : Changed ;
if ( bIsEnteringState )
{
STATETREE_TRACE_STATE_EVENT ( CurrentHandle , EStateTreeTraceEventType : : OnEntering ) ;
STATETREE_LOG ( Log , TEXT ( " %*sState '%s' %s " ) , Index * UE : : StateTree : : DebugIndentSize , TEXT ( " " ) ,
* DebugGetStatePath ( Transition . NextActiveFrames , & NextFrame , Index ) ,
* UEnum : : GetDisplayValueAsText ( CurrentTransition . ChangeType ) . ToString ( ) ) ;
}
2024-08-02 06:57:58 -04:00
// Call state change events on conditions if needed.
if ( bIsEnteringState & & State . bHasStateChangeConditions )
{
for ( int32 ConditionIndex = State . EnterConditionsBegin ; ConditionIndex < ( State . EnterConditionsBegin + State . EnterConditionsNum ) ; ConditionIndex + + )
{
const FStateTreeConditionBase & Cond = CurrentFrame . StateTree - > Nodes [ ConditionIndex ] . Get < const FStateTreeConditionBase > ( ) ;
if ( Cond . bHasShouldCallStateChangeEvents )
{
const bool bShouldCallStateChange = CurrentTransition . ChangeType = = EStateTreeStateChangeType : : Changed
| | ( CurrentTransition . ChangeType = = EStateTreeStateChangeType : : Sustained & & Cond . bShouldStateChangeOnReselect ) ;
if ( bShouldCallStateChange )
{
const FStateTreeDataView ConditionInstanceView = GetDataView ( CurrentParentFrame , CurrentFrame , Cond . InstanceDataHandle ) ;
FNodeInstanceDataScope DataScope ( * this , Cond . InstanceDataHandle , ConditionInstanceView ) ;
if ( Cond . BindingsBatch . IsValid ( ) )
{
// Use validated copy, since we test in situations where the sources are not always valid (e.g. enter conditions may try to access inactive parent state).
CopyBatchOnActiveInstances ( CurrentParentFrame , CurrentFrame , ConditionInstanceView , Cond . BindingsBatch ) ;
}
Cond . EnterState ( * this , Transition ) ;
// Reset copied properties that might contain object references.
if ( Cond . BindingsBatch . IsValid ( ) )
{
CurrentFrame . StateTree - > PropertyBindings . ResetObjects ( Cond . BindingsBatch , ConditionInstanceView ) ;
}
}
}
}
}
2023-11-22 04:08:33 -05:00
// Activate tasks on current state.
for ( int32 TaskIndex = State . TasksBegin ; TaskIndex < ( State . TasksBegin + State . TasksNum ) ; TaskIndex + + )
{
const FStateTreeTaskBase & Task = NextFrame . StateTree - > Nodes [ TaskIndex ] . Get < const FStateTreeTaskBase > ( ) ;
2023-11-30 07:03:20 -05:00
const FStateTreeDataView TaskInstanceView = GetDataView ( CurrentParentFrame , CurrentFrame , Task . InstanceDataHandle ) ;
FNodeInstanceDataScope DataScope ( * this , Task . InstanceDataHandle , TaskInstanceView ) ;
2023-11-22 04:08:33 -05:00
// Copy bound properties.
if ( Task . BindingsBatch . IsValid ( ) )
{
2023-11-30 07:03:20 -05:00
CopyBatchOnActiveInstances ( CurrentParentFrame , CurrentFrame , TaskInstanceView , Task . BindingsBatch ) ;
2023-04-19 13:26:23 -04:00
}
2022-11-22 08:13:54 -05:00
2023-11-22 04:08:33 -05:00
// Ignore disabled task
if ( Task . bTaskEnabled = = false )
2022-11-22 08:13:54 -05:00
{
2023-11-22 04:08:33 -05:00
STATETREE_LOG ( VeryVerbose , TEXT ( " %*sSkipped 'EnterState' for disabled Task: '%s' " ) , UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * Task . Name . ToString ( ) ) ;
continue ;
}
const bool bShouldCallStateChange = CurrentTransition . ChangeType = = EStateTreeStateChangeType : : Changed
| | ( CurrentTransition . ChangeType = = EStateTreeStateChangeType : : Sustained & & Task . bShouldStateChangeOnReselect ) ;
if ( bIsEnteringState & & bShouldCallStateChange )
{
STATETREE_LOG ( Verbose , TEXT ( " %*s Task '%s' " ) , Index * UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * Task . Name . ToString ( ) ) ;
EStateTreeRunStatus Status = EStateTreeRunStatus : : Unset ;
2022-11-22 08:13:54 -05:00
{
2023-11-22 04:08:33 -05:00
QUICK_SCOPE_CYCLE_COUNTER ( StateTree_Task_EnterState ) ;
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_Task_EnterState ) ;
Status = Task . EnterState ( * this , CurrentTransition ) ;
}
STATETREE_TRACE_TASK_EVENT ( TaskIndex , TaskInstanceView , EStateTreeTraceEventType : : OnEntered , Status ) ;
if ( Status ! = EStateTreeRunStatus : : Running )
{
// Store the first state that completed, will be used to decide where to trigger transitions.
if ( ! Exec . CompletedStateHandle . IsValid ( ) )
{
Exec . CompletedFrameIndex = FStateTreeIndex16 ( FrameIndex ) ;
Exec . CompletedStateHandle = CurrentHandle ;
}
Result = Status ;
}
if ( Status = = EStateTreeRunStatus : : Failed )
{
// Store how far in the enter state we got. This will be used to match the StateCompleted() and ExitState() calls.
Exec . EnterStateFailedFrameIndex = FStateTreeIndex16 ( FrameIndex ) ;
Exec . EnterStateFailedTaskIndex = FStateTreeIndex16 ( TaskIndex ) ;
break ;
2022-11-22 08:13:54 -05:00
}
2022-05-31 04:51:18 -04:00
}
}
2023-08-04 14:55:23 -04:00
2023-11-22 04:08:33 -05:00
if ( bIsEnteringState )
{
STATETREE_TRACE_STATE_EVENT ( CurrentHandle , EStateTreeTraceEventType : : OnEntered ) ;
}
2023-08-04 14:55:23 -04:00
}
2022-05-31 04:51:18 -04:00
}
2023-12-13 10:21:13 -05:00
STATETREE_TRACE_PHASE_END ( EStateTreeUpdatePhase : : EnterStates ) ;
STATETREE_TRACE_ACTIVE_STATES_EVENT ( Exec . ActiveFrames ) ;
2023-11-22 04:08:33 -05:00
2022-05-31 04:51:18 -04:00
return Result ;
}
2022-09-23 20:02:42 -04:00
void FStateTreeExecutionContext : : ExitState ( const FStateTreeTransitionResult & Transition )
2022-05-31 04:51:18 -04:00
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_ExitState ) ;
2023-11-22 04:08:33 -05:00
FStateTreeExecutionState & Exec = GetExecState ( ) ;
if ( Exec . ActiveFrames . IsEmpty ( ) )
2022-05-31 04:51:18 -04:00
{
return ;
}
// 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 ;
2023-11-22 04:08:33 -05:00
struct FExitStateCall
2023-11-17 07:41:08 -05:00
{
2023-11-22 04:08:33 -05:00
FExitStateCall ( ) = default ;
FExitStateCall ( const EStateTreeStateChangeType InChangeType , const bool bInShouldCall )
: ChangeType ( InChangeType )
, bShouldCall ( bInShouldCall )
2022-05-31 04:51:18 -04:00
{
}
2023-11-22 04:08:33 -05:00
EStateTreeStateChangeType ChangeType = EStateTreeStateChangeType : : None ;
bool bShouldCall = false ;
} ;
2023-11-17 11:42:17 -05:00
2023-11-22 04:08:33 -05:00
TArray < FExitStateCall , TConcurrentLinearArrayAllocator < FDefaultBlockAllocationTag > > ExitStateCalls ;
for ( int32 FrameIndex = 0 ; FrameIndex < Exec . ActiveFrames . Num ( ) ; FrameIndex + + )
{
2023-11-30 07:03:20 -05:00
FStateTreeExecutionFrame * CurrentParentFrame = FrameIndex > 0 ? & Exec . ActiveFrames [ FrameIndex - 1 ] : nullptr ;
2023-11-22 04:08:33 -05:00
FStateTreeExecutionFrame & CurrentFrame = Exec . ActiveFrames [ FrameIndex ] ;
const UStateTree * CurrentStateTree = CurrentFrame . StateTree ;
2023-11-30 07:03:20 -05:00
FCurrentlyProcessedFrameScope FrameScope ( * this , CurrentParentFrame , CurrentFrame ) ;
2023-11-22 04:08:33 -05:00
const FStateTreeExecutionFrame * NextFrame = nullptr ;
if ( Transition . NextActiveFrames . IsValidIndex ( FrameIndex )
& & Transition . NextActiveFrames [ FrameIndex ] . IsSameFrame ( CurrentFrame ) )
2022-05-31 04:51:18 -04:00
{
2023-11-22 04:08:33 -05:00
NextFrame = & Transition . NextActiveFrames [ FrameIndex ] ;
2023-11-17 11:42:17 -05:00
}
2023-11-17 07:41:08 -05:00
2024-06-25 02:37:11 -04:00
const bool bShouldCallOnEvaluatorsAndGlobalTasks = NextFrame = = nullptr & & CurrentFrame . bIsGlobalFrame ;
ExitStateCalls . Emplace ( EStateTreeStateChangeType : : Changed , bShouldCallOnEvaluatorsAndGlobalTasks ) ;
if ( bShouldCallOnEvaluatorsAndGlobalTasks )
{
for ( int32 EvalIndex = CurrentStateTree - > EvaluatorsBegin ; EvalIndex < ( CurrentStateTree - > EvaluatorsBegin + CurrentStateTree - > EvaluatorsNum ) ; EvalIndex + + )
{
const FStateTreeEvaluatorBase & Eval = CurrentStateTree - > Nodes [ EvalIndex ] . Get < const FStateTreeEvaluatorBase > ( ) ;
const FStateTreeDataView EvalInstanceView = GetDataView ( CurrentParentFrame , CurrentFrame , Eval . InstanceDataHandle ) ;
FNodeInstanceDataScope DataScope ( * this , Eval . InstanceDataHandle , EvalInstanceView ) ;
if ( Eval . BindingsBatch . IsValid ( ) )
{
CopyBatchOnActiveInstances ( CurrentParentFrame , CurrentFrame , EvalInstanceView , Eval . BindingsBatch ) ;
}
}
for ( int32 TaskIndex = CurrentStateTree - > GlobalTasksBegin ; TaskIndex < ( CurrentStateTree - > GlobalTasksBegin + CurrentStateTree - > GlobalTasksNum ) ; TaskIndex + + )
{
const FStateTreeTaskBase & Task = CurrentStateTree - > Nodes [ TaskIndex ] . Get < const FStateTreeTaskBase > ( ) ;
const FStateTreeDataView TaskInstanceView = GetDataView ( CurrentParentFrame , CurrentFrame , Task . InstanceDataHandle ) ;
FNodeInstanceDataScope DataScope ( * this , Task . InstanceDataHandle , TaskInstanceView ) ;
if ( Task . BindingsBatch . IsValid ( ) & & Task . bShouldCopyBoundPropertiesOnExitState )
{
CopyBatchOnActiveInstances ( CurrentParentFrame , CurrentFrame , TaskInstanceView , Task . BindingsBatch ) ;
}
}
}
2023-11-22 04:08:33 -05:00
for ( int32 Index = 0 ; Index < CurrentFrame . ActiveStates . Num ( ) ; Index + + )
2023-11-17 11:42:17 -05:00
{
2023-11-22 04:08:33 -05:00
const FStateTreeStateHandle CurrentHandle = CurrentFrame . ActiveStates [ Index ] ;
const FStateTreeStateHandle NextHandle = NextFrame ? NextFrame - > ActiveStates . GetStateSafe ( Index ) : FStateTreeStateHandle : : Invalid ;
const FCompactStateTreeState & State = CurrentStateTree - > States [ CurrentHandle . Index ] ;
2023-11-17 11:42:17 -05:00
2023-11-22 04:08:33 -05:00
FCurrentlyProcessedStateScope StateScope ( * this , CurrentHandle ) ;
2023-11-30 07:03:20 -05:00
if ( State . Type = = EStateTreeStateType : : Linked
| | State . Type = = EStateTreeStateType : : LinkedAsset )
2023-11-17 07:41:08 -05:00
{
2023-11-22 04:08:33 -05:00
if ( State . ParameterDataHandle . IsValid ( )
& & State . ParameterBindingsBatch . IsValid ( ) )
{
2023-11-30 07:03:20 -05:00
const FStateTreeDataView StateParamsDataView = GetDataView ( CurrentParentFrame , CurrentFrame , State . ParameterDataHandle ) ;
CopyBatchOnActiveInstances ( CurrentParentFrame , CurrentFrame , StateParamsDataView , State . ParameterBindingsBatch ) ;
2023-11-22 04:08:33 -05:00
}
}
const bool bRemainsActive = NextHandle = = CurrentHandle ;
2024-08-21 07:54:18 -04:00
const bool bCurrentIsTarget = CurrentFrame . StateTree = = Transition . SourceStateTree & & CurrentFrame . RootState = = Transition . SourceRootState & & CurrentHandle = = Transition . TargetState ;
bOnTargetBranch = bOnTargetBranch | | bCurrentIsTarget ;
2023-11-22 04:08:33 -05:00
const EStateTreeStateChangeType ChangeType = bRemainsActive ? EStateTreeStateChangeType : : Sustained : EStateTreeStateChangeType : : Changed ;
// Should call ExitState() on this state.
const bool bShouldCall = ! bRemainsActive | | bOnTargetBranch ;
ExitStateCalls . Emplace ( ChangeType , bShouldCall ) ;
// Do property copies, ExitState() is called below.
for ( int32 TaskIndex = State . TasksBegin ; TaskIndex < ( State . TasksBegin + State . TasksNum ) ; TaskIndex + + )
{
const FStateTreeTaskBase & Task = CurrentStateTree - > Nodes [ TaskIndex ] . Get < const FStateTreeTaskBase > ( ) ;
2023-11-30 07:03:20 -05:00
const FStateTreeDataView TaskInstanceView = GetDataView ( CurrentParentFrame , CurrentFrame , Task . InstanceDataHandle ) ;
2023-11-22 04:08:33 -05:00
// Copy bound properties.
if ( Task . BindingsBatch . IsValid ( ) & & Task . bShouldCopyBoundPropertiesOnExitState )
{
2023-11-30 07:03:20 -05:00
CopyBatchOnActiveInstances ( CurrentParentFrame , CurrentFrame , TaskInstanceView , Task . BindingsBatch ) ;
2023-11-22 04:08:33 -05:00
}
2022-05-31 04:51:18 -04:00
}
}
}
// Call in reverse order.
2023-11-22 04:08:33 -05:00
STATETREE_LOG ( Log , TEXT ( " Exit state '%s' (%d) " ) , * DebugGetStatePath ( Exec . ActiveFrames ) , Exec . StateChangeCount ) ;
2023-08-04 14:55:23 -04:00
STATETREE_TRACE_SCOPED_PHASE ( EStateTreeUpdatePhase : : ExitStates ) ;
2022-11-03 14:21:53 -04:00
2022-05-31 04:51:18 -04:00
FStateTreeTransitionResult CurrentTransition = Transition ;
2023-11-22 04:08:33 -05:00
int32 CallIndex = ExitStateCalls . Num ( ) - 1 ;
2022-05-31 04:51:18 -04:00
2023-11-22 04:08:33 -05:00
for ( int32 FrameIndex = Exec . ActiveFrames . Num ( ) - 1 ; FrameIndex > = 0 ; FrameIndex - - )
2022-05-31 04:51:18 -04:00
{
2023-11-30 07:03:20 -05:00
FStateTreeExecutionFrame * CurrentParentFrame = FrameIndex > 0 ? & Exec . ActiveFrames [ FrameIndex - 1 ] : nullptr ;
2023-11-22 04:08:33 -05:00
FStateTreeExecutionFrame & CurrentFrame = Exec . ActiveFrames [ FrameIndex ] ;
const UStateTree * CurrentStateTree = CurrentFrame . StateTree ;
2022-12-02 07:57:31 -05:00
2023-11-30 07:03:20 -05:00
FCurrentlyProcessedFrameScope FrameScope ( * this , CurrentParentFrame , CurrentFrame ) ;
2022-05-31 04:51:18 -04:00
2023-11-22 04:08:33 -05:00
for ( int32 Index = CurrentFrame . ActiveStates . Num ( ) - 1 ; Index > = 0 ; Index - - )
2023-11-17 11:42:17 -05:00
{
2023-11-22 04:08:33 -05:00
const FStateTreeStateHandle CurrentHandle = CurrentFrame . ActiveStates [ Index ] ;
const FCompactStateTreeState & State = CurrentStateTree - > States [ CurrentHandle . Index ] ;
const FExitStateCall & ExitCall = ExitStateCalls [ CallIndex - - ] ;
CurrentTransition . ChangeType = ExitCall . ChangeType ;
STATETREE_LOG ( Log , TEXT ( " %*sState '%s' %s " ) , Index * UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * DebugGetStatePath ( Exec . ActiveFrames , & CurrentFrame , CurrentHandle . Index ) , * UEnum : : GetDisplayValueAsText ( CurrentTransition . ChangeType ) . ToString ( ) ) ;
STATETREE_TRACE_STATE_EVENT ( CurrentHandle , EStateTreeTraceEventType : : OnExiting ) ;
if ( ExitCall . bShouldCall )
2023-11-17 11:42:17 -05:00
{
2023-11-22 04:08:33 -05:00
FCurrentlyProcessedStateScope StateScope ( * this , CurrentHandle ) ;
2023-11-17 07:41:08 -05:00
2023-11-22 04:08:33 -05:00
// Remove any delayed transitions that belong to this state.
Exec . DelayedTransitions . RemoveAllSwap (
[ StateTree = CurrentFrame . StateTree , Begin = State . TransitionsBegin , End = State . TransitionsBegin + State . TransitionsNum ] ( const FStateTreeTransitionDelayedState & DelayedState )
2023-11-17 07:41:08 -05:00
{
2023-11-22 04:08:33 -05:00
return DelayedState . StateTree = = StateTree & & DelayedState . TransitionIndex . Get ( ) > = Begin & & DelayedState . TransitionIndex . Get ( ) < End ;
} ) ;
CurrentTransition . CurrentState = CurrentHandle ;
// Do property copies, ExitState() is called below.
for ( int32 TaskIndex = ( State . TasksBegin + State . TasksNum ) - 1 ; TaskIndex > = State . TasksBegin ; TaskIndex - - )
{
// Call task completed only if EnterState() was called.
// The task order in the tree (BF) allows us to use the comparison.
// Relying here that invalid value of Exec.EnterStateFailedTaskIndex == MAX_uint16.
if ( TaskIndex < = Exec . EnterStateFailedTaskIndex . Get ( ) )
{
const FStateTreeTaskBase & Task = CurrentStateTree - > Nodes [ TaskIndex ] . Get < const FStateTreeTaskBase > ( ) ;
2023-11-30 07:03:20 -05:00
const FStateTreeDataView TaskInstanceView = GetDataView ( CurrentParentFrame , CurrentFrame , Task . InstanceDataHandle ) ;
FNodeInstanceDataScope DataScope ( * this , Task . InstanceDataHandle , TaskInstanceView ) ;
2023-11-22 04:08:33 -05:00
// Ignore disabled task
if ( Task . bTaskEnabled = = false )
{
STATETREE_LOG ( VeryVerbose , TEXT ( " %*sSkipped 'ExitState' for disabled Task: '%s' " ) , UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * Task . Name . ToString ( ) ) ;
continue ;
}
const bool bShouldCallStateChange = CurrentTransition . ChangeType = = EStateTreeStateChangeType : : Changed
| | ( CurrentTransition . ChangeType = = EStateTreeStateChangeType : : Sustained & & Task . bShouldStateChangeOnReselect ) ;
if ( bShouldCallStateChange )
{
STATETREE_LOG ( Verbose , TEXT ( " %*s Task '%s' " ) , Index * UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * Task . Name . ToString ( ) ) ;
{
QUICK_SCOPE_CYCLE_COUNTER ( StateTree_Task_ExitState ) ;
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_Task_ExitState ) ;
Task . ExitState ( * this , CurrentTransition ) ;
}
STATETREE_TRACE_TASK_EVENT ( TaskIndex , TaskInstanceView , EStateTreeTraceEventType : : OnExited , Transition . CurrentRunStatus ) ;
}
2022-09-28 09:55:53 -04:00
}
2022-05-31 04:51:18 -04:00
}
2024-08-02 06:57:58 -04:00
// Call state change events on conditions if needed.
if ( State . bHasStateChangeConditions )
{
for ( int32 ConditionIndex = ( State . EnterConditionsBegin + State . EnterConditionsNum ) - 1 ; ConditionIndex > = State . EnterConditionsBegin ; ConditionIndex - - )
{
const FStateTreeConditionBase & Cond = CurrentFrame . StateTree - > Nodes [ ConditionIndex ] . Get < const FStateTreeConditionBase > ( ) ;
if ( Cond . bHasShouldCallStateChangeEvents )
{
const bool bShouldCallStateChange = CurrentTransition . ChangeType = = EStateTreeStateChangeType : : Changed
| | ( CurrentTransition . ChangeType = = EStateTreeStateChangeType : : Sustained & & Cond . bShouldStateChangeOnReselect ) ;
if ( bShouldCallStateChange )
{
const FStateTreeDataView ConditionInstanceView = GetDataView ( CurrentParentFrame , CurrentFrame , Cond . InstanceDataHandle ) ;
FNodeInstanceDataScope DataScope ( * this , Cond . InstanceDataHandle , ConditionInstanceView ) ;
if ( Cond . BindingsBatch . IsValid ( ) )
{
// Use validated copy, since we test in situations where the sources are not always valid (e.g. enter conditions may try to access inactive parent state).
CopyBatchOnActiveInstances ( CurrentParentFrame , CurrentFrame , ConditionInstanceView , Cond . BindingsBatch ) ;
}
Cond . ExitState ( * this , Transition ) ;
// Reset copied properties that might contain object references.
if ( Cond . BindingsBatch . IsValid ( ) )
{
CurrentFrame . StateTree - > PropertyBindings . ResetObjects ( Cond . BindingsBatch , ConditionInstanceView ) ;
}
}
}
}
}
2022-05-31 04:51:18 -04:00
}
2023-11-17 07:41:08 -05:00
2023-11-22 04:08:33 -05:00
STATETREE_TRACE_STATE_EVENT ( CurrentHandle , EStateTreeTraceEventType : : OnExited ) ;
}
2024-06-25 02:37:11 -04:00
// Frame exit call
{
const FExitStateCall & ExitCall = ExitStateCalls [ CallIndex - - ] ;
if ( ExitCall . bShouldCall )
{
CurrentTransition . ChangeType = ExitCall . ChangeType ;
CallStopOnEvaluatorsAndGlobalTasks ( CurrentParentFrame , CurrentFrame , CurrentTransition ) ;
}
}
2023-11-17 11:42:17 -05:00
}
2023-11-22 04:08:33 -05:00
2022-05-31 04:51:18 -04:00
}
2022-09-23 20:02:42 -04:00
void FStateTreeExecutionContext : : StateCompleted ( )
2022-05-31 04:51:18 -04:00
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_StateCompleted ) ;
2022-09-23 20:02:42 -04:00
const FStateTreeExecutionState & Exec = GetExecState ( ) ;
2022-05-31 04:51:18 -04:00
2023-11-22 04:08:33 -05:00
if ( Exec . ActiveFrames . IsEmpty ( ) )
2022-05-31 04:51:18 -04:00
{
return ;
}
2022-11-03 14:21:53 -04:00
STATETREE_LOG ( Verbose , TEXT ( " State Completed %s (%d) " ) , * UEnum : : GetDisplayValueAsText ( Exec . LastTickStatus ) . ToString ( ) , Exec . StateChangeCount ) ;
2023-12-13 10:21:13 -05:00
STATETREE_TRACE_SCOPED_PHASE ( EStateTreeUpdatePhase : : StateCompleted ) ;
2022-11-03 14:21:53 -04:00
2022-05-31 04:51:18 -04:00
// Call from child towards root to allow to pass results back.
// Note: Completed is assumed to be called immediately after tick or enter state, so there's no property copying.
2023-11-22 04:08:33 -05:00
for ( int32 FrameIndex = Exec . ActiveFrames . Num ( ) - 1 ; FrameIndex > = 0 ; FrameIndex - - )
2022-05-31 04:51:18 -04:00
{
2023-11-30 07:03:20 -05:00
const FStateTreeExecutionFrame * CurrentParentFrame = FrameIndex > 0 ? & Exec . ActiveFrames [ FrameIndex - 1 ] : nullptr ;
2023-11-22 04:08:33 -05:00
const FStateTreeExecutionFrame & CurrentFrame = Exec . ActiveFrames [ FrameIndex ] ;
const UStateTree * CurrentStateTree = CurrentFrame . StateTree ;
2022-05-31 04:51:18 -04:00
2023-11-30 07:03:20 -05:00
FCurrentlyProcessedFrameScope FrameScope ( * this , CurrentParentFrame , CurrentFrame ) ;
2022-05-31 04:51:18 -04:00
2023-11-22 04:08:33 -05:00
if ( FrameIndex < = Exec . EnterStateFailedFrameIndex . Get ( ) )
2022-05-31 04:51:18 -04:00
{
2023-11-22 04:08:33 -05:00
for ( int32 Index = CurrentFrame . ActiveStates . Num ( ) - 1 ; Index > = 0 ; Index - - )
2022-05-31 04:51:18 -04:00
{
2023-11-22 04:08:33 -05:00
const FStateTreeStateHandle CurrentHandle = CurrentFrame . ActiveStates [ Index ] ;
const FCompactStateTreeState & State = CurrentStateTree - > States [ CurrentHandle . Index ] ;
2022-05-31 04:51:18 -04:00
2023-11-22 04:08:33 -05:00
FCurrentlyProcessedStateScope StateScope ( * this , CurrentHandle ) ;
2023-11-17 11:42:17 -05:00
2023-11-22 04:08:33 -05:00
STATETREE_LOG ( Verbose , TEXT ( " %*sState '%s' " ) , Index * UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * DebugGetStatePath ( Exec . ActiveFrames , & CurrentFrame , Index ) ) ;
STATETREE_TRACE_STATE_EVENT ( CurrentHandle , EStateTreeTraceEventType : : OnStateCompleted ) ;
// Notify Tasks
for ( int32 TaskIndex = ( State . TasksBegin + State . TasksNum ) - 1 ; TaskIndex > = State . TasksBegin ; TaskIndex - - )
{
// Call task completed only if EnterState() was called.
// The task order in the tree (BF) allows us to use the comparison.
// Relying here that invalid value of Exec.EnterStateFailedTaskIndex == MAX_uint16.
if ( TaskIndex < = Exec . EnterStateFailedTaskIndex . Get ( ) )
{
const FStateTreeTaskBase & Task = CurrentStateTree - > Nodes [ TaskIndex ] . Get < const FStateTreeTaskBase > ( ) ;
2023-11-30 07:03:20 -05:00
const FStateTreeDataView TaskInstanceView = GetDataView ( CurrentParentFrame , CurrentFrame , Task . InstanceDataHandle ) ;
FNodeInstanceDataScope DataScope ( * this , Task . InstanceDataHandle , TaskInstanceView ) ;
2023-11-22 04:08:33 -05:00
// Ignore disabled task
if ( Task . bTaskEnabled = = false )
{
STATETREE_LOG ( VeryVerbose , TEXT ( " %*sSkipped 'StateCompleted' for disabled Task: '%s' " ) , UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * Task . Name . ToString ( ) ) ;
continue ;
}
2024-08-16 09:07:06 -04:00
2023-11-22 04:08:33 -05:00
STATETREE_LOG ( Verbose , TEXT ( " %*s Task '%s' " ) , Index * UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * Task . Name . ToString ( ) ) ;
Task . StateCompleted ( * this , Exec . LastTickStatus , CurrentFrame . ActiveStates ) ;
}
}
2024-08-02 06:57:58 -04:00
// Call state change events on conditions if needed.
if ( State . bHasStateChangeConditions )
{
for ( int32 ConditionIndex = ( State . EnterConditionsBegin + State . EnterConditionsNum ) - 1 ; ConditionIndex > = State . EnterConditionsBegin ; ConditionIndex - - )
{
const FStateTreeConditionBase & Cond = CurrentFrame . StateTree - > Nodes [ ConditionIndex ] . Get < const FStateTreeConditionBase > ( ) ;
if ( Cond . bHasShouldCallStateChangeEvents )
{
const FStateTreeDataView ConditionInstanceView = GetDataView ( CurrentParentFrame , CurrentFrame , Cond . InstanceDataHandle ) ;
FNodeInstanceDataScope DataScope ( * this , Cond . InstanceDataHandle , ConditionInstanceView ) ;
if ( Cond . BindingsBatch . IsValid ( ) )
{
// Use validated copy, since we test in situations where the sources are not always valid (e.g. enter conditions may try to access inactive parent state).
CopyBatchOnActiveInstances ( CurrentParentFrame , CurrentFrame , ConditionInstanceView , Cond . BindingsBatch ) ;
}
Cond . StateCompleted ( * this , Exec . LastTickStatus , CurrentFrame . ActiveStates ) ;
// Reset copied properties that might contain object references.
if ( Cond . BindingsBatch . IsValid ( ) )
{
CurrentFrame . StateTree - > PropertyBindings . ResetObjects ( Cond . BindingsBatch , ConditionInstanceView ) ;
}
}
}
}
2022-05-31 04:51:18 -04:00
}
}
}
}
2023-05-23 10:46:16 -04:00
EStateTreeRunStatus FStateTreeExecutionContext : : TickEvaluatorsAndGlobalTasks ( const float DeltaTime , const bool bTickGlobalTasks )
2022-05-31 04:51:18 -04:00
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_TickEvaluators ) ;
2023-05-29 10:13:21 -04:00
STATETREE_TRACE_SCOPED_PHASE ( EStateTreeUpdatePhase : : TickingGlobalTasks ) ;
2022-05-31 04:51:18 -04:00
2023-11-22 04:08:33 -05:00
STATETREE_LOG ( VeryVerbose , TEXT ( " Ticking Evaluators & Global Tasks " ) ) ;
2023-01-10 15:44:28 -05:00
2023-11-22 04:08:33 -05:00
FStateTreeExecutionState & Exec = GetExecState ( ) ;
2023-01-10 15:44:28 -05:00
EStateTreeRunStatus Result = EStateTreeRunStatus : : Running ;
2023-11-22 04:08:33 -05:00
for ( int32 FrameIndex = 0 ; FrameIndex < Exec . ActiveFrames . Num ( ) ; FrameIndex + + )
2023-01-10 15:44:28 -05:00
{
2023-11-30 07:03:20 -05:00
FStateTreeExecutionFrame * CurrentParentFrame = FrameIndex > 0 ? & Exec . ActiveFrames [ FrameIndex - 1 ] : nullptr ;
2023-11-22 04:08:33 -05:00
FStateTreeExecutionFrame & CurrentFrame = Exec . ActiveFrames [ FrameIndex ] ;
if ( CurrentFrame . bIsGlobalFrame )
2023-01-10 15:44:28 -05:00
{
2023-11-30 07:03:20 -05:00
FCurrentlyProcessedFrameScope FrameScope ( * this , CurrentParentFrame , CurrentFrame ) ;
2023-06-05 13:12:19 -04:00
2023-11-22 04:08:33 -05:00
const UStateTree * CurrentStateTree = CurrentFrame . StateTree ;
// Tick evaluators
for ( int32 EvalIndex = CurrentStateTree - > EvaluatorsBegin ; EvalIndex < ( CurrentStateTree - > EvaluatorsBegin + CurrentStateTree - > EvaluatorsNum ) ; EvalIndex + + )
2023-06-05 13:12:19 -04:00
{
2023-11-22 04:08:33 -05:00
const FStateTreeEvaluatorBase & Eval = CurrentStateTree - > Nodes [ EvalIndex ] . Get < const FStateTreeEvaluatorBase > ( ) ;
2023-11-30 07:03:20 -05:00
const FStateTreeDataView EvalInstanceView = GetDataView ( CurrentParentFrame , CurrentFrame , Eval . InstanceDataHandle ) ;
FNodeInstanceDataScope DataScope ( * this , Eval . InstanceDataHandle , EvalInstanceView ) ;
2023-11-22 04:08:33 -05:00
// Copy bound properties.
if ( Eval . BindingsBatch . IsValid ( ) )
{
2023-11-30 07:03:20 -05:00
CopyBatchOnActiveInstances ( CurrentParentFrame , CurrentFrame , EvalInstanceView , Eval . BindingsBatch ) ;
2023-11-22 04:08:33 -05:00
}
STATETREE_LOG ( VeryVerbose , TEXT ( " Tick: '%s' " ) , * Eval . Name . ToString ( ) ) ;
{
QUICK_SCOPE_CYCLE_COUNTER ( StateTree_Eval_Tick ) ;
Eval . Tick ( * this , DeltaTime ) ;
STATETREE_TRACE_EVALUATOR_EVENT ( EvalIndex , EvalInstanceView , EStateTreeTraceEventType : : OnTicked ) ;
}
2023-06-05 13:12:19 -04:00
}
2023-11-22 04:08:33 -05:00
if ( bTickGlobalTasks )
2023-01-10 15:44:28 -05:00
{
2023-11-22 04:08:33 -05:00
// Used to stop ticking tasks after one fails, but we still want to keep updating the data views so that property binding works properly.
bool bShouldTickTasks = true ;
2024-04-12 06:04:01 -04:00
const bool bHasEvents = EventQueue & & EventQueue - > HasEvents ( ) ;
2023-01-10 15:44:28 -05:00
2023-11-22 04:08:33 -05:00
for ( int32 TaskIndex = CurrentStateTree - > GlobalTasksBegin ; TaskIndex < ( CurrentStateTree - > GlobalTasksBegin + CurrentStateTree - > GlobalTasksNum ) ; TaskIndex + + )
{
const FStateTreeTaskBase & Task = CurrentStateTree - > Nodes [ TaskIndex ] . Get < const FStateTreeTaskBase > ( ) ;
2023-11-30 07:03:20 -05:00
const FStateTreeDataView TaskInstanceView = GetDataView ( CurrentParentFrame , CurrentFrame , Task . InstanceDataHandle ) ;
FNodeInstanceDataScope DataScope ( * this , Task . InstanceDataHandle , TaskInstanceView ) ;
2023-06-09 15:27:31 -04:00
2023-11-22 04:08:33 -05:00
// Ignore disabled task
if ( Task . bTaskEnabled = = false )
{
STATETREE_LOG ( VeryVerbose , TEXT ( " %*sSkipped 'Tick' for disabled Task: '%s' " ) , UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * Task . Name . ToString ( ) ) ;
continue ;
}
2023-01-10 15:44:28 -05:00
2023-11-22 04:08:33 -05:00
const bool bNeedsTick = bShouldTickTasks & & ( Task . bShouldCallTick | | ( bHasEvents & & Task . bShouldCallTickOnlyOnEvents ) ) ;
STATETREE_LOG ( VeryVerbose , TEXT ( " Tick: '%s' %s " ) , * Task . Name . ToString ( ) , ! bNeedsTick ? TEXT ( " [not ticked] " ) : TEXT ( " " ) ) ;
if ( ! bNeedsTick )
{
continue ;
}
2023-01-10 15:44:28 -05:00
2023-11-22 04:08:33 -05:00
// Copy bound properties.
// Only copy properties when the task is actually ticked, and copy properties at tick is requested.
if ( Task . BindingsBatch . IsValid ( ) & & Task . bShouldCopyBoundPropertiesOnTick )
{
2023-11-30 07:03:20 -05:00
CopyBatchOnActiveInstances ( CurrentParentFrame , CurrentFrame , TaskInstanceView , Task . BindingsBatch ) ;
2023-11-22 04:08:33 -05:00
}
2023-01-10 15:44:28 -05:00
2023-11-22 04:08:33 -05:00
//STATETREE_TRACE_TASK_EVENT(TaskIndex, TaskDataView, EStateTreeTraceEventType::OnTickingTask, EStateTreeRunStatus::Running);
EStateTreeRunStatus TaskResult = EStateTreeRunStatus : : Unset ;
{
QUICK_SCOPE_CYCLE_COUNTER ( StateTree_Task_Tick ) ;
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_Task_Tick ) ;
2023-06-09 15:27:31 -04:00
2023-11-22 04:08:33 -05:00
TaskResult = Task . Tick ( * this , DeltaTime ) ;
}
2023-11-30 07:03:20 -05:00
STATETREE_TRACE_TASK_EVENT ( TaskIndex , TaskInstanceView ,
2023-11-22 04:08:33 -05:00
TaskResult ! = EStateTreeRunStatus : : Running ? EStateTreeTraceEventType : : OnTaskCompleted : EStateTreeTraceEventType : : OnTicked ,
TaskResult ) ;
// If a global task succeeds or fails, it will stop the whole tree.
if ( TaskResult ! = EStateTreeRunStatus : : Running )
{
Result = TaskResult ;
}
if ( TaskResult = = EStateTreeRunStatus : : Failed )
{
bShouldTickTasks = false ;
}
}
2023-01-10 15:44:28 -05:00
}
}
}
return Result ;
2022-05-31 04:51:18 -04:00
}
2023-06-05 06:33:07 -04:00
EStateTreeRunStatus FStateTreeExecutionContext : : StartEvaluatorsAndGlobalTasks ( FStateTreeIndex16 & OutLastInitializedTaskIndex )
2022-05-31 04:51:18 -04:00
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_StartEvaluators ) ;
2023-05-29 10:13:21 -04:00
STATETREE_TRACE_SCOPED_PHASE ( EStateTreeUpdatePhase : : StartGlobalTasks ) ;
2022-05-31 04:51:18 -04:00
2023-11-22 04:08:33 -05:00
STATETREE_LOG ( Verbose , TEXT ( " Start Evaluators & Global tasks " ) ) ;
FStateTreeExecutionState & Exec = GetExecState ( ) ;
2022-05-31 04:51:18 -04:00
2023-01-10 15:44:28 -05:00
OutLastInitializedTaskIndex = FStateTreeIndex16 ( ) ;
2023-06-05 06:33:07 -04:00
EStateTreeRunStatus Result = EStateTreeRunStatus : : Running ;
2023-11-22 04:08:33 -05:00
for ( int32 FrameIndex = 0 ; FrameIndex < Exec . ActiveFrames . Num ( ) ; FrameIndex + + )
2022-05-31 04:51:18 -04:00
{
2023-11-30 07:03:20 -05:00
FStateTreeExecutionFrame * CurrentParentFrame = FrameIndex > 0 ? & Exec . ActiveFrames [ FrameIndex - 1 ] : nullptr ;
2023-11-22 04:08:33 -05:00
FStateTreeExecutionFrame & CurrentFrame = Exec . ActiveFrames [ FrameIndex ] ;
if ( CurrentFrame . bIsGlobalFrame )
2022-05-31 04:51:18 -04:00
{
2023-11-30 07:03:20 -05:00
FCurrentlyProcessedFrameScope FrameScope ( * this , CurrentParentFrame , CurrentFrame ) ;
2023-11-22 04:08:33 -05:00
const UStateTree * CurrentStateTree = CurrentFrame . StateTree ;
2023-08-07 18:06:40 -04:00
2023-11-22 04:08:33 -05:00
// Start evaluators
for ( int32 EvalIndex = CurrentStateTree - > EvaluatorsBegin ; EvalIndex < ( CurrentStateTree - > EvaluatorsBegin + CurrentStateTree - > EvaluatorsNum ) ; EvalIndex + + )
2023-01-10 15:44:28 -05:00
{
2023-11-22 04:08:33 -05:00
const FStateTreeEvaluatorBase & Eval = CurrentStateTree - > Nodes [ EvalIndex ] . Get < const FStateTreeEvaluatorBase > ( ) ;
2023-11-30 07:03:20 -05:00
const FStateTreeDataView EvalInstanceView = GetDataView ( CurrentParentFrame , CurrentFrame , Eval . InstanceDataHandle ) ;
FNodeInstanceDataScope DataScope ( * this , Eval . InstanceDataHandle , EvalInstanceView ) ;
2023-11-22 04:08:33 -05:00
// Copy bound properties.
if ( Eval . BindingsBatch . IsValid ( ) )
{
2023-11-30 07:03:20 -05:00
CopyBatchOnActiveInstances ( CurrentParentFrame , CurrentFrame , EvalInstanceView , Eval . BindingsBatch ) ;
2023-11-22 04:08:33 -05:00
}
STATETREE_LOG ( Verbose , TEXT ( " Start: '%s' " ) , * Eval . Name . ToString ( ) ) ;
{
QUICK_SCOPE_CYCLE_COUNTER ( StateTree_Eval_TreeStart ) ;
Eval . TreeStart ( * this ) ;
STATETREE_TRACE_EVALUATOR_EVENT ( EvalIndex , EvalInstanceView , EStateTreeTraceEventType : : OnTreeStarted ) ;
}
}
// Start Global tasks
// Even if we call Enter/ExitState() on global tasks, they do not enter any specific state.
const FStateTreeTransitionResult Transition = { } ; // Empty transition
for ( int32 TaskIndex = CurrentStateTree - > GlobalTasksBegin ; TaskIndex < ( CurrentStateTree - > GlobalTasksBegin + CurrentStateTree - > GlobalTasksNum ) ; TaskIndex + + )
{
const FStateTreeTaskBase & Task = CurrentStateTree - > Nodes [ TaskIndex ] . Get < const FStateTreeTaskBase > ( ) ;
2023-11-30 07:03:20 -05:00
const FStateTreeDataView TaskInstanceView = GetDataView ( CurrentParentFrame , CurrentFrame , Task . InstanceDataHandle ) ;
FNodeInstanceDataScope DataScope ( * this , Task . InstanceDataHandle , TaskInstanceView ) ;
2023-11-22 04:08:33 -05:00
// Copy bound properties.
if ( Task . BindingsBatch . IsValid ( ) )
{
2023-11-30 07:03:20 -05:00
CopyBatchOnActiveInstances ( CurrentParentFrame , CurrentFrame , TaskInstanceView , Task . BindingsBatch ) ;
2023-11-22 04:08:33 -05:00
}
// Ignore disabled task
if ( Task . bTaskEnabled = = false )
{
STATETREE_LOG ( VeryVerbose , TEXT ( " %*sSkipped 'EnterState' for disabled Task: '%s' " ) , UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * Task . Name . ToString ( ) ) ;
continue ;
}
STATETREE_LOG ( Verbose , TEXT ( " Start: '%s' " ) , * Task . Name . ToString ( ) ) ;
{
QUICK_SCOPE_CYCLE_COUNTER ( StateTree_Task_TreeStart ) ;
const EStateTreeRunStatus TaskStatus = Task . EnterState ( * this , Transition ) ;
2023-11-30 07:03:20 -05:00
STATETREE_TRACE_TASK_EVENT ( TaskIndex , TaskInstanceView , EStateTreeTraceEventType : : OnEntered , TaskStatus ) ;
2023-11-22 04:08:33 -05:00
if ( TaskStatus ! = EStateTreeRunStatus : : Running )
{
OutLastInitializedTaskIndex = FStateTreeIndex16 ( TaskIndex ) ;
Result = TaskStatus ;
break ;
}
}
2023-01-10 15:44:28 -05:00
}
}
}
2023-06-05 06:33:07 -04:00
return Result ;
2023-01-10 15:44:28 -05:00
}
2023-06-05 06:33:07 -04:00
void FStateTreeExecutionContext : : StopEvaluatorsAndGlobalTasks ( const EStateTreeRunStatus CompletionStatus , const FStateTreeIndex16 LastInitializedTaskIndex )
2023-01-10 15:44:28 -05:00
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_StopEvaluators ) ;
2023-05-29 10:13:21 -04:00
STATETREE_TRACE_SCOPED_PHASE ( EStateTreeUpdatePhase : : StopGlobalTasks ) ;
2023-01-10 15:44:28 -05:00
2023-11-22 04:08:33 -05:00
STATETREE_LOG ( Verbose , TEXT ( " Stop Evaluators & Global Tasks " ) ) ;
2023-01-10 15:44:28 -05:00
2023-11-22 04:08:33 -05:00
FStateTreeExecutionState & Exec = GetExecState ( ) ;
// Update bindings
for ( int32 FrameIndex = 0 ; FrameIndex < Exec . ActiveFrames . Num ( ) ; FrameIndex + + )
2023-01-10 15:44:28 -05:00
{
2023-11-30 07:03:20 -05:00
FStateTreeExecutionFrame * CurrentParentFrame = FrameIndex > 0 ? & Exec . ActiveFrames [ FrameIndex - 1 ] : nullptr ;
2023-11-22 04:08:33 -05:00
FStateTreeExecutionFrame & CurrentFrame = Exec . ActiveFrames [ FrameIndex ] ;
if ( CurrentFrame . bIsGlobalFrame )
2023-01-10 15:44:28 -05:00
{
2023-11-30 07:03:20 -05:00
FCurrentlyProcessedFrameScope FrameScope ( * this , CurrentParentFrame , CurrentFrame ) ;
2023-11-22 04:08:33 -05:00
const UStateTree * CurrentStateTree = CurrentFrame . StateTree ;
for ( int32 EvalIndex = CurrentStateTree - > EvaluatorsBegin ; EvalIndex < ( CurrentStateTree - > EvaluatorsBegin + CurrentStateTree - > EvaluatorsNum ) ; EvalIndex + + )
{
const FStateTreeEvaluatorBase & Eval = CurrentStateTree - > Nodes [ EvalIndex ] . Get < const FStateTreeEvaluatorBase > ( ) ;
2023-11-30 07:03:20 -05:00
const FStateTreeDataView EvalInstanceView = GetDataView ( CurrentParentFrame , CurrentFrame , Eval . InstanceDataHandle ) ;
FNodeInstanceDataScope DataScope ( * this , Eval . InstanceDataHandle , EvalInstanceView ) ;
2023-11-22 04:08:33 -05:00
// Copy bound properties.
if ( Eval . BindingsBatch . IsValid ( ) )
{
2023-11-30 07:03:20 -05:00
CopyBatchOnActiveInstances ( CurrentParentFrame , CurrentFrame , EvalInstanceView , Eval . BindingsBatch ) ;
2023-11-22 04:08:33 -05:00
}
}
for ( int32 TaskIndex = CurrentStateTree - > GlobalTasksBegin ; TaskIndex < ( CurrentStateTree - > GlobalTasksBegin + CurrentStateTree - > GlobalTasksNum ) ; TaskIndex + + )
{
const FStateTreeTaskBase & Task = CurrentStateTree - > Nodes [ TaskIndex ] . Get < const FStateTreeTaskBase > ( ) ;
2023-11-30 07:03:20 -05:00
const FStateTreeDataView TaskInstanceView = GetDataView ( CurrentParentFrame , CurrentFrame , Task . InstanceDataHandle ) ;
FNodeInstanceDataScope DataScope ( * this , Task . InstanceDataHandle , TaskInstanceView ) ;
2023-11-22 04:08:33 -05:00
// Copy bound properties.
if ( Task . BindingsBatch . IsValid ( ) & & Task . bShouldCopyBoundPropertiesOnExitState )
{
2023-11-30 07:03:20 -05:00
CopyBatchOnActiveInstances ( CurrentParentFrame , CurrentFrame , TaskInstanceView , Task . BindingsBatch ) ;
2023-11-22 04:08:33 -05:00
}
}
2023-01-10 15:44:28 -05:00
}
}
2023-06-05 06:33:07 -04:00
// Call in reverse order.
FStateTreeTransitionResult Transition ;
Transition . TargetState = FStateTreeStateHandle : : FromCompletionStatus ( CompletionStatus ) ;
Transition . CurrentRunStatus = CompletionStatus ;
2023-01-10 15:44:28 -05:00
2024-06-25 02:37:11 -04:00
bool bIsLastGlobalFrame = true ;
2023-11-22 04:08:33 -05:00
for ( int32 FrameIndex = Exec . ActiveFrames . Num ( ) - 1 ; FrameIndex > = 0 ; FrameIndex - - )
2023-01-10 15:44:28 -05:00
{
2023-11-30 07:03:20 -05:00
const FStateTreeExecutionFrame * CurrentParentFrame = FrameIndex > 0 ? & Exec . ActiveFrames [ FrameIndex - 1 ] : nullptr ;
2023-11-22 04:08:33 -05:00
const FStateTreeExecutionFrame & CurrentFrame = Exec . ActiveFrames [ FrameIndex ] ;
if ( CurrentFrame . bIsGlobalFrame )
2023-06-05 13:12:19 -04:00
{
2024-06-25 02:37:11 -04:00
// LastInitializedTaskIndex belongs to the last frame.
const FStateTreeIndex16 LastTaskToBeStopped = bIsLastGlobalFrame ? LastInitializedTaskIndex : FStateTreeIndex16 : : Invalid ;
CallStopOnEvaluatorsAndGlobalTasks ( CurrentParentFrame , CurrentFrame , Transition , LastTaskToBeStopped ) ;
bIsLastGlobalFrame = false ;
}
}
}
2023-06-05 13:12:19 -04:00
2024-06-25 02:37:11 -04:00
void FStateTreeExecutionContext : : CallStopOnEvaluatorsAndGlobalTasks ( const FStateTreeExecutionFrame * ParentFrame , const FStateTreeExecutionFrame & Frame , const FStateTreeTransitionResult & Transition , const FStateTreeIndex16 LastInitializedTaskIndex /*= FStateTreeIndex16()*/ )
{
check ( Frame . bIsGlobalFrame ) ;
2023-11-22 04:08:33 -05:00
2024-06-25 02:37:11 -04:00
FCurrentlyProcessedFrameScope FrameScope ( * this , ParentFrame , Frame ) ;
const UStateTree * CurrentStateTree = Frame . StateTree ;
for ( int32 TaskIndex = ( CurrentStateTree - > GlobalTasksBegin + CurrentStateTree - > GlobalTasksNum ) - 1 ; TaskIndex > = CurrentStateTree - > GlobalTasksBegin ; TaskIndex - - )
{
const FStateTreeTaskBase & Task = CurrentStateTree - > Nodes [ TaskIndex ] . Get < const FStateTreeTaskBase > ( ) ;
const FStateTreeDataView TaskInstanceView = GetDataView ( ParentFrame , Frame , Task . InstanceDataHandle ) ;
FNodeInstanceDataScope DataScope ( * this , Task . InstanceDataHandle , TaskInstanceView ) ;
// Ignore disabled task
if ( Task . bTaskEnabled = = false )
{
STATETREE_LOG ( VeryVerbose , TEXT ( " %*sSkipped 'ExitState' for disabled Task: '%s' " ) , UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * Task . Name . ToString ( ) ) ;
continue ;
}
// Relying here that invalid value of LastInitializedTaskIndex == MAX_uint16.
if ( TaskIndex < = LastInitializedTaskIndex . Get ( ) )
{
STATETREE_LOG ( Verbose , TEXT ( " Stop: '%s' " ) , * Task . Name . ToString ( ) ) ;
2023-01-10 15:44:28 -05:00
{
2024-06-25 02:37:11 -04:00
QUICK_SCOPE_CYCLE_COUNTER ( StateTree_Task_TreeStop ) ;
Task . ExitState ( * this , Transition ) ;
2023-01-10 15:44:28 -05:00
}
2024-06-25 02:37:11 -04:00
STATETREE_TRACE_TASK_EVENT ( TaskIndex , TaskInstanceView , EStateTreeTraceEventType : : OnExited , Transition . CurrentRunStatus ) ;
}
}
2023-01-10 15:44:28 -05:00
2024-06-25 02:37:11 -04:00
for ( int32 EvalIndex = ( CurrentStateTree - > EvaluatorsBegin + CurrentStateTree - > EvaluatorsNum ) - 1 ; EvalIndex > = CurrentStateTree - > EvaluatorsBegin ; EvalIndex - - )
{
const FStateTreeEvaluatorBase & Eval = CurrentStateTree - > Nodes [ EvalIndex ] . Get < const FStateTreeEvaluatorBase > ( ) ;
const FStateTreeDataView EvalInstanceView = GetDataView ( ParentFrame , Frame , Eval . InstanceDataHandle ) ;
FNodeInstanceDataScope DataScope ( * this , Eval . InstanceDataHandle , EvalInstanceView ) ;
2023-11-30 07:03:20 -05:00
2024-06-25 02:37:11 -04:00
STATETREE_LOG ( Verbose , TEXT ( " Stop: '%s' " ) , * Eval . Name . ToString ( ) ) ;
{
QUICK_SCOPE_CYCLE_COUNTER ( StateTree_Eval_TreeStop ) ;
Eval . TreeStop ( * this ) ;
2023-08-07 18:06:40 -04:00
2024-06-25 02:37:11 -04:00
STATETREE_TRACE_EVALUATOR_EVENT ( EvalIndex , EvalInstanceView , EStateTreeTraceEventType : : OnTreeStopped ) ;
2022-05-31 04:51:18 -04:00
}
}
}
2023-11-30 07:03:20 -05:00
EStateTreeRunStatus FStateTreeExecutionContext : : StartTemporaryEvaluatorsAndGlobalTasks ( const FStateTreeExecutionFrame * CurrentParentFrame , const FStateTreeExecutionFrame & CurrentFrame )
{
if ( ! CurrentFrame . bIsGlobalFrame )
{
return EStateTreeRunStatus : : Failed ;
}
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_StartEvaluators ) ;
// @todo: figure out debugger phase for temporary start.
// STATETREE_TRACE_SCOPED_PHASE(EStateTreeUpdatePhase::StartGlobalTasks);
STATETREE_LOG ( Verbose , TEXT ( " Start Temporary Evaluators & Global tasks while trying to select linked asset: %s " ) , * GetNameSafe ( CurrentFrame . StateTree ) ) ;
FStateTreeExecutionState & Exec = GetExecState ( ) ;
EStateTreeRunStatus Result = EStateTreeRunStatus : : Running ;
FCurrentlyProcessedFrameScope FrameScope ( * this , CurrentParentFrame , CurrentFrame ) ;
const UStateTree * CurrentStateTree = CurrentFrame . StateTree ;
// Start evaluators
for ( int32 EvalIndex = CurrentStateTree - > EvaluatorsBegin ; EvalIndex < ( CurrentStateTree - > EvaluatorsBegin + CurrentStateTree - > EvaluatorsNum ) ; EvalIndex + + )
{
const FStateTreeEvaluatorBase & Eval = CurrentStateTree - > Nodes [ EvalIndex ] . Get < const FStateTreeEvaluatorBase > ( ) ;
FStateTreeDataView EvalInstanceView = GetDataViewOrTemporary ( CurrentParentFrame , CurrentFrame , Eval . InstanceDataHandle ) ;
bool bWasCreated = false ;
if ( ! EvalInstanceView . IsValid ( ) )
{
EvalInstanceView = AddTemporaryInstance ( CurrentFrame , FStateTreeIndex16 ( EvalIndex ) , Eval . InstanceDataHandle , CurrentFrame . StateTree - > DefaultInstanceData . GetStruct ( Eval . InstanceTemplateIndex . Get ( ) ) ) ;
check ( EvalInstanceView . IsValid ( ) ) ;
bWasCreated = true ;
}
FNodeInstanceDataScope DataScope ( * this , Eval . InstanceDataHandle , EvalInstanceView ) ;
// Copy bound properties.
if ( Eval . BindingsBatch . IsValid ( ) )
{
CopyBatchOnActiveInstances ( CurrentParentFrame , CurrentFrame , EvalInstanceView , Eval . BindingsBatch ) ;
}
if ( bWasCreated )
{
STATETREE_LOG ( Verbose , TEXT ( " Start: '%s' " ) , * Eval . Name . ToString ( ) ) ;
{
QUICK_SCOPE_CYCLE_COUNTER ( StateTree_Eval_TreeStart ) ;
Eval . TreeStart ( * this ) ;
STATETREE_TRACE_EVALUATOR_EVENT ( EvalIndex , EvalInstanceView , EStateTreeTraceEventType : : OnTreeStarted ) ;
}
}
}
// Start Global tasks
// Even if we call Enter/ExitState() on global tasks, they do not enter any specific state.
const FStateTreeTransitionResult Transition = { } ; // Empty transition
for ( int32 TaskIndex = CurrentStateTree - > GlobalTasksBegin ; TaskIndex < ( CurrentStateTree - > GlobalTasksBegin + CurrentStateTree - > GlobalTasksNum ) ; TaskIndex + + )
{
const FStateTreeTaskBase & Task = CurrentStateTree - > Nodes [ TaskIndex ] . Get < const FStateTreeTaskBase > ( ) ;
// Ignore disabled task
if ( Task . bTaskEnabled = = false )
{
STATETREE_LOG ( VeryVerbose , TEXT ( " %*sSkipped 'EnterState' for disabled Task: '%s' " ) , UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * Task . Name . ToString ( ) ) ;
continue ;
}
FStateTreeDataView TaskDataView = GetDataViewOrTemporary ( CurrentParentFrame , CurrentFrame , Task . InstanceDataHandle ) ;
bool bWasCreated = false ;
if ( ! TaskDataView . IsValid ( ) )
{
TaskDataView = AddTemporaryInstance ( CurrentFrame , FStateTreeIndex16 ( TaskIndex ) , Task . InstanceDataHandle , CurrentFrame . StateTree - > DefaultInstanceData . GetStruct ( Task . InstanceTemplateIndex . Get ( ) ) ) ;
check ( TaskDataView . IsValid ( ) )
bWasCreated = true ;
}
FNodeInstanceDataScope DataScope ( * this , Task . InstanceDataHandle , TaskDataView ) ;
// Copy bound properties.
if ( Task . BindingsBatch . IsValid ( ) )
{
CopyBatchOnActiveInstances ( CurrentParentFrame , CurrentFrame , TaskDataView , Task . BindingsBatch ) ;
}
STATETREE_LOG ( Verbose , TEXT ( " Start: '%s' " ) , * Task . Name . ToString ( ) ) ;
if ( bWasCreated )
{
QUICK_SCOPE_CYCLE_COUNTER ( StateTree_Task_TreeStart ) ;
const EStateTreeRunStatus TaskStatus = Task . EnterState ( * this , Transition ) ;
STATETREE_TRACE_TASK_EVENT ( TaskIndex , TaskDataView , EStateTreeTraceEventType : : OnEntered , TaskStatus ) ;
if ( TaskStatus ! = EStateTreeRunStatus : : Running )
{
Result = TaskStatus ;
break ;
}
}
}
return Result ;
}
2024-06-25 04:09:43 -04:00
void FStateTreeExecutionContext : : StopTemporaryEvaluatorsAndGlobalTasks ( const FStateTreeExecutionFrame * CurrentParentFrame , const FStateTreeExecutionFrame & CurrentFrame )
2023-11-30 07:03:20 -05:00
{
// @todo: figure out debugger phase for temporary stop.
2024-06-25 04:09:43 -04:00
STATETREE_LOG ( Verbose , TEXT ( " Stop Temporary Evaluators & Global tasks " ) ) ;
2023-11-30 07:03:20 -05:00
// Create temporary transition to stop the unused global tasks and evaluators.
constexpr EStateTreeRunStatus CompletionStatus = EStateTreeRunStatus : : Stopped ;
FStateTreeTransitionResult Transition ;
Transition . TargetState = FStateTreeStateHandle : : FromCompletionStatus ( CompletionStatus ) ;
Transition . CurrentRunStatus = CompletionStatus ;
2024-06-25 04:09:43 -04:00
TArrayView < FStateTreeTemporaryInstanceData > TempInstances = InstanceDataStorage - > GetMutableTemporaryInstances ( ) ;
2023-11-30 07:03:20 -05:00
for ( int32 Index = TempInstances . Num ( ) - 1 ; Index > = 0 ; Index - - )
{
FStateTreeTemporaryInstanceData & TempInstance = TempInstances [ Index ] ;
2024-06-25 04:09:43 -04:00
if ( TempInstance . StateTree ! = CurrentFrame . StateTree | | TempInstance . RootState ! = CurrentFrame . RootState )
{
continue ;
}
2023-11-30 07:03:20 -05:00
if ( TempInstance . OwnerNodeIndex . IsValid ( )
& & TempInstance . Instance . IsValid ( ) )
{
FStateTreeDataView NodeInstanceView ;
if ( FStateTreeInstanceObjectWrapper * Wrapper = TempInstance . Instance . GetMutablePtr < FStateTreeInstanceObjectWrapper > ( ) )
{
NodeInstanceView = FStateTreeDataView ( Wrapper - > InstanceObject ) ;
}
else
{
NodeInstanceView = FStateTreeDataView ( TempInstance . Instance ) ;
}
2024-06-25 04:09:43 -04:00
FCurrentlyProcessedFrameScope FrameScope ( * this , CurrentParentFrame , CurrentFrame ) ;
2023-11-30 07:03:20 -05:00
FNodeInstanceDataScope DataScope ( * this , TempInstance . DataHandle , NodeInstanceView ) ;
2024-06-25 04:09:43 -04:00
FConstStructView NodeView = CurrentFrame . StateTree - > Nodes [ TempInstance . OwnerNodeIndex . Get ( ) ] ;
2023-11-30 07:03:20 -05:00
if ( const FStateTreeTaskBase * Task = NodeView . GetPtr < const FStateTreeTaskBase > ( ) )
{
STATETREE_LOG ( Verbose , TEXT ( " Stop: '%s' " ) , * Task - > Name . ToString ( ) ) ;
{
QUICK_SCOPE_CYCLE_COUNTER ( StateTree_Task_TreeStop ) ;
Task - > ExitState ( * this , Transition ) ;
}
STATETREE_TRACE_TASK_EVENT ( TempInstance . OwnerNodeIndex . Get ( ) , NodeInstanceView , EStateTreeTraceEventType : : OnExited , Transition . CurrentRunStatus ) ;
}
else if ( const FStateTreeEvaluatorBase * Eval = NodeView . GetPtr < const FStateTreeEvaluatorBase > ( ) )
{
STATETREE_LOG ( Verbose , TEXT ( " Stop: '%s' " ) , * Eval - > Name . ToString ( ) ) ;
{
QUICK_SCOPE_CYCLE_COUNTER ( StateTree_Eval_TreeStop ) ;
Eval - > TreeStop ( * this ) ;
STATETREE_TRACE_EVALUATOR_EVENT ( TempInstance . OwnerNodeIndex . Get ( ) , NodeInstanceView , EStateTreeTraceEventType : : OnTreeStopped ) ;
}
}
}
}
}
2022-09-23 20:02:42 -04:00
EStateTreeRunStatus FStateTreeExecutionContext : : TickTasks ( const float DeltaTime )
2022-05-31 04:51:18 -04:00
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_TickTasks ) ;
2023-05-29 10:13:21 -04:00
STATETREE_TRACE_SCOPED_PHASE ( EStateTreeUpdatePhase : : TickingTasks ) ;
2022-05-31 04:51:18 -04:00
2022-11-22 08:13:54 -05:00
FStateTreeExecutionState & Exec = GetExecState ( ) ;
2022-05-31 04:51:18 -04:00
2023-11-22 04:08:33 -05:00
if ( Exec . ActiveFrames . IsEmpty ( ) )
2022-05-31 04:51:18 -04:00
{
return EStateTreeRunStatus : : Failed ;
}
EStateTreeRunStatus Result = EStateTreeRunStatus : : Running ;
int32 NumTotalTasks = 0 ;
2024-04-12 06:04:01 -04:00
const bool bHasEvents = EventQueue & & EventQueue - > HasEvents ( ) ;
2022-05-31 04:51:18 -04:00
2023-11-22 04:08:33 -05:00
Exec . CompletedFrameIndex = FStateTreeIndex16 : : Invalid ;
2022-11-22 08:13:54 -05:00
Exec . CompletedStateHandle = FStateTreeStateHandle : : Invalid ;
2023-01-10 15:44:28 -05:00
// Used to stop ticking tasks after one fails, but we still want to keep updating the data views so that property binding works properly.
2022-09-27 10:55:54 -04:00
bool bShouldTickTasks = true ;
2022-11-03 14:21:53 -04:00
2023-11-22 04:08:33 -05:00
STATETREE_CLOG ( Exec . ActiveFrames . Num ( ) > 0 , VeryVerbose , TEXT ( " Ticking Tasks " ) ) ;
2022-11-03 14:21:53 -04:00
2023-11-22 04:08:33 -05:00
for ( int32 FrameIndex = 0 ; FrameIndex < Exec . ActiveFrames . Num ( ) ; FrameIndex + + )
2022-05-31 04:51:18 -04:00
{
2023-11-30 07:03:20 -05:00
const FStateTreeExecutionFrame * CurrentParentFrame = FrameIndex > 0 ? & Exec . ActiveFrames [ FrameIndex - 1 ] : nullptr ;
2023-11-22 04:08:33 -05:00
const FStateTreeExecutionFrame & CurrentFrame = Exec . ActiveFrames [ FrameIndex ] ;
const UStateTree * CurrentStateTree = CurrentFrame . StateTree ;
2022-05-31 04:51:18 -04:00
2023-11-30 07:03:20 -05:00
FCurrentlyProcessedFrameScope FrameScope ( * this , CurrentParentFrame , CurrentFrame ) ;
2023-08-04 14:55:23 -04:00
2023-11-22 04:08:33 -05:00
for ( int32 Index = 0 ; Index < CurrentFrame . ActiveStates . Num ( ) ; Index + + )
2022-05-31 04:51:18 -04:00
{
2023-11-22 04:08:33 -05:00
const FStateTreeStateHandle CurrentHandle = CurrentFrame . ActiveStates [ Index ] ;
const FCompactStateTreeState & State = CurrentStateTree - > States [ CurrentHandle . Index ] ;
2023-11-17 11:42:17 -05:00
2023-11-22 04:08:33 -05:00
FCurrentlyProcessedStateScope StateScope ( * this , CurrentHandle ) ;
STATETREE_TRACE_SCOPED_STATE ( CurrentHandle ) ;
2023-11-17 11:42:17 -05:00
2023-11-22 04:08:33 -05:00
STATETREE_CLOG ( State . TasksNum > 0 , VeryVerbose , TEXT ( " %*sState '%s' " ) , Index * UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * DebugGetStatePath ( Exec . ActiveFrames , & CurrentFrame , Index ) ) ;
2023-11-30 07:03:20 -05:00
if ( State . Type = = EStateTreeStateType : : Linked
| | State . Type = = EStateTreeStateType : : LinkedAsset )
2023-11-17 11:42:17 -05:00
{
2023-11-22 04:08:33 -05:00
if ( State . ParameterDataHandle . IsValid ( )
& & State . ParameterBindingsBatch . IsValid ( ) )
2023-11-17 11:42:17 -05:00
{
2023-11-30 07:03:20 -05:00
const FStateTreeDataView StateParamsDataView = GetDataView ( CurrentParentFrame , CurrentFrame , State . ParameterDataHandle ) ;
CopyBatchOnActiveInstances ( CurrentParentFrame , CurrentFrame , StateParamsDataView , State . ParameterBindingsBatch ) ;
2023-11-17 11:42:17 -05:00
}
}
2023-11-22 04:08:33 -05:00
// Update Tasks data and tick if possible (ie. if no task has yet failed and so bShouldTickTasks is true)
for ( int32 TaskIndex = State . TasksBegin ; TaskIndex < ( State . TasksBegin + State . TasksNum ) ; TaskIndex + + )
2023-11-17 11:42:17 -05:00
{
2023-11-22 04:08:33 -05:00
const FStateTreeTaskBase & Task = CurrentStateTree - > Nodes [ TaskIndex ] . Get < const FStateTreeTaskBase > ( ) ;
2023-11-30 07:03:20 -05:00
const FStateTreeDataView TaskInstanceView = GetDataView ( CurrentParentFrame , CurrentFrame , Task . InstanceDataHandle ) ;
FNodeInstanceDataScope DataScope ( * this , Task . InstanceDataHandle , TaskInstanceView ) ;
2023-11-22 04:08:33 -05:00
// Ignore disabled task
if ( Task . bTaskEnabled = = false )
{
STATETREE_LOG ( VeryVerbose , TEXT ( " %*sSkipped 'Tick' for disabled Task: '%s' " ) , UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * Task . Name . ToString ( ) ) ;
continue ;
}
const bool bNeedsTick = bShouldTickTasks & & ( Task . bShouldCallTick | | ( bHasEvents & & Task . bShouldCallTickOnlyOnEvents ) ) ;
STATETREE_LOG ( VeryVerbose , TEXT ( " %*s Tick: '%s' %s " ) , Index * UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * Task . Name . ToString ( ) , ! bNeedsTick ? TEXT ( " [not ticked] " ) : TEXT ( " " ) ) ;
if ( ! bNeedsTick )
{
continue ;
}
// Copy bound properties.
// Only copy properties when the task is actually ticked, and copy properties at tick is requested.
if ( Task . BindingsBatch . IsValid ( ) & & Task . bShouldCopyBoundPropertiesOnTick )
{
2023-11-30 07:03:20 -05:00
CopyBatchOnActiveInstances ( CurrentParentFrame , CurrentFrame , TaskInstanceView , Task . BindingsBatch ) ;
2023-11-22 04:08:33 -05:00
}
//STATETREE_TRACE_TASK_EVENT(TaskIndex, TaskDataView, EStateTreeTraceEventType::OnTickingTask, EStateTreeRunStatus::Running);
EStateTreeRunStatus TaskResult = EStateTreeRunStatus : : Unset ;
{
QUICK_SCOPE_CYCLE_COUNTER ( StateTree_Task_Tick ) ;
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_Task_Tick ) ;
TaskResult = Task . Tick ( * this , DeltaTime ) ;
}
2023-11-30 07:03:20 -05:00
STATETREE_TRACE_TASK_EVENT ( TaskIndex , TaskInstanceView ,
2023-11-22 04:08:33 -05:00
TaskResult ! = EStateTreeRunStatus : : Running ? EStateTreeTraceEventType : : OnTaskCompleted : EStateTreeTraceEventType : : OnTicked ,
TaskResult ) ;
// TODO: Add more control over which states can control the failed/succeeded result.
if ( TaskResult ! = EStateTreeRunStatus : : Running )
{
// Store the first state that completed, will be used to decide where to trigger transitions.
if ( ! Exec . CompletedStateHandle . IsValid ( ) )
{
Exec . CompletedFrameIndex = FStateTreeIndex16 ( FrameIndex ) ;
Exec . CompletedStateHandle = CurrentHandle ;
}
Result = TaskResult ;
}
if ( TaskResult = = EStateTreeRunStatus : : Failed )
{
bShouldTickTasks = false ;
}
2023-11-17 11:42:17 -05:00
}
2023-11-22 04:08:33 -05:00
NumTotalTasks + = State . TasksNum ;
2023-11-17 11:42:17 -05:00
}
2022-05-31 04:51:18 -04:00
}
if ( NumTotalTasks = = 0 )
{
// No tasks, done ticking.
Result = EStateTreeRunStatus : : Succeeded ;
2023-11-22 04:08:33 -05:00
Exec . CompletedFrameIndex = FStateTreeIndex16 ( 0 ) ;
2024-09-10 10:26:02 -04:00
Exec . CompletedStateHandle = Exec . ActiveFrames . Num ( ) > 0 ? Exec . ActiveFrames [ 0 ] . ActiveStates . Last ( ) : FStateTreeStateHandle : : Invalid ;
2022-05-31 04:51:18 -04:00
}
return Result ;
}
2023-11-30 07:03:20 -05:00
bool FStateTreeExecutionContext : : TestAllConditions ( const FStateTreeExecutionFrame * CurrentParentFrame , const FStateTreeExecutionFrame & CurrentFrame , const int32 ConditionsOffset , const int32 ConditionsNum )
2022-05-31 04:51:18 -04:00
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_TestConditions ) ;
2023-11-22 04:08:33 -05:00
if ( ConditionsNum = = 0 )
2022-05-31 04:51:18 -04:00
{
return true ;
}
2024-05-13 20:48:39 -04:00
TStaticArray < EStateTreeExpressionOperand , UE : : StateTree : : MaxExpressionIndent + 1 > Operands ( InPlace , EStateTreeExpressionOperand : : Copy ) ;
TStaticArray < bool , UE : : StateTree : : MaxExpressionIndent + 1 > Values ( InPlace , false ) ;
2022-05-31 04:51:18 -04:00
int32 Level = 0 ;
for ( int32 Index = 0 ; Index < ConditionsNum ; Index + + )
{
2023-10-05 08:21:08 -04:00
const int32 ConditionIndex = ConditionsOffset + Index ;
2023-11-22 04:08:33 -05:00
const FStateTreeConditionBase & Cond = CurrentFrame . StateTree - > Nodes [ ConditionIndex ] . Get < const FStateTreeConditionBase > ( ) ;
2023-11-30 07:03:20 -05:00
const FStateTreeDataView ConditionInstanceView = GetDataView ( CurrentParentFrame , CurrentFrame , Cond . InstanceDataHandle ) ;
FNodeInstanceDataScope DataScope ( * this , Cond . InstanceDataHandle , ConditionInstanceView ) ;
2023-06-05 13:12:19 -04:00
bool bValue = false ;
if ( Cond . EvaluationMode = = EStateTreeConditionEvaluationMode : : Evaluated )
2022-05-31 04:51:18 -04:00
{
2023-06-05 13:12:19 -04:00
// Copy bound properties.
if ( Cond . BindingsBatch . IsValid ( ) )
2022-05-31 04:51:18 -04:00
{
2023-11-22 04:08:33 -05:00
// Use validated copy, since we test in situations where the sources are not always valid (e.g. enter conditions may try to access inactive parent state).
2023-11-30 07:03:20 -05:00
if ( ! CopyBatchWithValidation ( CurrentParentFrame , CurrentFrame , ConditionInstanceView , Cond . BindingsBatch ) )
2023-06-05 13:12:19 -04:00
{
// If the source data cannot be accessed, the whole expression evaluates to false.
2024-01-08 09:52:36 -05:00
STATETREE_TRACE_CONDITION_EVENT ( ConditionIndex , ConditionInstanceView , EStateTreeTraceEventType : : InternalForcedFailure ) ;
2024-04-22 11:34:53 -04:00
STATETREE_TRACE_LOG_EVENT ( Warning , TEXT ( " Evaluation forced to false: source data cannot be accessed (e.g. enter conditions trying to access inactive parent state) " ) ) ;
2023-06-05 13:12:19 -04:00
Values [ 0 ] = false ;
break ;
}
}
bValue = Cond . TestCondition ( * this ) ;
2023-11-30 07:03:20 -05:00
STATETREE_TRACE_CONDITION_EVENT ( ConditionIndex , ConditionInstanceView , bValue ? EStateTreeTraceEventType : : Passed : EStateTreeTraceEventType : : Failed ) ;
2023-06-05 13:12:19 -04:00
// Reset copied properties that might contain object references.
if ( Cond . BindingsBatch . IsValid ( ) )
{
2023-11-30 07:03:20 -05:00
CurrentFrame . StateTree - > PropertyBindings . ResetObjects ( Cond . BindingsBatch , ConditionInstanceView ) ;
2022-05-31 04:51:18 -04:00
}
}
2023-06-05 13:12:19 -04:00
else
2022-11-30 07:17:26 -05:00
{
2024-01-08 09:52:36 -05:00
bValue = Cond . EvaluationMode = = EStateTreeConditionEvaluationMode : : ForcedTrue ;
STATETREE_TRACE_CONDITION_EVENT ( ConditionIndex , FStateTreeDataView { } , bValue ? EStateTreeTraceEventType : : ForcedSuccess : EStateTreeTraceEventType : : ForcedFailure ) ;
2022-11-30 07:17:26 -05:00
}
2022-05-31 04:51:18 -04:00
const int32 DeltaIndent = Cond . DeltaIndent ;
const int32 OpenParens = FMath : : Max ( 0 , DeltaIndent ) + 1 ; // +1 for the current value that is stored at the empty slot at the top of the value stack.
const int32 ClosedParens = FMath : : Max ( 0 , - DeltaIndent ) + 1 ;
// Store the operand to apply when merging higher level down when returning to this level.
// @todo: remove this conditions in 5.1, needs resaving existing StateTrees.
2024-05-13 20:48:39 -04:00
const EStateTreeExpressionOperand Operand = Index = = 0 ? EStateTreeExpressionOperand : : Copy : Cond . Operand ;
2022-05-31 04:51:18 -04:00
Operands [ Level ] = Operand ;
// Store current value at the top of the stack.
Level + = OpenParens ;
Values [ Level ] = bValue ;
// Evaluate and merge down values based on closed braces.
// The current value is placed in parens (see +1 above), which makes merging down and applying the new value consistent.
// The default operand is copy, so if the value is needed immediately, it is just copied down, or if we're on the same level,
// the operand storing above gives handles with the right logic.
for ( int32 Paren = 0 ; Paren < ClosedParens ; Paren + + )
{
Level - - ;
switch ( Operands [ Level ] )
{
2024-05-13 20:48:39 -04:00
case EStateTreeExpressionOperand : : Copy :
2022-05-31 04:51:18 -04:00
Values [ Level ] = Values [ Level + 1 ] ;
break ;
2024-05-13 20:48:39 -04:00
case EStateTreeExpressionOperand : : And :
2022-05-31 04:51:18 -04:00
Values [ Level ] & = Values [ Level + 1 ] ;
break ;
2024-05-13 20:48:39 -04:00
case EStateTreeExpressionOperand : : Or :
2022-05-31 04:51:18 -04:00
Values [ Level ] | = Values [ Level + 1 ] ;
break ;
}
2024-05-13 20:48:39 -04:00
Operands [ Level ] = EStateTreeExpressionOperand : : Copy ;
2022-05-31 04:51:18 -04:00
}
}
return Values [ 0 ] ;
}
2024-08-19 15:41:09 -04:00
float FStateTreeExecutionContext : : EvaluateUtility ( const FStateTreeExecutionFrame * CurrentParentFrame , const FStateTreeExecutionFrame & CurrentFrame , const int32 ConsiderationsOffset , const int32 ConsiderationsNum , const float StateWeight )
2024-06-13 22:57:49 -04:00
{
// @todo: Tracing support
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_EvaluateUtility ) ;
if ( ConsiderationsNum = = 0 )
{
return .0f ;
}
TStaticArray < EStateTreeExpressionOperand , UE : : StateTree : : MaxExpressionIndent + 1 > Operands ( InPlace , EStateTreeExpressionOperand : : Copy ) ;
TStaticArray < float , UE : : StateTree : : MaxExpressionIndent + 1 > Values ( InPlace , false ) ;
int32 Level = 0 ;
float Value = .0f ;
for ( int32 Index = 0 ; Index < ConsiderationsNum ; Index + + )
{
const int32 ConsiderationIndex = ConsiderationsOffset + Index ;
const FStateTreeConsiderationBase & Consideration = CurrentFrame . StateTree - > Nodes [ ConsiderationIndex ] . Get < const FStateTreeConsiderationBase > ( ) ;
const FStateTreeDataView ConsiderationInstanceView = GetDataView ( CurrentParentFrame , CurrentFrame , Consideration . InstanceDataHandle ) ;
FNodeInstanceDataScope DataScope ( * this , Consideration . InstanceDataHandle , ConsiderationInstanceView ) ;
// Copy bound properties.
if ( Consideration . BindingsBatch . IsValid ( ) )
{
// Use validated copy, since we test in situations where the sources are not always valid (e.g. considerations may try to access inactive parent state).
if ( ! CopyBatchWithValidation ( CurrentParentFrame , CurrentFrame , ConsiderationInstanceView , Consideration . BindingsBatch ) )
{
// If the source data cannot be accessed, the whole expression evaluates to zero.
Values [ 0 ] = .0f ;
break ;
}
}
2024-09-10 10:26:02 -04:00
Value = Consideration . GetNormalizedScore ( * this ) ;
2024-06-13 22:57:49 -04:00
// Reset copied properties that might contain object references.
if ( Consideration . BindingsBatch . IsValid ( ) )
{
CurrentFrame . StateTree - > PropertyBindings . ResetObjects ( Consideration . BindingsBatch , ConsiderationInstanceView ) ;
}
const int32 DeltaIndent = Consideration . 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.
const EStateTreeExpressionOperand Operand = Index = = 0 ? EStateTreeExpressionOperand : : Copy : Consideration . Operand ;
Operands [ Level ] = Operand ;
// Store current value at the top of the stack.
Level + = OpenParens ;
Values [ Level ] = Value ;
// Evaluate and merge down values based on closed braces.
// The current value is placed in parens (see +1 above), which makes merging down and applying the new value consistent.
// The default operand is copy, so if the value is needed immediately, it is just copied down, or if we're on the same level,
// the operand storing above gives handles with the right logic.
for ( int32 Paren = 0 ; Paren < ClosedParens ; Paren + + )
{
Level - - ;
switch ( Operands [ Level ] )
{
case EStateTreeExpressionOperand : : Copy :
Values [ Level ] = Values [ Level + 1 ] ;
break ;
case EStateTreeExpressionOperand : : And :
Values [ Level ] = FMath : : Min ( Values [ Level ] , Values [ Level + 1 ] ) ;
break ;
case EStateTreeExpressionOperand : : Or :
Values [ Level ] = FMath : : Max ( Values [ Level ] , Values [ Level + 1 ] ) ;
break ;
}
Operands [ Level ] = EStateTreeExpressionOperand : : Copy ;
}
}
2024-08-19 15:41:09 -04:00
return StateWeight * Values [ 0 ] ;
2024-06-13 22:57:49 -04:00
}
2024-06-04 03:56:38 -04:00
void FStateTreeExecutionContext : : EvaluatePropertyFunctionsOnActiveInstances ( const FStateTreeExecutionFrame * CurrentParentFrame , const FStateTreeExecutionFrame & CurrentFrame , FStateTreeIndex16 FuncsBegin , uint16 FuncsNum )
{
for ( int32 FuncIndex = FuncsBegin . Get ( ) ; FuncIndex < FuncsBegin . Get ( ) + FuncsNum ; + + FuncIndex )
{
const FStateTreePropertyFunctionBase & Func = CurrentFrame . StateTree - > Nodes [ FuncIndex ] . Get < const FStateTreePropertyFunctionBase > ( ) ;
const FStateTreeDataView FuncInstanceView = GetDataView ( CurrentParentFrame , CurrentFrame , Func . InstanceDataHandle ) ;
FNodeInstanceDataScope DataScope ( * this , Func . InstanceDataHandle , FuncInstanceView ) ;
// Copy bound properties.
if ( Func . BindingsBatch . IsValid ( ) )
{
// Use validated copy, since we test in situations where the sources are not always valid (e.g. enter conditions may try to access inactive parent state).
CopyBatchOnActiveInstances ( CurrentParentFrame , CurrentFrame , FuncInstanceView , Func . BindingsBatch ) ;
}
Func . Execute ( * this ) ;
// Reset copied properties that might contain object references.
if ( Func . BindingsBatch . IsValid ( ) )
{
CurrentFrame . StateTree - > PropertyBindings . ResetObjects ( Func . BindingsBatch , FuncInstanceView ) ;
}
}
}
void FStateTreeExecutionContext : : EvaluatePropertyFunctionsWithValidation ( const FStateTreeExecutionFrame * CurrentParentFrame , const FStateTreeExecutionFrame & CurrentFrame , FStateTreeIndex16 FuncsBegin , uint16 FuncsNum )
{
for ( int32 FuncIndex = FuncsBegin . Get ( ) ; FuncIndex < FuncsBegin . Get ( ) + FuncsNum ; + + FuncIndex )
{
const FStateTreePropertyFunctionBase & Func = CurrentFrame . StateTree - > Nodes [ FuncIndex ] . Get < const FStateTreePropertyFunctionBase > ( ) ;
const FStateTreeDataView FuncInstanceView = GetDataView ( CurrentParentFrame , CurrentFrame , Func . InstanceDataHandle ) ;
FNodeInstanceDataScope DataScope ( * this , Func . InstanceDataHandle , FuncInstanceView ) ;
// Copy bound properties.
if ( Func . BindingsBatch . IsValid ( ) )
{
// Use validated copy, since we test in situations where the sources are not always valid (e.g. enter conditions may try to access inactive parent state).
CopyBatchWithValidation ( CurrentParentFrame , CurrentFrame , FuncInstanceView , Func . BindingsBatch ) ;
}
Func . Execute ( * this ) ;
// Reset copied properties that might contain object references.
if ( Func . BindingsBatch . IsValid ( ) )
{
CurrentFrame . StateTree - > PropertyBindings . ResetObjects ( Func . BindingsBatch , FuncInstanceView ) ;
}
}
}
2022-11-01 15:11:19 -04:00
FString FStateTreeExecutionContext : : DebugGetEventsAsString ( ) const
{
2024-08-16 09:07:06 -04:00
TStringBuilder < 512 > StrBuilder ;
2024-04-12 06:04:01 -04:00
if ( EventQueue )
2022-11-01 15:11:19 -04:00
{
2024-04-12 06:04:01 -04:00
for ( const FStateTreeSharedEvent & Event : EventQueue - > GetEventsView ( ) )
2022-11-01 15:11:19 -04:00
{
2024-04-12 06:04:01 -04:00
if ( Event . IsValid ( ) )
{
if ( StrBuilder . Len ( ) > 0 )
{
2024-08-16 09:07:06 -04:00
StrBuilder < < TEXT ( " , " ) ;
2024-04-12 06:04:01 -04:00
}
2024-03-11 04:54:44 -04:00
2024-04-12 06:04:01 -04:00
const bool bHasTag = Event - > Tag . IsValid ( ) ;
const bool bHasPayload = Event - > Payload . GetScriptStruct ( ) ! = nullptr ;
if ( bHasTag | | bHasPayload )
{
2024-08-16 09:07:06 -04:00
StrBuilder < < ( TEXT ( ' ( ' ) ) ;
2024-04-12 06:04:01 -04:00
if ( bHasTag )
{
2024-08-16 09:07:06 -04:00
StrBuilder < < TEXT ( " Tag: ' " ) ;
StrBuilder < < Event - > Tag . GetTagName ( ) ;
StrBuilder < < TEXT ( ' \' ' ) ;
2024-04-12 06:04:01 -04:00
}
if ( bHasTag & & bHasPayload )
{
2024-08-16 09:07:06 -04:00
StrBuilder < < TEXT ( " , " ) ;
2024-04-12 06:04:01 -04:00
}
if ( bHasPayload )
{
2024-08-16 09:07:06 -04:00
StrBuilder < < TEXT ( " Payload: ' " ) ;
StrBuilder < < Event - > Payload . GetScriptStruct ( ) - > GetFName ( ) ;
StrBuilder < < TEXT ( ' \' ' ) ;
2024-04-12 06:04:01 -04:00
}
2024-08-16 09:07:06 -04:00
StrBuilder < < TEXT ( " ) " ) ;
2024-04-12 06:04:01 -04:00
}
}
}
2022-11-01 15:11:19 -04:00
}
2024-08-16 09:07:06 -04:00
2024-04-12 06:04:01 -04:00
return StrBuilder . ToString ( ) ;
2022-11-01 15:11:19 -04:00
}
2024-03-11 04:54:44 -04:00
bool FStateTreeExecutionContext : : RequestTransition (
const FStateTreeExecutionFrame & CurrentFrame ,
const FStateTreeStateHandle NextState ,
const EStateTreeTransitionPriority Priority ,
const FStateTreeSharedEvent * TransitionEvent ,
const EStateTreeSelectionFallback Fallback )
2022-12-02 07:57:31 -05:00
{
2023-01-23 12:48:04 -05:00
// Skip lower priority transitions.
if ( NextTransition . Priority > = Priority )
2022-12-02 07:57:31 -05:00
{
2023-01-23 12:48:04 -05:00
return false ;
2022-12-02 07:57:31 -05:00
}
2023-01-23 12:48:04 -05:00
if ( NextState . IsCompletionState ( ) )
{
2023-11-22 04:08:33 -05:00
SetupNextTransition ( CurrentFrame , NextState , Priority ) ;
STATETREE_LOG ( Verbose , TEXT ( " Transition on state '%s' -> state '%s' " ) ,
* GetSafeStateName ( CurrentFrame , CurrentFrame . ActiveStates . Last ( ) ) , * NextState . Describe ( ) ) ;
2023-01-23 12:48:04 -05:00
return true ;
}
if ( ! NextState . IsValid ( ) )
{
// NotSet is no-operation, but can be used to mask a transition at parent state. Returning unset keeps updating current state.
2023-11-22 04:08:33 -05:00
SetupNextTransition ( CurrentFrame , FStateTreeStateHandle : : Invalid , Priority ) ;
2022-12-02 07:57:31 -05:00
return true ;
}
2024-03-11 04:54:44 -04:00
FStateSelectionResult StateSelectionResult ;
if ( SelectState ( CurrentFrame , NextState , StateSelectionResult , TransitionEvent , Fallback ) )
2023-01-23 12:48:04 -05:00
{
2023-11-22 04:08:33 -05:00
SetupNextTransition ( CurrentFrame , NextState , Priority ) ;
2024-03-11 04:54:44 -04:00
NextTransition . NextActiveFrames = StateSelectionResult . GetSelectedFrames ( ) ;
NextTransition . NextActiveFrameEvents = StateSelectionResult . GetFramesStateSelectionEvents ( ) ;
2023-01-23 12:48:04 -05:00
2024-04-12 06:04:01 -04:00
// Consume events from states, if required.
for ( int32 FrameIndex = 0 ; FrameIndex < NextTransition . NextActiveFrames . Num ( ) ; FrameIndex + + )
{
const FStateTreeExecutionFrame & Frame = NextTransition . NextActiveFrames [ FrameIndex ] ;
const FStateTreeFrameStateSelectionEvents & FrameEvents = NextTransition . NextActiveFrameEvents [ FrameIndex ] ;
for ( int32 StateIndex = 0 ; StateIndex < Frame . ActiveStates . Num ( ) ; StateIndex + + )
{
if ( FrameEvents . Events [ StateIndex ] . IsValid ( ) )
{
const FCompactStateTreeState & State = Frame . StateTree - > States [ StateIndex ] ;
if ( State . bConsumeEventOnSelect )
{
ConsumeEvent ( FrameEvents . Events [ StateIndex ] ) ;
}
}
}
}
2023-01-23 12:48:04 -05:00
STATETREE_LOG ( Verbose , TEXT ( " Transition on state '%s' -[%s]-> state '%s' " ) ,
2023-11-22 04:08:33 -05:00
* GetSafeStateName ( CurrentFrame , CurrentFrame . ActiveStates . Last ( ) ) ,
* GetSafeStateName ( CurrentFrame , NextState ) ,
* GetSafeStateName ( NextTransition . NextActiveFrames . Last ( ) , NextTransition . NextActiveFrames . Last ( ) . ActiveStates . Last ( ) ) ) ;
2023-01-23 12:48:04 -05:00
return true ;
}
2022-12-02 07:57:31 -05:00
return false ;
}
2023-11-22 04:08:33 -05:00
void FStateTreeExecutionContext : : SetupNextTransition ( const FStateTreeExecutionFrame & CurrentFrame , const FStateTreeStateHandle NextState , const EStateTreeTransitionPriority Priority )
2023-09-22 09:53:34 -04:00
{
const FStateTreeExecutionState & Exec = GetExecState ( ) ;
NextTransition . CurrentRunStatus = Exec . LastTickStatus ;
NextTransition . SourceState = CurrentlyProcessedState ;
2023-11-22 04:08:33 -05:00
NextTransition . SourceStateTree = CurrentFrame . StateTree ;
NextTransition . SourceRootState = CurrentFrame . ActiveStates . GetStateSafe ( 0 ) ;
2023-09-22 09:53:34 -04:00
NextTransition . TargetState = NextState ;
2023-11-22 04:08:33 -05:00
NextTransition . Priority = Priority ;
FStateTreeExecutionFrame & NewFrame = NextTransition . NextActiveFrames . AddDefaulted_GetRef ( ) ;
NewFrame . StateTree = CurrentFrame . StateTree ;
NewFrame . RootState = CurrentFrame . RootState ;
2023-09-22 09:53:34 -04:00
if ( NextState = = FStateTreeStateHandle : : Invalid )
{
2023-11-22 04:08:33 -05:00
NewFrame . ActiveStates = { } ;
2023-09-22 09:53:34 -04:00
}
else
{
2023-11-22 04:08:33 -05:00
NewFrame . ActiveStates = FStateTreeActiveStates ( NextState ) ;
2023-09-22 09:53:34 -04:00
}
}
2023-01-23 12:48:04 -05:00
bool FStateTreeExecutionContext : : TriggerTransitions ( )
2022-05-31 04:51:18 -04:00
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_TriggerTransition ) ;
2023-05-29 10:13:21 -04:00
STATETREE_TRACE_SCOPED_PHASE ( EStateTreeUpdatePhase : : TriggerTransitions ) ;
2022-05-31 04:51:18 -04:00
2023-01-23 12:48:04 -05:00
FAllowDirectTransitionsScope AllowDirectTransitionsScope ( * this ) ; // Set flag for the scope of this function to allow direct transitions without buffering.
2022-09-23 20:02:42 -04:00
FStateTreeExecutionState & Exec = GetExecState ( ) ;
2022-11-22 08:13:54 -05:00
2024-04-12 06:04:01 -04:00
if ( EventQueue & & EventQueue - > HasEvents ( ) )
2023-05-09 12:57:56 -04:00
{
2024-04-22 11:34:53 -04:00
STATETREE_LOG_AND_TRACE ( Verbose , Log , TEXT ( " Trigger transitions with events: %s " ) , * DebugGetEventsAsString ( ) ) ;
2023-05-09 12:57:56 -04:00
}
2022-12-02 07:57:31 -05:00
2023-01-23 12:48:04 -05:00
NextTransition . Reset ( ) ;
2022-11-23 09:22:14 -05:00
2022-12-02 07:57:31 -05:00
//
2023-01-23 12:48:04 -05:00
// Process transition requests
//
for ( const FStateTreeTransitionRequest & Request : InstanceData . GetTransitionRequests ( ) )
{
2023-11-22 04:08:33 -05:00
// Find frame associated with the request.
2024-02-29 03:19:31 -05:00
const FStateTreeExecutionFrame * ParentFrame = nullptr ;
const FStateTreeExecutionFrame * CurrentFrame = FindFrame ( Request . SourceStateTree , Request . SourceRootState , Exec . ActiveFrames , ParentFrame ) ;
2023-11-22 04:08:33 -05:00
2024-02-29 03:19:31 -05:00
if ( CurrentFrame )
2023-11-22 04:08:33 -05:00
{
2024-02-29 03:19:31 -05:00
if ( RequestTransition ( * CurrentFrame , Request . TargetState , Request . Priority ) )
2023-11-22 04:08:33 -05:00
{
NextTransitionSource = FStateTreeTransitionSource ( EStateTreeTransitionSourceType : : ExternalRequest , Request . TargetState , Request . Priority ) ;
}
2023-08-04 14:55:23 -04:00
}
2023-01-23 12:48:04 -05:00
}
InstanceData . ResetTransitionRequests ( ) ;
2023-11-22 04:08:33 -05:00
2023-01-23 12:48:04 -05:00
//
2024-04-12 06:04:01 -04:00
// Collect tick, event, and task based transitions.
2022-12-02 07:57:31 -05:00
//
2024-04-12 06:04:01 -04:00
struct FTransitionHandler
{
FTransitionHandler ( ) = default ;
FTransitionHandler ( const uint8 InFrameIndex , const FStateTreeStateHandle InStateHandle , const EStateTreeTransitionPriority InPriority )
: StateHandle ( InStateHandle )
, TaskIndex ( FStateTreeIndex16 : : Invalid )
, FrameIndex ( InFrameIndex )
, Priority ( InPriority )
{
}
FTransitionHandler ( const uint8 InFrameIndex , const FStateTreeStateHandle InStateHandle , const FStateTreeIndex16 InTaskIndex , const EStateTreeTransitionPriority InPriority )
: StateHandle ( InStateHandle )
, TaskIndex ( InTaskIndex )
, FrameIndex ( InFrameIndex )
, Priority ( InPriority )
{
}
FStateTreeStateHandle StateHandle ;
FStateTreeIndex16 TaskIndex = FStateTreeIndex16 : : Invalid ;
uint8 FrameIndex = 0 ;
EStateTreeTransitionPriority Priority = EStateTreeTransitionPriority : : Normal ;
bool operator < ( const FTransitionHandler & Other ) const
{
// Highest priority first.
return Priority > Other . Priority ;
}
} ;
TArray < FTransitionHandler , TInlineAllocator < 16 > > TransitionHandlers ;
2023-11-22 04:08:33 -05:00
if ( Exec . ActiveFrames . Num ( ) > 0 )
2022-05-31 04:51:18 -04:00
{
2024-04-12 06:04:01 -04:00
for ( int32 FrameIndex = Exec . ActiveFrames . Num ( ) - 1 ; FrameIndex > = 0 ; FrameIndex - - )
2023-01-23 12:48:04 -05:00
{
2023-11-22 04:08:33 -05:00
FStateTreeExecutionFrame & CurrentFrame = Exec . ActiveFrames [ FrameIndex ] ;
const UStateTree * CurrentStateTree = CurrentFrame . StateTree ;
2023-01-23 12:48:04 -05:00
2023-11-22 04:08:33 -05:00
for ( int32 StateIndex = CurrentFrame . ActiveStates . Num ( ) - 1 ; StateIndex > = 0 ; StateIndex - - )
2023-01-23 12:48:04 -05:00
{
2023-11-22 04:08:33 -05:00
const FStateTreeStateHandle StateHandle = CurrentFrame . ActiveStates [ StateIndex ] ;
const FCompactStateTreeState & State = CurrentStateTree - > States [ StateHandle . Index ] ;
2023-06-05 13:12:19 -04:00
2023-11-22 04:08:33 -05:00
// Do not process any transitions from a disabled state
if ( ! State . bEnabled )
2023-06-20 13:49:25 -04:00
{
continue ;
}
2023-11-17 11:42:17 -05:00
2024-04-12 06:04:01 -04:00
// Transition tasks.
2023-11-22 04:08:33 -05:00
if ( State . bHasTransitionTasks )
2023-06-13 04:36:07 -04:00
{
2023-11-22 04:08:33 -05:00
for ( int32 TaskIndex = ( State . TasksBegin + State . TasksNum ) - 1 ; TaskIndex > = State . TasksBegin ; TaskIndex - - )
2023-11-17 07:41:08 -05:00
{
2023-11-22 04:08:33 -05:00
const FStateTreeTaskBase & Task = CurrentStateTree - > Nodes [ TaskIndex ] . Get < const FStateTreeTaskBase > ( ) ;
if ( Task . bShouldAffectTransitions )
{
2024-04-12 06:04:01 -04:00
TransitionHandlers . Emplace ( ( uint8 ) FrameIndex , StateHandle , FStateTreeIndex16 ( TaskIndex ) , Task . TransitionHandlingPriority ) ;
2023-11-17 07:41:08 -05:00
}
2023-06-20 13:49:25 -04:00
}
2023-06-13 04:36:07 -04:00
}
2023-11-22 04:08:33 -05:00
2024-04-12 06:04:01 -04:00
// Regular transitions on state
if ( State . TransitionsNum > 0 )
2023-11-17 07:41:08 -05:00
{
2024-04-12 06:04:01 -04:00
TransitionHandlers . Emplace ( ( uint8 ) FrameIndex , StateHandle , EStateTreeTransitionPriority : : Normal ) ;
2023-11-17 07:41:08 -05:00
}
2023-01-23 12:48:04 -05:00
}
2023-11-22 04:08:33 -05:00
if ( CurrentFrame . bIsGlobalFrame )
2023-11-17 11:42:17 -05:00
{
2024-04-12 06:04:01 -04:00
// Global transition tasks.
2023-11-22 04:08:33 -05:00
if ( CurrentFrame . StateTree - > bHasGlobalTransitionTasks )
{
for ( int32 TaskIndex = ( CurrentStateTree - > GlobalTasksBegin + CurrentStateTree - > GlobalTasksNum ) - 1 ; TaskIndex > = CurrentFrame . StateTree - > GlobalTasksBegin ; TaskIndex - - )
{
2024-04-12 06:04:01 -04:00
const FStateTreeTaskBase & Task = CurrentStateTree - > Nodes [ TaskIndex ] . Get < const FStateTreeTaskBase > ( ) ;
2023-11-22 04:08:33 -05:00
if ( Task . bShouldAffectTransitions )
{
2024-04-12 06:04:01 -04:00
TransitionHandlers . Emplace ( ( uint8 ) FrameIndex , FStateTreeStateHandle ( ) , FStateTreeIndex16 ( TaskIndex ) , Task . TransitionHandlingPriority ) ;
}
}
}
}
}
// Sort by priority and adding order.
TransitionHandlers . StableSort ( ) ;
}
//
// Process task and state transitions in priority order.
//
for ( const FTransitionHandler & Handler : TransitionHandlers )
{
const int32 FrameIndex = Handler . FrameIndex ;
FStateTreeExecutionFrame * CurrentParentFrame = FrameIndex > 0 ? & Exec . ActiveFrames [ FrameIndex - 1 ] : nullptr ;
FStateTreeExecutionFrame & CurrentFrame = Exec . ActiveFrames [ FrameIndex ] ;
const UStateTree * CurrentStateTree = CurrentFrame . StateTree ;
FCurrentlyProcessedFrameScope FrameScope ( * this , CurrentParentFrame , CurrentFrame ) ;
FCurrentlyProcessedStateScope StateScope ( * this , Handler . StateHandle ) ;
STATETREE_TRACE_SCOPED_STATE ( Handler . StateHandle ) ;
if ( Handler . TaskIndex . IsValid ( ) )
{
const FStateTreeTaskBase & Task = CurrentStateTree - > Nodes [ Handler . TaskIndex . Get ( ) ] . Get < const FStateTreeTaskBase > ( ) ;
// Ignore disabled task
if ( Task . bTaskEnabled = = false )
{
STATETREE_LOG ( VeryVerbose , TEXT ( " %*sSkipped 'TriggerTransitions' for disabled Task: '%s' " ) , UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * Task . Name . ToString ( ) ) ;
continue ;
}
const FStateTreeDataView TaskInstanceView = GetDataView ( CurrentParentFrame , CurrentFrame , Task . InstanceDataHandle ) ;
FNodeInstanceDataScope DataScope ( * this , Task . InstanceDataHandle , TaskInstanceView ) ;
STATETREE_LOG ( VeryVerbose , TEXT ( " %*sTriggerTransitions: '%s' " ) , UE : : StateTree : : DebugIndentSize , TEXT ( " " ) , * Task . Name . ToString ( ) ) ;
STATETREE_TRACE_TASK_EVENT ( Handler . TaskIndex . Get ( ) , TaskInstanceView , EStateTreeTraceEventType : : OnEvaluating , EStateTreeRunStatus : : Running ) ;
check ( TaskInstanceView . IsValid ( ) ) ;
Task . TriggerTransitions ( * this ) ;
}
else if ( Handler . StateHandle . IsValid ( ) )
{
const FCompactStateTreeState & State = CurrentStateTree - > States [ Handler . StateHandle . Index ] ;
// Transitions
for ( uint8 i = 0 ; i < State . TransitionsNum ; i + + )
{
// All transition conditions must pass
const int16 TransitionIndex = State . TransitionsBegin + i ;
const FCompactStateTransition & Transition = CurrentStateTree - > Transitions [ TransitionIndex ] ;
// Skip disabled transitions
if ( Transition . bTransitionEnabled = = false )
{
continue ;
}
// No need to test the transition if same or higher priority transition has already been processed.
if ( Transition . Priority < = NextTransition . Priority )
{
continue ;
}
// Skip completion transitions
if ( EnumHasAnyFlags ( Transition . Trigger , EStateTreeTransitionTrigger : : OnStateCompleted ) )
{
continue ;
}
// If a delayed transition has passed the delay, and remove it from the queue, and try trigger it.
if ( Transition . HasDelay ( ) )
{
bool bTriggeredDelayedTransition = false ;
const TArray < FStateTreeTransitionDelayedState , TInlineAllocator < 8 > > ExpiredDelayedStates = Exec . FindAndRemoveExpiredDelayedTransitions ( CurrentFrame . StateTree , FStateTreeIndex16 ( TransitionIndex ) ) ;
for ( const FStateTreeTransitionDelayedState & DelayedState : ExpiredDelayedStates )
{
STATETREE_LOG ( Verbose , TEXT ( " Passed delayed transition from '%s' (%s) -> '%s' " ) ,
* GetSafeStateName ( CurrentFrame , CurrentFrame . ActiveStates . Last ( ) ) , * State . Name . ToString ( ) , * GetSafeStateName ( CurrentFrame , Transition . State ) ) ;
// Trigger Delayed Transition when the delay has passed.
if ( RequestTransition ( CurrentFrame , Transition . State , Transition . Priority , & DelayedState . CapturedEvent , Transition . Fallback ) )
{
// If the transition was successfully requested with a specific event, consume and remove the event, it's been used.
if ( DelayedState . CapturedEvent . IsValid ( ) & & Transition . bConsumeEventOnSelect )
{
ConsumeEvent ( DelayedState . CapturedEvent ) ;
}
NextTransitionSource = FStateTreeTransitionSource ( FStateTreeIndex16 ( TransitionIndex ) , Transition . State , Transition . Priority ) ;
bTriggeredDelayedTransition = true ;
break ;
}
}
if ( bTriggeredDelayedTransition )
{
continue ;
}
}
TArray < const FStateTreeSharedEvent * , TInlineAllocator < 8 > > TransitionEvents ;
if ( Transition . Trigger = = EStateTreeTransitionTrigger : : OnEvent )
{
check ( Transition . RequiredEvent . IsValid ( ) ) ;
TConstArrayView < FStateTreeSharedEvent > EventsQueue = GetEventsToProcessView ( ) ;
for ( const FStateTreeSharedEvent & Event : EventsQueue )
{
check ( Event . IsValid ( ) ) ;
if ( Transition . RequiredEvent . DoesEventMatchDesc ( * Event ) )
{
TransitionEvents . Emplace ( & Event ) ;
2023-11-22 04:08:33 -05:00
}
}
}
2024-04-12 06:04:01 -04:00
else if ( Transition . Trigger = = EStateTreeTransitionTrigger : : OnTick )
{
2024-04-22 07:02:22 -04:00
// Dummy event to make sure we iterate to loop below once.
TransitionEvents . Emplace ( nullptr ) ;
2024-04-12 06:04:01 -04:00
}
2023-11-22 04:08:33 -05:00
2024-04-12 06:04:01 -04:00
for ( const FStateTreeSharedEvent * TransitionEvent : TransitionEvents )
{
bool bPassed = false ;
{
FCurrentlyProcessedTransitionEventScope TransitionEventScope ( * this , TransitionEvent ? TransitionEvent - > Get ( ) : nullptr ) ;
STATETREE_TRACE_TRANSITION_EVENT ( FStateTreeTransitionSource ( FStateTreeIndex16 ( TransitionIndex ) , Transition . State , Transition . Priority ) , EStateTreeTraceEventType : : OnEvaluating ) ;
STATETREE_TRACE_SCOPED_PHASE ( EStateTreeUpdatePhase : : TransitionConditions ) ;
bPassed = TestAllConditions ( CurrentParentFrame , CurrentFrame , Transition . ConditionsBegin , Transition . ConditionsNum ) ;
}
if ( bPassed )
{
// If the transitions is delayed, set up the delay.
if ( Transition . HasDelay ( ) )
{
uint32 TransitionEventHash = 0u ;
if ( TransitionEvent & & TransitionEvent - > IsValid ( ) )
{
TransitionEventHash = GetTypeHash ( * TransitionEvent - > Get ( ) ) ;
}
const bool bIsDelayedTransitionExisting = Exec . DelayedTransitions . ContainsByPredicate ( [ StateTree = CurrentFrame . StateTree , TransitionIndex , TransitionEventHash ] ( const FStateTreeTransitionDelayedState & DelayedState )
{
return DelayedState . StateTree = = StateTree & & DelayedState . TransitionIndex . Get ( ) = = TransitionIndex & & DelayedState . CapturedEventHash = = TransitionEventHash ;
} ) ;
if ( ! bIsDelayedTransitionExisting )
{
// Initialize new delayed transition.
2024-08-05 03:21:02 -04:00
const float DelayDuration = Transition . Delay . GetRandomDuration ( Exec . RandomStream ) ;
2024-04-12 06:04:01 -04:00
if ( DelayDuration > 0.0f )
{
FStateTreeTransitionDelayedState & DelayedState = Exec . DelayedTransitions . AddDefaulted_GetRef ( ) ;
DelayedState . StateTree = CurrentFrame . StateTree ;
DelayedState . TransitionIndex = FStateTreeIndex16 ( TransitionIndex ) ;
DelayedState . TimeLeft = DelayDuration ;
2024-04-22 07:02:22 -04:00
if ( TransitionEvent & & TransitionEvent - > IsValid ( ) )
{
DelayedState . CapturedEvent = * TransitionEvent ;
DelayedState . CapturedEventHash = TransitionEventHash ;
}
2024-04-12 06:04:01 -04:00
BeginDelayedTransition ( DelayedState ) ;
STATETREE_LOG ( Verbose , TEXT ( " Delayed transition triggered from '%s' (%s) -> '%s' %.1fs " ) ,
* GetSafeStateName ( CurrentFrame , CurrentFrame . ActiveStates . Last ( ) ) , * State . Name . ToString ( ) , * GetSafeStateName ( CurrentFrame , Transition . State ) , DelayedState . TimeLeft ) ;
// Delay state added, skip requesting the transition.
continue ;
}
// Fallthrough to request transition if duration was zero.
}
else
{
// We get here if the transitions re-triggers during the delay, on which case we'll just ignore it.
continue ;
}
}
if ( RequestTransition ( CurrentFrame , Transition . State , Transition . Priority , TransitionEvent , Transition . Fallback ) )
{
// If the transition was successfully requested with a specific event, consume and remove the event, it's been used.
if ( TransitionEvent & & Transition . bConsumeEventOnSelect )
{
ConsumeEvent ( * TransitionEvent ) ;
}
NextTransitionSource = FStateTreeTransitionSource ( FStateTreeIndex16 ( TransitionIndex ) , Transition . State , Transition . Priority ) ;
break ;
}
}
}
2023-11-17 11:42:17 -05:00
}
}
}
2023-11-17 07:41:08 -05:00
2024-04-12 06:04:01 -04:00
// All events have had the change to be reacted to, clear the event queue (if this instance owns it).
if ( InstanceData . IsOwningEventQueue ( ) )
{
EventQueue - > Reset ( ) ;
}
2023-11-22 04:08:33 -05:00
2022-12-02 07:57:31 -05:00
//
// Check state completion transitions.
//
2023-09-22 09:53:34 -04:00
bool bProcessSubTreeCompletion = true ;
2023-01-23 12:48:04 -05:00
if ( NextTransition . Priority = = EStateTreeTransitionPriority : : None
& & Exec . LastTickStatus ! = EStateTreeRunStatus : : Running )
2022-05-31 04:51:18 -04:00
{
2023-11-22 04:08:33 -05:00
// Start from the last completed state if specified.
const int32 FrameStartIndex = Exec . CompletedFrameIndex . IsValid ( ) ? Exec . CompletedFrameIndex . AsInt32 ( ) : ( Exec . ActiveFrames . Num ( ) - 1 ) ;
check ( FrameStartIndex > = 0 & & FrameStartIndex < Exec . ActiveFrames . Num ( ) ) ;
2022-12-02 07:57:31 -05:00
2023-11-22 04:08:33 -05:00
for ( int32 FrameIndex = FrameStartIndex ; FrameIndex > = 0 ; FrameIndex - - )
2022-12-02 07:57:31 -05:00
{
2023-11-30 07:03:20 -05:00
FStateTreeExecutionFrame * CurrentParentFrame = FrameIndex > 0 ? & Exec . ActiveFrames [ FrameIndex - 1 ] : nullptr ;
2023-11-22 04:08:33 -05:00
FStateTreeExecutionFrame & CurrentFrame = Exec . ActiveFrames [ FrameIndex ] ;
const UStateTree * CurrentStateTree = CurrentFrame . StateTree ;
2023-01-23 12:48:04 -05:00
2023-11-30 07:03:20 -05:00
FCurrentlyProcessedFrameScope FrameScope ( * this , CurrentParentFrame , CurrentFrame ) ;
2023-08-04 14:55:23 -04:00
2023-11-22 04:08:33 -05:00
int32 StateStartIndex = CurrentFrame . ActiveStates . Num ( ) - 1 ; // This is ok, even if the ActiveStates is 0, -1 will skip the whole state loop below.
if ( FrameIndex = = FrameStartIndex
& & Exec . CompletedStateHandle . IsValid ( ) )
2022-12-02 07:57:31 -05:00
{
2023-11-22 04:08:33 -05:00
StateStartIndex = CurrentFrame . ActiveStates . IndexOfReverse ( Exec . CompletedStateHandle ) ;
// INDEX_NONE (-1) will skip the whole state loop below. We still want to warn.
ensureMsgf ( StateStartIndex ! = INDEX_NONE , TEXT ( " If CompletedFrameIndex and CompletedStateHandle are specified, we expect that the state is found " ) ) ;
}
const EStateTreeTransitionTrigger CompletionTrigger = Exec . LastTickStatus = = EStateTreeRunStatus : : Succeeded ? EStateTreeTransitionTrigger : : OnStateSucceeded : EStateTreeTransitionTrigger : : OnStateFailed ;
// Check completion transitions
for ( int32 StateIndex = StateStartIndex ; StateIndex > = 0 ; StateIndex - - )
{
const FStateTreeStateHandle StateHandle = CurrentFrame . ActiveStates [ StateIndex ] ;
const FCompactStateTreeState & State = CurrentStateTree - > States [ StateHandle . Index ] ;
2022-12-02 07:57:31 -05:00
2023-11-22 04:08:33 -05:00
FCurrentlyProcessedStateScope StateScope ( * this , StateHandle ) ;
STATETREE_TRACE_SCOPED_STATE_PHASE ( StateHandle , EStateTreeUpdatePhase : : TriggerTransitions ) ;
2023-11-17 07:41:08 -05:00
2023-11-22 04:08:33 -05:00
for ( uint8 i = 0 ; i < State . TransitionsNum ; i + + )
2023-11-17 11:42:17 -05:00
{
2023-11-22 04:08:33 -05:00
// All transition conditions must pass
const int16 TransitionIndex = State . TransitionsBegin + i ;
const FCompactStateTransition & Transition = CurrentStateTree - > Transitions [ TransitionIndex ] ;
// Skip disabled transitions
if ( Transition . bTransitionEnabled = = false )
2023-05-23 10:46:16 -04:00
{
2023-11-22 04:08:33 -05:00
continue ;
2023-05-23 10:46:16 -04:00
}
2023-11-22 04:08:33 -05:00
if ( EnumHasAnyFlags ( Transition . Trigger , CompletionTrigger ) )
2022-12-02 07:57:31 -05:00
{
2023-11-22 04:08:33 -05:00
bool bPassed = false ;
2022-12-02 07:57:31 -05:00
{
2023-11-22 04:08:33 -05:00
STATETREE_TRACE_TRANSITION_EVENT ( FStateTreeTransitionSource ( FStateTreeIndex16 ( TransitionIndex ) , Transition . State , Transition . Priority ) , EStateTreeTraceEventType : : OnEvaluating ) ;
STATETREE_TRACE_SCOPED_PHASE ( EStateTreeUpdatePhase : : TransitionConditions ) ;
2023-11-30 07:03:20 -05:00
bPassed = TestAllConditions ( CurrentParentFrame , CurrentFrame , Transition . ConditionsBegin , Transition . ConditionsNum ) ;
2023-11-22 04:08:33 -05:00
}
if ( bPassed )
{
// No delay allowed on completion conditions.
// No priority on completion transitions, use the priority to signal that state is selected.
2024-03-11 04:54:44 -04:00
if ( RequestTransition ( CurrentFrame , Transition . State , EStateTreeTransitionPriority : : Normal , /*TransitionEvent*/ nullptr , Transition . Fallback ) )
2023-11-22 04:08:33 -05:00
{
NextTransitionSource = FStateTreeTransitionSource ( FStateTreeIndex16 ( TransitionIndex ) , Transition . State , Transition . Priority ) ;
break ;
}
2022-12-02 07:57:31 -05:00
}
}
}
2023-11-22 04:08:33 -05:00
2024-04-05 02:53:00 -04:00
if ( NextTransition . Priority ! = EStateTreeTransitionPriority : : None ) //-V547
2023-11-22 04:08:33 -05:00
{
break ;
}
2022-12-02 07:57:31 -05:00
}
2023-11-30 07:03:20 -05:00
}
2023-01-23 12:48:04 -05:00
2023-11-30 07:03:20 -05:00
// Handle the case where no transition was found.
2024-04-05 02:53:00 -04:00
if ( NextTransition . Priority = = EStateTreeTransitionPriority : : None ) //-V547
2023-11-30 07:03:20 -05:00
{
2024-04-22 11:34:53 -04:00
STATETREE_LOG_AND_TRACE ( Verbose , Warning , TEXT ( " Could not trigger completion transition, jump back to root state. " ) ) ;
2023-11-30 07:03:20 -05:00
check ( ! Exec . ActiveFrames . IsEmpty ( ) ) ;
FStateTreeExecutionFrame & RootFrame = Exec . ActiveFrames [ 0 ] ;
FCurrentlyProcessedFrameScope RootFrameScope ( * this , nullptr , RootFrame ) ;
FCurrentlyProcessedStateScope RootStateScope ( * this , FStateTreeStateHandle : : Root ) ;
if ( RequestTransition ( RootFrame , FStateTreeStateHandle : : Root , EStateTreeTransitionPriority : : Normal ) )
2023-01-23 12:48:04 -05:00
{
2023-11-30 07:03:20 -05:00
NextTransitionSource = FStateTreeTransitionSource ( EStateTreeTransitionSourceType : : Internal , FStateTreeStateHandle : : Root , EStateTreeTransitionPriority : : Normal ) ;
}
else
{
2024-04-22 11:34:53 -04:00
STATETREE_LOG_AND_TRACE ( Warning , Error , TEXT ( " Failed to select root state. Stopping the tree with failure. " ) ) ;
2022-12-02 07:57:31 -05:00
2023-11-30 07:03:20 -05:00
SetupNextTransition ( RootFrame , FStateTreeStateHandle : : Failed , EStateTreeTransitionPriority : : Critical ) ;
2023-09-22 09:53:34 -04:00
2023-11-30 07:03:20 -05:00
// In this case we don't want to complete subtrees, we want to force the whole tree to stop.
bProcessSubTreeCompletion = false ;
2023-09-22 09:53:34 -04:00
}
2022-12-02 07:57:31 -05:00
}
}
2023-01-23 12:48:04 -05:00
// Check if the transition was succeed/failed, if we're on a sub-tree, complete the subtree instead of transition.
2023-09-22 09:53:34 -04:00
if ( NextTransition . TargetState . IsCompletionState ( ) & & bProcessSubTreeCompletion )
2023-01-23 12:48:04 -05:00
{
2023-11-22 04:08:33 -05:00
const int32 SourceFrameIndex = Exec . ActiveFrames . IndexOfByPredicate ( [ & NextTransition = NextTransition ] ( const FStateTreeExecutionFrame & Frame )
2023-01-23 12:48:04 -05:00
{
2023-11-22 04:08:33 -05:00
return Frame . StateTree = = NextTransition . SourceStateTree & & Frame . RootState = = NextTransition . SourceRootState ;
} ) ;
// Check that the transition source frame is a sub-tree, the first frame (0 index) is not a subtree.
if ( SourceFrameIndex > 0 )
{
const FStateTreeExecutionFrame & SourceFrame = Exec . ActiveFrames [ SourceFrameIndex ] ;
const int32 ParentFrameIndex = SourceFrameIndex - 1 ;
const FStateTreeExecutionFrame & ParentFrame = Exec . ActiveFrames [ ParentFrameIndex ] ;
const FStateTreeStateHandle ParentLinkedState = ParentFrame . ActiveStates . Last ( ) ;
2023-06-12 07:34:11 -04:00
2023-11-22 04:08:33 -05:00
if ( ParentLinkedState . IsValid ( ) )
{
const EStateTreeRunStatus RunStatus = NextTransition . TargetState . ToCompletionStatus ( ) ;
STATETREE_LOG ( Verbose , TEXT ( " Completed subtree '%s' from state '%s': %s " ) ,
* GetSafeStateName ( ParentFrame , ParentLinkedState ) , * GetSafeStateName ( SourceFrame , NextTransition . SourceState ) , * UEnum : : GetDisplayValueAsText ( RunStatus ) . ToString ( ) ) ;
2023-01-23 12:48:04 -05:00
2023-11-22 04:08:33 -05:00
// Set the parent linked state as last completed state, and update tick status to the status from the transition.
Exec . CompletedFrameIndex = FStateTreeIndex16 ( ParentFrameIndex ) ;
Exec . CompletedStateHandle = ParentLinkedState ;
Exec . LastTickStatus = RunStatus ;
// Clear the transition and return that no transition took place.
// Since the LastTickStatus != running, the transition loop will try another transition
// now starting from the linked parent state. If we run out of retires in the selection loop (e.g. very deep hierarchy)
// we will continue on next tick.
NextTransition . Reset ( ) ;
return false ;
}
2023-01-23 12:48:04 -05:00
}
}
return NextTransition . TargetState . IsValid ( ) ;
2022-05-31 04:51:18 -04:00
}
2023-11-22 04:08:33 -05:00
bool FStateTreeExecutionContext : : SelectState ( const FStateTreeExecutionFrame & CurrentFrame ,
const FStateTreeStateHandle NextState ,
2024-03-11 04:54:44 -04:00
FStateSelectionResult & OutSelectionResult ,
const FStateTreeSharedEvent * TransitionEvent ,
2023-11-22 04:08:33 -05:00
const EStateTreeSelectionFallback Fallback )
2022-05-31 04:51:18 -04:00
{
2022-09-23 20:02:42 -04:00
const FStateTreeExecutionState & Exec = GetExecState ( ) ;
2022-05-31 04:51:18 -04:00
2023-11-22 04:08:33 -05:00
if ( Exec . ActiveFrames . IsEmpty ( ) )
{
STATETREE_LOG ( Error , TEXT ( " %hs: SelectState can only be called on initialized tree. '%s' using StateTree '%s'. " ) ,
__FUNCTION__ , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & RootStateTree ) ) ;
return false ;
}
2022-05-31 04:51:18 -04:00
if ( ! NextState . IsValid ( ) )
{
return false ;
}
// Walk towards the root from current state.
2024-04-05 02:16:32 -04:00
TArray < FStateTreeStateHandle , TInlineAllocator < FStateTreeActiveStates : : MaxStates > > PathToNextState ;
2022-05-31 04:51:18 -04:00
FStateTreeStateHandle CurrState = NextState ;
while ( CurrState . IsValid ( ) )
{
2024-04-05 02:16:32 -04:00
if ( PathToNextState . Num ( ) = = FStateTreeActiveStates : : MaxStates )
2022-05-31 04:51:18 -04:00
{
2023-11-22 04:08:33 -05:00
STATETREE_LOG ( Error , TEXT ( " %hs: Reached max execution depth when trying to select state %s from '%s'. '%s' using StateTree '%s'. " ) ,
__FUNCTION__ , * GetSafeStateName ( CurrentFrame , NextState ) , * GetStateStatusString ( Exec ) , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & RootStateTree ) ) ;
2022-05-31 04:51:18 -04:00
return false ;
}
2023-11-22 04:08:33 -05:00
// Store the states that are in between the 'NextState' and common ancestor.
2024-04-05 02:16:32 -04:00
PathToNextState . Push ( CurrState ) ;
2023-11-22 04:08:33 -05:00
CurrState = CurrentFrame . StateTree - > States [ CurrState . Index ] . Parent ;
2022-05-31 04:51:18 -04:00
}
2024-04-05 02:16:32 -04:00
Algo : : Reverse ( PathToNextState ) ;
2023-11-22 04:08:33 -05:00
const UStateTree * NextStateTree = CurrentFrame . StateTree ;
2024-04-05 02:16:32 -04:00
const FStateTreeStateHandle NextRootState = PathToNextState [ 0 ] ;
2023-11-22 04:08:33 -05:00
// Find the frame that the next state belongs to.
int32 CurrentFrameIndex = INDEX_NONE ;
int32 CurrentStateTreeIndex = INDEX_NONE ;
for ( int32 FrameIndex = Exec . ActiveFrames . Num ( ) - 1 ; FrameIndex > = 0 ; FrameIndex - - )
2023-11-17 07:41:08 -05:00
{
2023-11-22 04:08:33 -05:00
const FStateTreeExecutionFrame & Frame = Exec . ActiveFrames [ FrameIndex ] ;
if ( Frame . StateTree = = NextStateTree )
{
CurrentStateTreeIndex = FrameIndex ;
if ( Frame . RootState = = NextRootState )
{
CurrentFrameIndex = FrameIndex ;
break ;
}
}
2023-11-17 07:41:08 -05:00
}
2023-11-22 04:08:33 -05:00
// Copy common frames over.
2023-12-01 06:43:27 -05:00
// ReferenceCurrentFrame is the original of the last copied frame. It will be used to keep track if we are following the current active frames and states.
const FStateTreeExecutionFrame * CurrentFrameInActiveFrames = nullptr ;
2023-11-22 04:08:33 -05:00
if ( CurrentFrameIndex ! = INDEX_NONE )
2023-11-17 07:41:08 -05:00
{
2023-11-22 04:08:33 -05:00
const int32 NumCommonFrames = CurrentFrameIndex + 1 ;
2024-03-11 04:54:44 -04:00
OutSelectionResult = FStateSelectionResult ( MakeArrayView ( Exec . ActiveFrames . GetData ( ) , NumCommonFrames ) ) ;
2023-12-01 06:43:27 -05:00
CurrentFrameInActiveFrames = & Exec . ActiveFrames [ CurrentFrameIndex ] ;
2023-11-22 04:08:33 -05:00
}
else if ( CurrentStateTreeIndex ! = INDEX_NONE )
{
// If we could not find a common frame, we assume that we jumped to different subtree in same asset.
const int32 NumCommonFrames = CurrentStateTreeIndex + 1 ;
2024-03-11 04:54:44 -04:00
OutSelectionResult = FStateSelectionResult ( MakeArrayView ( Exec . ActiveFrames . GetData ( ) , NumCommonFrames ) ) ;
2023-12-01 06:43:27 -05:00
CurrentFrameInActiveFrames = & Exec . ActiveFrames [ CurrentStateTreeIndex ] ;
2023-11-22 04:08:33 -05:00
}
else
{
STATETREE_LOG ( Error , TEXT ( " %hs: Encountered unrecognized state %s during state selection from '%s'. '%s' using StateTree '%s'. " ) ,
__FUNCTION__ , * GetNameSafe ( NextStateTree ) , * GetStateStatusString ( Exec ) , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & RootStateTree ) ) ;
2023-11-17 11:42:17 -05:00
return false ;
2023-11-17 07:41:08 -05:00
}
2023-11-22 04:08:33 -05:00
// Append in between state in reverse order, they were collected from leaf towards the root.
// Note: NextState will be added by SelectStateInternal() if conditions pass.
2024-03-11 04:54:44 -04:00
const int32 LastFrameIndex = OutSelectionResult . FramesNum ( ) - 1 ;
FStateTreeExecutionFrame & LastFrame = OutSelectionResult . GetSelectedFrames ( ) [ LastFrameIndex ] ;
2023-11-17 11:42:17 -05:00
2024-04-05 02:16:32 -04:00
// Find index of the first state to be evaluated.
int32 FirstNewStateIndex = 0 ;
if ( CurrentFrameIndex ! = INDEX_NONE )
2023-11-22 04:08:33 -05:00
{
2024-04-05 02:16:32 -04:00
// If LastFrame.ActiveStates is a subset of PathToNextState (e.g when someone use "TryEnter" selection behavior and then make a transition to it's child or if one is reentering the same state).
// In such case loop below won't break on anything and FirstNewStateIndex will be incorrectly 0, thus we initialize it to be right after the shorter range.
FirstNewStateIndex = FMath : : Max ( 0 , FMath : : Min ( PathToNextState . Num ( ) , LastFrame . ActiveStates . Num ( ) ) - 1 ) ;
for ( int32 Index = 0 ; Index < FMath : : Min ( PathToNextState . Num ( ) , LastFrame . ActiveStates . Num ( ) ) ; + + Index )
{
if ( LastFrame . ActiveStates [ Index ] ! = PathToNextState [ Index ] )
{
FirstNewStateIndex = Index ;
break ;
}
}
2023-11-22 04:08:33 -05:00
}
2024-04-05 02:16:32 -04:00
LastFrame . ActiveStates . SetNum ( FirstNewStateIndex ) ;
2024-09-10 10:26:02 -04:00
// Existing state's data is safe to access during select.
LastFrame . NumCurrentlyActiveStates = static_cast < uint8 > ( LastFrame . ActiveStates . Num ( ) ) ;
2024-03-11 04:54:44 -04:00
FStateSelectionResult InitialSelection ;
2023-11-22 04:08:33 -05:00
if ( Fallback = = EStateTreeSelectionFallback : : NextSelectableSibling )
{
2024-03-11 04:54:44 -04:00
InitialSelection = OutSelectionResult ;
2023-11-22 04:08:33 -05:00
}
// We take copy of the last frame and assign it later, as SelectStateInternal() might change the array and invalidate the pointer.
2024-04-05 02:16:32 -04:00
const FStateTreeExecutionFrame * CurrentParentFrame = LastFrameIndex > 0 ? & OutSelectionResult . GetSelectedFrames ( ) [ LastFrameIndex - 1 ] : nullptr ;
// Path from the first new state up to the NextState
TConstArrayView < FStateTreeStateHandle > NewStatesPathToNextState ( & PathToNextState [ FirstNewStateIndex ] , PathToNextState . Num ( ) - FirstNewStateIndex ) ;
if ( SelectStateInternal ( CurrentParentFrame , OutSelectionResult . GetSelectedFrames ( ) [ LastFrameIndex ] , CurrentFrameInActiveFrames , NewStatesPathToNextState , OutSelectionResult , TransitionEvent ) )
2023-10-17 16:15:53 -04:00
{
return true ;
}
// Failed to Select Next State, handle fallback here
// Return true on the first next sibling that gets selected successfully
2024-04-05 02:16:32 -04:00
if ( Fallback = = EStateTreeSelectionFallback : : NextSelectableSibling & & PathToNextState . Num ( ) > = 2 )
2023-10-17 16:15:53 -04:00
{
2024-04-05 02:16:32 -04:00
const FStateTreeStateHandle Parent = PathToNextState . Last ( 1 ) ;
2023-10-17 16:15:53 -04:00
if ( Parent . IsValid ( ) )
{
2023-11-22 04:08:33 -05:00
const FCompactStateTreeState & ParentState = CurrentFrame . StateTree - > States [ Parent . Index ] ;
2023-10-17 16:15:53 -04:00
2023-11-22 04:08:33 -05:00
uint16 ChildState = CurrentFrame . StateTree - > States [ NextState . Index ] . GetNextSibling ( ) ;
for ( ; ChildState < ParentState . ChildrenEnd ; ChildState = CurrentFrame . StateTree - > States [ ChildState ] . GetNextSibling ( ) )
2023-10-17 16:15:53 -04:00
{
FStateTreeStateHandle ChildStateHandle = FStateTreeStateHandle ( ChildState ) ;
2023-11-22 04:08:33 -05:00
// Start selection from blank slate.
2024-03-11 04:54:44 -04:00
OutSelectionResult = InitialSelection ;
2023-11-22 04:08:33 -05:00
// We take copy of the last frame and assign it later, as SelectStateInternal() might change the array and invalidate the pointer.
2024-03-11 04:54:44 -04:00
CurrentParentFrame = LastFrameIndex > 0 ? & OutSelectionResult . GetSelectedFrames ( ) [ LastFrameIndex - 1 ] : nullptr ;
2024-04-05 02:16:32 -04:00
if ( SelectStateInternal ( CurrentParentFrame , OutSelectionResult . GetSelectedFrames ( ) [ LastFrameIndex ] , CurrentFrameInActiveFrames , { ChildStateHandle } , OutSelectionResult ) )
2023-10-17 16:15:53 -04:00
{
return true ;
}
}
}
}
2023-11-22 04:08:33 -05:00
2023-10-17 16:15:53 -04:00
return false ;
2022-05-31 04:51:18 -04:00
}
2023-11-30 07:03:20 -05:00
bool FStateTreeExecutionContext : : SelectStateInternal (
const FStateTreeExecutionFrame * CurrentParentFrame ,
FStateTreeExecutionFrame & CurrentFrame ,
2023-12-01 06:43:27 -05:00
const FStateTreeExecutionFrame * CurrentFrameInActiveFrames ,
2024-04-05 02:16:32 -04:00
TConstArrayView < FStateTreeStateHandle > PathToNextState ,
2024-03-11 04:54:44 -04:00
FStateSelectionResult & OutSelectionResult ,
const FStateTreeSharedEvent * TransitionEvent )
2022-05-31 04:51:18 -04:00
{
CSV_SCOPED_TIMING_STAT_EXCLUSIVE ( StateTree_SelectState ) ;
2022-09-23 20:02:42 -04:00
const FStateTreeExecutionState & Exec = GetExecState ( ) ;
2022-05-31 04:51:18 -04:00
2024-04-05 02:16:32 -04:00
check ( ! PathToNextState . IsEmpty ( ) ) ;
const FStateTreeStateHandle NextStateHandle = PathToNextState [ 0 ] ;
2023-11-30 07:03:20 -05:00
if ( ! NextStateHandle . IsValid ( ) )
2022-05-31 04:51:18 -04:00
{
// Trying to select non-existing state.
2023-04-12 07:59:16 -04:00
STATETREE_LOG ( Error , TEXT ( " %hs: Trying to select invalid state from '%s'. '%s' using StateTree '%s'. " ) ,
2023-11-22 04:08:33 -05:00
__FUNCTION__ , * GetStateStatusString ( Exec ) , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( CurrentFrame . StateTree ) ) ;
2022-05-31 04:51:18 -04:00
return false ;
}
2023-11-30 07:03:20 -05:00
FCurrentlyProcessedFrameScope FrameScope ( * this , CurrentParentFrame , CurrentFrame ) ;
2024-07-31 06:05:07 -04:00
FCurrentlyProcessedStateScope NextStateScope ( * this , NextStateHandle ) ;
2024-03-11 04:54:44 -04:00
FCurrentFrameStateSelectionEventsScope CapturedEventsScope ( * this , OutSelectionResult . GetFramesStateSelectionEvents ( ) . Last ( ) ) ;
2022-05-31 04:51:18 -04:00
2023-11-22 04:08:33 -05:00
const UStateTree * CurrentStateTree = CurrentFrame . StateTree ;
2023-11-30 07:03:20 -05:00
const FCompactStateTreeState & NextState = CurrentStateTree - > States [ NextStateHandle . Index ] ;
2023-11-22 04:08:33 -05:00
2023-11-30 07:03:20 -05:00
if ( NextState . bEnabled = = false )
2023-06-05 13:12:19 -04:00
{
// Do not select disabled state
STATETREE_LOG ( VeryVerbose , TEXT ( " %hs: Ignoring disabled state '%s'. '%s' using StateTree '%s'. " ) ,
2023-11-30 07:03:20 -05:00
__FUNCTION__ , * GetSafeStateName ( CurrentFrame , NextStateHandle ) , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( CurrentFrame . StateTree ) ) ;
2023-06-05 13:12:19 -04:00
return false ;
}
2023-11-30 07:03:20 -05:00
STATETREE_TRACE_SCOPED_STATE_PHASE ( NextStateHandle , EStateTreeUpdatePhase : : StateSelection ) ;
2023-11-22 04:08:33 -05:00
// The state cannot be directly selected.
2023-11-30 07:03:20 -05:00
if ( NextState . SelectionBehavior = = EStateTreeStateSelectionBehavior : : None )
2023-11-17 11:42:17 -05:00
{
2023-11-22 04:08:33 -05:00
return false ;
2023-11-17 11:42:17 -05:00
}
2023-11-17 07:41:08 -05:00
2024-04-22 07:44:19 -04:00
const UStateTree * NextLinkedStateAsset = NextState . LinkedAsset ;
2024-03-28 05:18:19 -04:00
// Look up linked state overrides
const FInstancedPropertyBag * NextLinkedStateParameterOverride = nullptr ;
if ( NextState . Type = = EStateTreeStateType : : LinkedAsset )
{
if ( const FStateTreeReference * Override = GetLinkedStateTreeOverrideForTag ( NextState . Tag ) )
{
2024-04-22 07:44:19 -04:00
NextLinkedStateAsset = Override - > GetStateTree ( ) ;
2024-03-28 05:18:19 -04:00
NextLinkedStateParameterOverride = & Override - > GetParameters ( ) ;
2024-04-22 07:44:19 -04:00
STATETREE_LOG ( VeryVerbose , TEXT ( " %hs: In state '%s', overriding linked asset '%s' with '%s'. '%s' using StateTree '%s'. " ) ,
__FUNCTION__ , * GetSafeStateName ( CurrentFrame , NextStateHandle ) ,
* GetFullNameSafe ( NextState . LinkedAsset ) , * GetFullNameSafe ( NextLinkedStateAsset ) ,
* GetNameSafe ( & Owner ) , * GetFullNameSafe ( CurrentFrame . StateTree ) ) ;
2024-03-28 05:18:19 -04:00
}
}
2024-03-22 06:34:36 -04:00
if ( NextState . ParameterDataHandle . IsValid ( ) )
{
// Instantiate state parameters if not done yet.
FStateTreeDataView NextStateParametersView = GetDataViewOrTemporary ( CurrentParentFrame , CurrentFrame , NextState . ParameterDataHandle ) ;
if ( ! NextStateParametersView . IsValid ( ) )
{
// Allocate temporary instance for parameters if the state has params.
2024-05-27 15:15:55 -04:00
// The subtree state selection below assumes that this creates always a valid temporary, we'll create the temp data even if parameters are empty.
// @todo: Empty params is valid and common case, we should not require to create empty parameters data (this needs to be handle in compiler and UpdateInstanceData too).
2024-03-28 05:18:19 -04:00
if ( NextLinkedStateParameterOverride )
2024-03-22 06:34:36 -04:00
{
2024-03-28 05:18:19 -04:00
// Create from an override.
2024-05-27 15:15:55 -04:00
FStateTreeDataView TempStateParametersView = AddTemporaryInstance ( CurrentFrame , FStateTreeIndex16 : : Invalid , NextState . ParameterDataHandle , FConstStructView ( TBaseStructure < FCompactStateTreeParameters > : : Get ( ) ) ) ;
check ( TempStateParametersView . IsValid ( ) ) ;
FCompactStateTreeParameters & StateParams = TempStateParametersView . GetMutable < FCompactStateTreeParameters > ( ) ;
StateParams . Parameters = * NextLinkedStateParameterOverride ;
NextStateParametersView = FStateTreeDataView ( StateParams . Parameters . GetMutableValue ( ) ) ;
2024-03-28 05:18:19 -04:00
}
else
{
// Create from template in the asset.
const FConstStructView DefaultStateParamsInstanceData = CurrentFrame . StateTree - > DefaultInstanceData . GetStruct ( NextState . ParameterTemplateIndex . Get ( ) ) ;
2024-05-27 15:15:55 -04:00
FStateTreeDataView TempStateParametersView = AddTemporaryInstance ( CurrentFrame , FStateTreeIndex16 : : Invalid , NextState . ParameterDataHandle , DefaultStateParamsInstanceData ) ;
check ( TempStateParametersView . IsValid ( ) ) ;
FCompactStateTreeParameters & StateParams = TempStateParametersView . GetMutable < FCompactStateTreeParameters > ( ) ;
NextStateParametersView = FStateTreeDataView ( StateParams . Parameters . GetMutableValue ( ) ) ;
2024-03-22 06:34:36 -04:00
}
}
// Copy parameters if needed
if ( NextStateParametersView . IsValid ( )
& & NextState . ParameterDataHandle . IsValid ( )
& & NextState . ParameterBindingsBatch . IsValid ( ) )
{
// Note: the parameters are for the current (linked) state, stored in current frame.
2024-03-28 05:18:19 -04:00
// The copy can fail, if the overridden parameters do not match, this is by design.
2024-03-22 06:34:36 -04:00
CopyBatchWithValidation ( CurrentParentFrame , CurrentFrame , NextStateParametersView , NextState . ParameterBindingsBatch ) ;
}
}
2024-04-05 02:16:32 -04:00
const bool bIsDestinationState = PathToNextState . Num ( ) < 2 ;
const bool bShouldPrerequisitesBeChecked = bIsDestinationState | | NextState . bCheckPrerequisitesWhenActivatingChildDirectly ;
2024-03-11 04:54:44 -04:00
TArray < const FStateTreeSharedEvent * , TInlineAllocator < FStateTreeEventQueue : : MaxActiveEvents > > StateSelectionEvents ;
if ( NextState . EventDataIndex . IsValid ( ) )
2022-05-31 04:51:18 -04:00
{
2024-03-11 04:54:44 -04:00
check ( NextState . RequiredEventToEnter . IsValid ( ) ) ;
2024-04-05 02:16:32 -04:00
// Use the same event as performed transition unless it didn't lead to this state as only state selected by the transition should get it's event.
if ( TransitionEvent & & TransitionEvent - > IsValid ( ) & & bIsDestinationState )
2024-03-11 04:54:44 -04:00
{
if ( NextState . RequiredEventToEnter . DoesEventMatchDesc ( * TransitionEvent - > Get ( ) ) )
{
StateSelectionEvents . Emplace ( TransitionEvent ) ;
}
}
else
{
2024-04-05 02:16:32 -04:00
TArrayView < FStateTreeSharedEvent > EventsQueue = GetMutableEventsToProcessView ( ) ;
2024-03-11 04:54:44 -04:00
for ( FStateTreeSharedEvent & Event : EventsQueue )
{
check ( Event . IsValid ( ) ) ;
if ( NextState . RequiredEventToEnter . DoesEventMatchDesc ( * Event ) )
{
StateSelectionEvents . Emplace ( & Event ) ;
}
}
2024-04-05 02:16:32 -04:00
// Couldn't find matching state's event, but it's marked as not required. Adding an empty event which allows us to continue the state selection.
if ( ! bShouldPrerequisitesBeChecked & & StateSelectionEvents . IsEmpty ( ) )
{
StateSelectionEvents . Emplace ( ) ;
}
2024-03-11 04:54:44 -04:00
}
2024-06-25 04:30:22 -04:00
if ( StateSelectionEvents . IsEmpty ( ) )
{
return false ;
}
2024-03-11 04:54:44 -04:00
}
else
{
StateSelectionEvents . Emplace ( ) ;
}
2024-06-25 04:30:22 -04:00
if ( ! CurrentFrame . ActiveStates . Push ( NextStateHandle ) )
{
STATETREE_LOG ( Error , TEXT ( " %hs: Reached max execution depth when trying to select state %s from '%s'. '%s' using StateTree '%s'. " ) ,
__FUNCTION__ , * GetSafeStateName ( CurrentFrame , NextStateHandle ) , * GetStateStatusString ( Exec ) , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( CurrentFrame . StateTree ) ) ;
return false ;
}
// Check if we're still tracking on the current active frame and state.
// If we are, update the NumCurrentlyActiveStates to indicate that this state's instance data can be accessed.
const uint8 PrevNumCurrentlyActiveStates = CurrentFrame . NumCurrentlyActiveStates ;
if ( CurrentFrame . ActiveInstanceIndexBase . IsValid ( )
& & CurrentFrameInActiveFrames )
{
const int32 CurrentStateIndex = CurrentFrame . ActiveStates . Num ( ) - 1 ;
const FStateTreeStateHandle MatchingActiveHandle = CurrentFrameInActiveFrames - > ActiveStates . GetStateSafe ( CurrentStateIndex ) ;
if ( MatchingActiveHandle = = NextStateHandle )
{
CurrentFrame . NumCurrentlyActiveStates = static_cast < uint8 > ( CurrentFrame . ActiveStates . Num ( ) ) ;
}
}
bool bSucceededToSelectState = false ;
2024-03-11 04:54:44 -04:00
for ( const FStateTreeSharedEvent * StateSelectionEvent : StateSelectionEvents )
{
if ( StateSelectionEvent )
{
CurrentlyProcessedStateSelectionEvents - > Events [ NextState . Depth ] = * StateSelectionEvent ;
}
2024-04-05 02:16:32 -04:00
if ( bShouldPrerequisitesBeChecked )
2024-04-04 17:51:12 -04:00
{
2024-04-05 02:16:32 -04:00
// Check that the state can be entered
STATETREE_TRACE_PHASE_BEGIN ( EStateTreeUpdatePhase : : EnterConditions ) ;
const bool bEnterConditionsPassed = TestAllConditions ( CurrentParentFrame , CurrentFrame , NextState . EnterConditionsBegin , NextState . EnterConditionsNum ) ;
STATETREE_TRACE_PHASE_END ( EStateTreeUpdatePhase : : EnterConditions ) ;
if ( ! bEnterConditionsPassed )
{
continue ;
}
2024-03-11 04:54:44 -04:00
}
2023-12-01 06:43:27 -05:00
2024-04-05 02:16:32 -04:00
if ( ! bIsDestinationState )
{
// Next child state is already known. Passing TransitionEvent further so state selected directly by transition can use it.
if ( SelectStateInternal ( CurrentParentFrame , CurrentFrame , CurrentFrameInActiveFrames , PathToNextState . Mid ( 1 ) , OutSelectionResult , TransitionEvent ) )
{
2024-06-25 04:30:22 -04:00
bSucceededToSelectState = true ;
break ;
2024-04-05 02:16:32 -04:00
}
}
else if ( NextState . Type = = EStateTreeStateType : : Linked )
2023-11-30 07:03:20 -05:00
{
if ( NextState . LinkedState . IsValid ( ) )
{
2024-03-11 04:54:44 -04:00
if ( OutSelectionResult . IsFull ( ) )
2023-11-30 07:03:20 -05:00
{
STATETREE_LOG ( Error , TEXT ( " %hs: Reached max execution depth when trying to select state %s from '%s'. '%s' using StateTree '%s'. " ) ,
__FUNCTION__ , * GetSafeStateName ( CurrentFrame , NextStateHandle ) , * GetStateStatusString ( Exec ) , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( CurrentFrame . StateTree ) ) ;
2024-06-25 04:30:22 -04:00
break ;
2023-11-30 07:03:20 -05:00
}
FStateTreeExecutionFrame NewFrame ;
NewFrame . StateTree = CurrentFrame . StateTree ;
NewFrame . RootState = NextState . LinkedState ;
2023-12-13 06:34:27 -05:00
NewFrame . ExternalDataBaseIndex = CurrentFrame . ExternalDataBaseIndex ;
2023-12-13 10:21:13 -05:00
2023-11-30 07:03:20 -05:00
// Check and prevent recursion.
2024-03-11 04:54:44 -04:00
const bool bNewFrameAlreadySelected = OutSelectionResult . GetSelectedFrames ( ) . ContainsByPredicate ( [ & NewFrame ] ( const FStateTreeExecutionFrame & Frame ) {
2023-11-30 07:03:20 -05:00
return Frame . IsSameFrame ( NewFrame ) ;
} ) ;
if ( bNewFrameAlreadySelected )
{
STATETREE_LOG ( Error , TEXT ( " %hs: Trying to recursively enter subtree '%s' from '%s'. '%s' using StateTree '%s'. " ) ,
__FUNCTION__ , * GetSafeStateName ( NewFrame , NewFrame . RootState ) , * GetStateStatusString ( Exec ) , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( CurrentFrame . StateTree ) ) ;
2024-06-25 04:30:22 -04:00
break ;
2023-11-30 07:03:20 -05:00
}
// If the Frame already exists, copy instance indices so that conditions that rely on active states work correctly.
const FStateTreeExecutionFrame * ExistingFrame = Exec . ActiveFrames . FindByPredicate (
[ StateTree = NewFrame . StateTree , RootState = NewFrame . RootState ] ( const FStateTreeExecutionFrame & Frame )
{
return Frame . StateTree = = StateTree & & Frame . RootState = = RootState ;
} ) ;
if ( ExistingFrame )
{
NewFrame . ActiveInstanceIndexBase = ExistingFrame - > ActiveInstanceIndexBase ;
NewFrame . GlobalInstanceIndexBase = ExistingFrame - > GlobalInstanceIndexBase ;
NewFrame . StateParameterDataHandle = ExistingFrame - > StateParameterDataHandle ;
NewFrame . GlobalParameterDataHandle = ExistingFrame - > GlobalParameterDataHandle ;
}
else
{
// Since the StateTree is the same, we can access the global tasks of CurrentFrame, if they are initialized.
NewFrame . GlobalParameterDataHandle = CurrentFrame . GlobalParameterDataHandle ;
NewFrame . GlobalInstanceIndexBase = CurrentFrame . GlobalInstanceIndexBase ;
NewFrame . StateParameterDataHandle = NextState . ParameterDataHandle ; // Temporary allocated earlier if did not exists.
}
2024-03-11 04:54:44 -04:00
OutSelectionResult . PushFrame ( NewFrame ) ;
2023-11-30 07:03:20 -05:00
// If State is linked, proceed to the linked state.
2024-04-05 02:16:32 -04:00
if ( SelectStateInternal ( & CurrentFrame , OutSelectionResult . GetSelectedFrames ( ) . Last ( ) , ExistingFrame , { NewFrame . RootState } , OutSelectionResult ) )
2023-11-30 07:03:20 -05:00
{
2024-06-25 04:30:22 -04:00
bSucceededToSelectState = true ;
break ;
2023-11-30 07:03:20 -05:00
}
2024-03-11 04:54:44 -04:00
OutSelectionResult . PopFrame ( ) ;
2023-11-30 07:03:20 -05:00
}
else
{
STATETREE_LOG ( Warning , TEXT ( " %hs: Trying to enter invalid linked subtree from '%s'. '%s' using StateTree '%s'. " ) ,
__FUNCTION__ , * GetStateStatusString ( Exec ) , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( CurrentFrame . StateTree ) ) ;
}
}
else if ( NextState . Type = = EStateTreeStateType : : LinkedAsset )
{
2024-04-22 07:44:19 -04:00
if ( NextLinkedStateAsset = = nullptr )
2024-03-28 05:18:19 -04:00
{
2024-06-25 04:30:22 -04:00
break ;
2024-03-28 05:18:19 -04:00
}
2024-04-22 07:44:19 -04:00
if ( OutSelectionResult . IsFull ( ) )
2023-11-30 07:03:20 -05:00
{
2024-04-22 07:44:19 -04:00
STATETREE_LOG ( Error , TEXT ( " %hs: Reached max execution depth when trying to select state %s from '%s'. '%s' using StateTree '%s'. " ) ,
__FUNCTION__ , * GetSafeStateName ( CurrentFrame , NextStateHandle ) , * GetStateStatusString ( Exec ) , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( CurrentFrame . StateTree ) ) ;
2024-06-25 04:30:22 -04:00
break ;
2024-04-22 07:44:19 -04:00
}
2023-11-30 07:03:20 -05:00
2024-04-22 07:44:19 -04:00
// The linked state tree should have compatible context requirements.
if ( ! NextLinkedStateAsset - > HasCompatibleContextData ( RootStateTree ) )
{
STATETREE_LOG ( Error , TEXT ( " %hs: The linked State Tree '%s' does not have compatible schema, trying to select state %s from '%s'. '%s' using StateTree '%s'. " ) ,
__FUNCTION__ , * GetFullNameSafe ( NextLinkedStateAsset ) , * GetSafeStateName ( CurrentFrame , NextStateHandle ) , * GetStateStatusString ( Exec ) , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( CurrentFrame . StateTree ) ) ;
2024-06-25 04:30:22 -04:00
break ;
2024-04-22 07:44:19 -04:00
}
2023-12-13 06:34:27 -05:00
2024-04-22 07:44:19 -04:00
FStateTreeExecutionFrame NewFrame ;
NewFrame . StateTree = NextLinkedStateAsset ;
NewFrame . RootState = FStateTreeStateHandle : : Root ;
NewFrame . bIsGlobalFrame = true ;
2023-11-30 07:03:20 -05:00
2024-04-22 07:44:19 -04:00
// Check and prevent recursion.
const bool bNewFrameAlreadySelected = OutSelectionResult . GetSelectedFrames ( ) . ContainsByPredicate ( [ & NewFrame ] ( const FStateTreeExecutionFrame & Frame ) {
return Frame . IsSameFrame ( NewFrame ) ;
} ) ;
if ( bNewFrameAlreadySelected )
{
STATETREE_LOG ( Error , TEXT ( " %hs: Trying to recursively enter subtree '%s' from '%s'. '%s' using StateTree '%s'. " ) ,
__FUNCTION__ , * GetSafeStateName ( NewFrame , NewFrame . RootState ) , * GetStateStatusString ( Exec ) , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( CurrentFrame . StateTree ) ) ;
2024-06-25 04:30:22 -04:00
break ;
2024-04-22 07:44:19 -04:00
}
// If the Frame already exists, copy instance indices so that conditions that rely on active states work correctly.
const FStateTreeExecutionFrame * ExistingFrame = Exec . ActiveFrames . FindByPredicate (
[ StateTree = NewFrame . StateTree , RootState = NewFrame . RootState ] ( const FStateTreeExecutionFrame & Frame )
{
return Frame . StateTree = = StateTree & & Frame . RootState = = RootState ;
2023-11-30 07:03:20 -05:00
} ) ;
2024-06-25 04:09:43 -04:00
bool bStartedTemporaryEvaluatorsAndGlobalTasks = false ;
2024-04-22 07:44:19 -04:00
if ( ExistingFrame )
{
NewFrame . ActiveInstanceIndexBase = ExistingFrame - > ActiveInstanceIndexBase ;
NewFrame . GlobalInstanceIndexBase = ExistingFrame - > GlobalInstanceIndexBase ;
NewFrame . StateParameterDataHandle = ExistingFrame - > StateParameterDataHandle ;
NewFrame . GlobalParameterDataHandle = ExistingFrame - > GlobalParameterDataHandle ;
NewFrame . ExternalDataBaseIndex = ExistingFrame - > ExternalDataBaseIndex ;
2023-11-30 07:03:20 -05:00
}
else
{
2024-04-22 07:44:19 -04:00
// Pass the linked state's parameters as global parameters to the linked asset.
NewFrame . GlobalParameterDataHandle = NextState . ParameterDataHandle ;
// Collect external data if needed
NewFrame . ExternalDataBaseIndex = CollectExternalData ( NewFrame . StateTree ) ;
if ( ! NewFrame . ExternalDataBaseIndex . IsValid ( ) )
{
STATETREE_LOG ( VeryVerbose , TEXT ( " %hs: Cannot select state '%s' because failed to collect external data for nested tree '%s'. '%s' using StateTree '%s'. " ) ,
__FUNCTION__ , * GetSafeStateName ( CurrentFrame , NextStateHandle ) , * GetFullNameSafe ( NewFrame . StateTree ) , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( CurrentFrame . StateTree ) ) ;
2024-06-25 04:30:22 -04:00
break ;
2024-04-22 07:44:19 -04:00
}
// The state parameters will be from the root state.
const FCompactStateTreeState & RootState = NewFrame . StateTree - > States [ NewFrame . RootState . Index ] ;
NewFrame . StateParameterDataHandle = RootState . ParameterDataHandle ;
// Start global tasks and evaluators temporarily, so that their data is available already during select.
if ( StartTemporaryEvaluatorsAndGlobalTasks ( nullptr , NewFrame ) ! = EStateTreeRunStatus : : Running )
{
STATETREE_LOG ( VeryVerbose , TEXT ( " %hs: Cannot select state '%s' because cannot start nested tree's '%s' global tasks and evaluators. '%s' using StateTree '%s'. " ) ,
__FUNCTION__ , * GetSafeStateName ( CurrentFrame , NextStateHandle ) , * GetFullNameSafe ( NewFrame . StateTree ) , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( CurrentFrame . StateTree ) ) ;
2024-06-25 04:09:43 -04:00
StopTemporaryEvaluatorsAndGlobalTasks ( nullptr , NewFrame ) ;
2024-06-25 04:30:22 -04:00
break ;
2024-04-22 07:44:19 -04:00
}
2024-06-25 04:09:43 -04:00
bStartedTemporaryEvaluatorsAndGlobalTasks = true ;
2023-11-30 07:03:20 -05:00
}
2024-04-22 07:44:19 -04:00
OutSelectionResult . PushFrame ( NewFrame ) ;
// If State is linked, proceed to the linked state.
if ( SelectStateInternal ( & CurrentFrame , OutSelectionResult . GetSelectedFrames ( ) . Last ( ) , ExistingFrame , { NewFrame . RootState } , OutSelectionResult ) )
{
2024-06-25 04:30:22 -04:00
bSucceededToSelectState = true ;
break ;
2024-04-22 07:44:19 -04:00
}
2024-06-25 04:09:43 -04:00
if ( bStartedTemporaryEvaluatorsAndGlobalTasks )
{
StopTemporaryEvaluatorsAndGlobalTasks ( nullptr , NewFrame ) ;
}
2024-04-22 07:44:19 -04:00
OutSelectionResult . PopFrame ( ) ;
2023-11-30 07:03:20 -05:00
}
else if ( NextState . SelectionBehavior = = EStateTreeStateSelectionBehavior : : TryEnterState )
2022-05-31 04:51:18 -04:00
{
// Select this state.
2023-11-30 07:03:20 -05:00
STATETREE_TRACE_STATE_EVENT ( NextStateHandle , EStateTreeTraceEventType : : OnStateSelected ) ;
2024-06-25 04:30:22 -04:00
bSucceededToSelectState = true ;
break ;
2022-05-31 04:51:18 -04:00
}
2023-11-30 07:03:20 -05:00
else if ( NextState . SelectionBehavior = = EStateTreeStateSelectionBehavior : : TryFollowTransitions )
2023-04-12 07:59:16 -04:00
{
2023-12-13 10:21:13 -05:00
STATETREE_TRACE_SCOPED_STATE_PHASE ( NextStateHandle , EStateTreeUpdatePhase : : TrySelectBehavior ) ;
2023-04-12 07:59:16 -04:00
EStateTreeTransitionPriority CurrentPriority = EStateTreeTransitionPriority : : None ;
2023-11-22 04:08:33 -05:00
2023-11-30 07:03:20 -05:00
for ( uint8 i = 0 ; i < NextState . TransitionsNum ; i + + )
2023-04-12 07:59:16 -04:00
{
2023-11-30 07:03:20 -05:00
const int16 TransitionIndex = NextState . TransitionsBegin + i ;
2023-11-22 04:08:33 -05:00
const FCompactStateTransition & Transition = RootStateTree . Transitions [ TransitionIndex ] ;
2023-04-12 07:59:16 -04:00
2023-06-20 13:49:25 -04:00
// Skip disabled transitions
if ( Transition . bTransitionEnabled = = false )
{
continue ;
}
2023-04-12 07:59:16 -04:00
// No need to test the transition if same or higher priority transition has already been processed.
if ( Transition . Priority < = CurrentPriority )
{
continue ;
}
// Skip completion transitions
if ( EnumHasAnyFlags ( Transition . Trigger , EStateTreeTransitionTrigger : : OnStateCompleted ) )
{
continue ;
}
// Cannot follow transitions with delay.
if ( Transition . HasDelay ( ) )
{
continue ;
}
// Try to prevent (infinite) loops in the selection.
2023-11-22 04:08:33 -05:00
if ( CurrentFrame . ActiveStates . Contains ( Transition . State ) )
2023-04-12 07:59:16 -04:00
{
STATETREE_LOG ( Error , TEXT ( " %hs: Loop detected when trying to select state %s from '%s'. Prior states: %s. '%s' using StateTree '%s'. " ) ,
2024-03-11 04:54:44 -04:00
__FUNCTION__ , * GetSafeStateName ( CurrentFrame , NextStateHandle ) , * GetStateStatusString ( Exec ) , * DebugGetStatePath ( OutSelectionResult . GetSelectedFrames ( ) , & CurrentFrame ) , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( CurrentFrame . StateTree ) ) ;
2023-04-12 07:59:16 -04:00
continue ;
}
2024-03-11 04:54:44 -04:00
TArray < const FStateTreeSharedEvent * , TInlineAllocator < FStateTreeEventQueue : : MaxActiveEvents > > SelectedStateTransitionEvents ;
if ( Transition . Trigger = = EStateTreeTransitionTrigger : : OnEvent )
2024-03-07 06:53:02 -05:00
{
2024-03-11 04:54:44 -04:00
check ( Transition . RequiredEvent . IsValid ( ) ) ;
if ( StateSelectionEvent )
{
SelectedStateTransitionEvents . Emplace ( StateSelectionEvent ) ;
}
else
{
TArrayView < FStateTreeSharedEvent > EventsQueue = GetMutableEventsToProcessView ( ) ;
for ( FStateTreeSharedEvent & Event : EventsQueue )
{
check ( Event . IsValid ( ) ) ;
if ( Transition . RequiredEvent . DoesEventMatchDesc ( * Event ) )
{
SelectedStateTransitionEvents . Emplace ( & Event ) ;
}
}
}
}
else if ( Transition . Trigger = = EStateTreeTransitionTrigger : : OnTick )
{
SelectedStateTransitionEvents . Emplace ( ) ;
2023-05-23 10:46:16 -04:00
}
2024-03-11 04:54:44 -04:00
for ( const FStateTreeSharedEvent * SelectedStateTransitionEvent : SelectedStateTransitionEvents )
2023-04-12 07:59:16 -04:00
{
2024-03-11 04:54:44 -04:00
bool bTransitionConditionsPassed = false ;
2023-04-12 07:59:16 -04:00
{
2024-03-11 04:54:44 -04:00
FCurrentlyProcessedTransitionEventScope TransitionEventScope ( * this , SelectedStateTransitionEvent ? SelectedStateTransitionEvent - > Get ( ) : nullptr ) ;
STATETREE_TRACE_TRANSITION_EVENT ( FStateTreeTransitionSource ( FStateTreeIndex16 ( TransitionIndex ) , Transition . State , Transition . Priority ) , EStateTreeTraceEventType : : OnEvaluating ) ;
STATETREE_TRACE_SCOPED_PHASE ( EStateTreeUpdatePhase : : TransitionConditions ) ;
bTransitionConditionsPassed = TestAllConditions ( CurrentParentFrame , CurrentFrame , Transition . ConditionsBegin , Transition . ConditionsNum ) ;
}
if ( bTransitionConditionsPassed )
{
// Using SelectState() instead of SelectStateInternal to treat the transitions the same way as regular transitions,
// e.g. it may jump to a completely different branch.
FStateSelectionResult StateSelectionResult ;
if ( SelectState ( CurrentFrame , Transition . State , StateSelectionResult , SelectedStateTransitionEvent , Transition . Fallback ) )
{
// Selection succeeded.
// Cannot break yet because higher priority transitions may override the selection.
OutSelectionResult = StateSelectionResult ;
CurrentPriority = Transition . Priority ;
break ;
}
2023-04-12 07:59:16 -04:00
}
}
}
if ( CurrentPriority ! = EStateTreeTransitionPriority : : None )
{
2024-06-25 04:30:22 -04:00
bSucceededToSelectState = true ;
break ;
2023-04-12 07:59:16 -04:00
}
}
2023-11-30 07:03:20 -05:00
else if ( NextState . SelectionBehavior = = EStateTreeStateSelectionBehavior : : TrySelectChildrenInOrder )
2023-04-12 07:59:16 -04:00
{
2023-11-30 07:03:20 -05:00
if ( NextState . HasChildren ( ) )
2023-04-12 07:59:16 -04:00
{
2023-12-13 10:21:13 -05:00
STATETREE_TRACE_SCOPED_STATE_PHASE ( NextStateHandle , EStateTreeUpdatePhase : : TrySelectBehavior ) ;
2023-04-12 07:59:16 -04:00
// If the state has children, proceed to select children.
2023-11-30 07:03:20 -05:00
for ( uint16 ChildState = NextState . ChildrenBegin ; ChildState < NextState . ChildrenEnd ; ChildState = CurrentStateTree - > States [ ChildState ] . GetNextSibling ( ) )
2023-04-12 07:59:16 -04:00
{
2024-04-05 02:16:32 -04:00
if ( SelectStateInternal ( CurrentParentFrame , CurrentFrame , CurrentFrameInActiveFrames , { FStateTreeStateHandle ( ChildState ) } , OutSelectionResult ) )
2023-04-12 07:59:16 -04:00
{
// Selection succeeded
2024-06-25 04:30:22 -04:00
bSucceededToSelectState = true ;
break ;
2023-04-12 07:59:16 -04:00
}
}
2024-06-25 04:30:22 -04:00
if ( bSucceededToSelectState )
{
break ;
}
2023-04-12 07:59:16 -04:00
}
else
{
// Select this state (For backwards compatibility)
2023-11-30 07:03:20 -05:00
STATETREE_TRACE_STATE_EVENT ( NextStateHandle , EStateTreeTraceEventType : : OnStateSelected ) ;
2024-06-25 04:30:22 -04:00
bSucceededToSelectState = true ;
break ;
2023-04-12 07:59:16 -04:00
}
}
2024-09-03 14:56:39 -04:00
else if ( NextState . SelectionBehavior = = EStateTreeStateSelectionBehavior : : TrySelectChildrenAtRandom )
2024-04-16 13:30:24 -04:00
{
if ( NextState . HasChildren ( ) )
{
STATETREE_TRACE_SCOPED_STATE_PHASE ( NextStateHandle , EStateTreeUpdatePhase : : TrySelectBehavior ) ;
2024-06-13 22:57:49 -04:00
TArray < uint16 , TInlineAllocator < 8 > > NextLevelChildStates ;
2024-04-16 13:30:24 -04:00
for ( uint16 ChildState = NextState . ChildrenBegin ; ChildState < NextState . ChildrenEnd ; ChildState = CurrentStateTree - > States [ ChildState ] . GetNextSibling ( ) )
{
2024-06-13 22:57:49 -04:00
NextLevelChildStates . Push ( ChildState ) ;
2024-04-16 13:30:24 -04:00
}
2024-06-13 22:57:49 -04:00
while ( ! NextLevelChildStates . IsEmpty ( ) )
2024-04-16 13:30:24 -04:00
{
2024-06-13 22:57:49 -04:00
const int32 ChildStateIndex = Exec . RandomStream . RandRange ( 0 , NextLevelChildStates . Num ( ) - 1 ) ;
if ( SelectStateInternal ( CurrentParentFrame , CurrentFrame , CurrentFrameInActiveFrames , { FStateTreeStateHandle ( ChildStateIndex ) } , OutSelectionResult ) )
2024-04-16 13:30:24 -04:00
{
// Selection succeeded
2024-06-25 04:30:22 -04:00
bSucceededToSelectState = true ;
break ;
2024-04-16 13:30:24 -04:00
}
2024-06-13 22:57:49 -04:00
constexpr EAllowShrinking AllowShrinking = EAllowShrinking : : No ;
NextLevelChildStates . RemoveAtSwap ( ChildStateIndex , AllowShrinking ) ;
}
2024-06-25 04:30:22 -04:00
if ( bSucceededToSelectState )
{
break ;
}
2024-06-13 22:57:49 -04:00
}
else
{
// Select this state (For backwards compatibility)
STATETREE_TRACE_STATE_EVENT ( NextStateHandle , EStateTreeTraceEventType : : OnStateSelected ) ;
2024-06-25 04:30:22 -04:00
bSucceededToSelectState = true ;
break ;
2024-06-13 22:57:49 -04:00
}
}
else if ( NextState . SelectionBehavior = = EStateTreeStateSelectionBehavior : : TrySelectChildrenWithHighestUtility )
{
if ( NextState . HasChildren ( ) )
{
STATETREE_TRACE_SCOPED_STATE_PHASE ( NextStateHandle , EStateTreeUpdatePhase : : TrySelectBehavior ) ;
TArray < uint16 , TInlineAllocator < 8 > > NextLevelChildStates ;
for ( uint16 ChildState = NextState . ChildrenBegin ; ChildState < NextState . ChildrenEnd ; ChildState = CurrentStateTree - > States [ ChildState ] . GetNextSibling ( ) )
{
NextLevelChildStates . Push ( ChildState ) ;
}
while ( ! NextLevelChildStates . IsEmpty ( ) )
{
//Find one with highest score in the remaining candidates
2024-08-06 05:21:13 -04:00
float HighestScore = - std : : numeric_limits < float > : : infinity ( ) ; ;
2024-06-13 22:57:49 -04:00
uint16 StateIndexWithHighestScore = FStateTreeStateHandle : : InvalidIndex ;
2024-08-06 05:21:13 -04:00
int32 ArrayIndexWithHighestScore = INDEX_NONE ;
2024-06-13 22:57:49 -04:00
for ( int32 Index = 0 ; Index < NextLevelChildStates . Num ( ) ; + + Index )
{
const uint16 CurrentStateIndex = NextLevelChildStates [ Index ] ;
const FCompactStateTreeState & CurrentState = CurrentStateTree - > States [ CurrentStateIndex ] ;
2024-08-19 15:41:09 -04:00
const float Score = EvaluateUtility ( CurrentParentFrame , CurrentFrame , CurrentState . UtilityConsiderationsBegin , CurrentState . UtilityConsiderationsNum , CurrentState . Weight ) ;
2024-06-13 22:57:49 -04:00
if ( Score > HighestScore )
{
HighestScore = Score ;
StateIndexWithHighestScore = CurrentStateIndex ;
2024-08-06 05:21:13 -04:00
ArrayIndexWithHighestScore = Index ;
2024-06-13 22:57:49 -04:00
}
}
2024-08-06 05:21:13 -04:00
if ( FStateTreeStateHandle : : IsValidIndex ( StateIndexWithHighestScore ) )
2024-06-13 22:57:49 -04:00
{
2024-08-06 05:21:13 -04:00
if ( SelectStateInternal ( CurrentParentFrame , CurrentFrame , CurrentFrameInActiveFrames , { FStateTreeStateHandle ( StateIndexWithHighestScore ) } , OutSelectionResult ) )
{
// Selection succeeded
bSucceededToSelectState = true ;
break ;
}
// Disqualify the state we failed to enter
NextLevelChildStates . RemoveAtSwap ( ArrayIndexWithHighestScore , EAllowShrinking : : No ) ;
}
else
{
// No states in array were valid
2024-06-25 04:30:22 -04:00
break ;
2024-06-13 22:57:49 -04:00
}
}
2024-06-25 04:30:22 -04:00
if ( bSucceededToSelectState )
{
break ;
}
2024-06-13 22:57:49 -04:00
}
else
{
// Select this state (For backwards compatibility)
STATETREE_TRACE_STATE_EVENT ( NextStateHandle , EStateTreeTraceEventType : : OnStateSelected ) ;
2024-06-25 04:30:22 -04:00
bSucceededToSelectState = true ;
break ;
2024-06-13 22:57:49 -04:00
}
}
2024-09-03 14:56:39 -04:00
else if ( NextState . SelectionBehavior = = EStateTreeStateSelectionBehavior : : TrySelectChildrenAtRandomWeightedByUtility )
2024-06-13 22:57:49 -04:00
{
if ( NextState . HasChildren ( ) )
{
TArray < TTuple < uint16 , float > , TInlineAllocator < 8 > > NextLevelChildStates ;
float TotalScore = .0f ;
for ( uint16 CurrentStateIndex = NextState . ChildrenBegin ; CurrentStateIndex < NextState . ChildrenEnd ; CurrentStateIndex = CurrentStateTree - > States [ CurrentStateIndex ] . GetNextSibling ( ) )
{
const FCompactStateTreeState & CurrentState = CurrentStateTree - > States [ CurrentStateIndex ] ;
2024-08-19 15:41:09 -04:00
const float CurrentStateScore = EvaluateUtility ( CurrentParentFrame , CurrentFrame , CurrentState . UtilityConsiderationsBegin , CurrentState . UtilityConsiderationsNum , CurrentState . Weight ) ;
2024-06-13 22:57:49 -04:00
NextLevelChildStates . Emplace ( CurrentStateIndex , CurrentStateScore ) ;
TotalScore + = CurrentStateScore ;
}
while ( ! NextLevelChildStates . IsEmpty ( ) )
{
const float RandomScore = Exec . RandomStream . FRand ( ) * TotalScore ;
float AccumulatedScore = .0f ;
for ( int32 Index = 0 ; Index < NextLevelChildStates . Num ( ) ; + + Index )
{
const TTuple < uint16 , float > & StateScorePair = NextLevelChildStates [ Index ] ;
const uint16 StateIndex = StateScorePair . Key ;
const float StateScore = StateScorePair . Value ;
AccumulatedScore + = StateScore ;
if ( RandomScore < AccumulatedScore | | ( Index = = ( NextLevelChildStates . Num ( ) - 1 ) ) )
{
2024-08-12 15:21:12 -04:00
// States with zero possibility won't be selected
if ( StateScore ! = 0.f & & SelectStateInternal ( CurrentParentFrame , CurrentFrame , CurrentFrameInActiveFrames , { FStateTreeStateHandle ( StateIndex ) } , OutSelectionResult ) )
2024-06-13 22:57:49 -04:00
{
// Selection succeeded
2024-06-25 04:30:22 -04:00
bSucceededToSelectState = true ;
break ;
2024-06-13 22:57:49 -04:00
}
//Disqualify the state we failed to enter, and restart the loop
TotalScore - = StateScore ;
constexpr EAllowShrinking AllowShrinking = EAllowShrinking : : No ;
NextLevelChildStates . RemoveAtSwap ( Index , AllowShrinking ) ;
break ;
}
}
2024-06-25 04:30:22 -04:00
if ( bSucceededToSelectState )
{
break ;
}
}
if ( bSucceededToSelectState )
{
break ;
2024-04-16 13:30:24 -04:00
}
}
else
{
// Select this state (For backwards compatibility)
STATETREE_TRACE_STATE_EVENT ( NextStateHandle , EStateTreeTraceEventType : : OnStateSelected ) ;
2024-06-25 04:30:22 -04:00
bSucceededToSelectState = true ;
break ;
2024-04-16 13:30:24 -04:00
}
}
2024-06-25 04:30:22 -04:00
}
if ( ! bSucceededToSelectState )
{
2023-12-01 06:43:27 -05:00
// State could not be selected, restore.
2024-03-27 11:51:13 -04:00
CurrentFrame . NumCurrentlyActiveStates = PrevNumCurrentlyActiveStates ;
2023-11-22 04:08:33 -05:00
CurrentFrame . ActiveStates . Pop ( ) ;
2022-05-31 04:51:18 -04:00
}
2024-06-25 04:30:22 -04:00
return bSucceededToSelectState ;
2022-05-31 04:51:18 -04:00
}
2023-11-22 04:08:33 -05:00
FString FStateTreeExecutionContext : : GetSafeStateName ( const FStateTreeExecutionFrame & CurrentFrame , const FStateTreeStateHandle State ) const
2022-05-31 04:51:18 -04:00
{
if ( State = = FStateTreeStateHandle : : Invalid )
{
return TEXT ( " (State Invalid) " ) ;
}
else if ( State = = FStateTreeStateHandle : : Succeeded )
{
return TEXT ( " (State Succeeded) " ) ;
}
else if ( State = = FStateTreeStateHandle : : Failed )
{
return TEXT ( " (State Failed) " ) ;
}
2023-11-22 04:08:33 -05:00
else if ( CurrentFrame . StateTree & & CurrentFrame . StateTree - > States . IsValidIndex ( State . Index ) )
2022-05-31 04:51:18 -04:00
{
2023-11-22 04:08:33 -05:00
return * CurrentFrame . StateTree - > States [ State . Index ] . Name . ToString ( ) ;
2022-05-31 04:51:18 -04:00
}
return TEXT ( " (Unknown) " ) ;
}
2023-11-22 04:08:33 -05:00
FString FStateTreeExecutionContext : : DebugGetStatePath ( TConstArrayView < FStateTreeExecutionFrame > ActiveFrames , const FStateTreeExecutionFrame * CurrentFrame , const int32 ActiveStateIndex ) const
2022-05-31 04:51:18 -04:00
{
FString StatePath ;
2023-11-22 04:08:33 -05:00
const UStateTree * LastStateTree = & RootStateTree ;
for ( const FStateTreeExecutionFrame & Frame : ActiveFrames )
2022-05-31 04:51:18 -04:00
{
2023-11-22 04:08:33 -05:00
if ( ! ensure ( Frame . StateTree ) )
{
return StatePath ;
}
2023-11-17 11:42:17 -05:00
2023-11-22 04:08:33 -05:00
// If requested up the active state, clamp count.
int32 Num = Frame . ActiveStates . Num ( ) ;
if ( CurrentFrame = = & Frame & & Frame . ActiveStates . IsValidIndex ( ActiveStateIndex ) )
{
Num = ActiveStateIndex + 1 ;
}
if ( Frame . StateTree ! = LastStateTree )
{
StatePath . Appendf ( TEXT ( " [%s] " ) , * GetNameSafe ( Frame . StateTree ) ) ;
LastStateTree = Frame . StateTree ;
}
for ( int32 i = 0 ; i < Num ; i + + )
{
const FCompactStateTreeState & State = Frame . StateTree - > States [ Frame . ActiveStates [ i ] . Index ] ;
StatePath . Appendf ( TEXT ( " %s%s " ) , i = = 0 ? TEXT ( " " ) : TEXT ( " . " ) , * State . Name . ToString ( ) ) ;
}
2022-05-31 04:51:18 -04:00
}
2023-11-22 04:08:33 -05:00
2022-05-31 04:51:18 -04:00
return StatePath ;
}
FString FStateTreeExecutionContext : : GetStateStatusString ( const FStateTreeExecutionState & ExecState ) const
{
2023-11-22 04:08:33 -05:00
if ( ExecState . TreeRunStatus ! = EStateTreeRunStatus : : Running )
{
return TEXT ( " --: " ) + UEnum : : GetDisplayValueAsText ( ExecState . LastTickStatus ) . ToString ( ) ;
}
return GetSafeStateName ( ExecState . ActiveFrames . Last ( ) , ExecState . ActiveFrames . Last ( ) . ActiveStates . Last ( ) ) + TEXT ( " : " ) + UEnum : : GetDisplayValueAsText ( ExecState . LastTickStatus ) . ToString ( ) ;
2022-05-31 04:51:18 -04:00
}
2022-09-23 20:02:42 -04:00
EStateTreeRunStatus FStateTreeExecutionContext : : GetLastTickStatus ( ) const
2022-05-31 04:51:18 -04:00
{
2024-08-09 09:48:26 -04:00
if ( ! IsValid ( ) )
{
STATETREE_LOG ( Warning , TEXT ( " %hs: StateTree context is not initialized properly ('%s' using StateTree '%s') " ) ,
__FUNCTION__ , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & RootStateTree ) ) ;
return EStateTreeRunStatus : : Failed ;
}
2022-09-23 20:02:42 -04:00
const FStateTreeExecutionState & Exec = GetExecState ( ) ;
2022-05-31 04:51:18 -04:00
return Exec . LastTickStatus ;
}
2024-01-22 05:38:03 -05:00
void FStateTreeExecutionContext : : SetDefaultParameters ( )
{
SetGlobalParameters ( RootStateTree . GetDefaultParameters ( ) ) ;
}
void FStateTreeExecutionContext : : SetParameters ( const FInstancedPropertyBag & Parameters )
{
SetGlobalParameters ( Parameters ) ;
}
2022-05-31 04:51:18 -04:00
FString FStateTreeExecutionContext : : GetInstanceDescription ( ) const
{
2023-06-06 11:25:12 -04:00
return FString : : Printf ( TEXT ( " %s " ) , * GetNameSafe ( & Owner ) ) ;
2022-05-31 04:51:18 -04:00
}
2023-11-22 04:08:33 -05:00
TConstArrayView < FStateTreeExecutionFrame > FStateTreeExecutionContext : : GetActiveFrames ( ) const
2022-05-31 04:51:18 -04:00
{
2024-08-09 09:48:26 -04:00
if ( ! IsValid ( ) )
{
STATETREE_LOG ( Warning , TEXT ( " %hs: StateTree context is not initialized properly ('%s' using StateTree '%s') " ) ,
__FUNCTION__ , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & RootStateTree ) ) ;
return TConstArrayView < FStateTreeExecutionFrame > ( ) ;
}
2022-09-23 20:02:42 -04:00
const FStateTreeExecutionState & Exec = GetExecState ( ) ;
2023-11-22 04:08:33 -05:00
return Exec . ActiveFrames ;
2022-05-31 04:51:18 -04:00
}
# if WITH_GAMEPLAY_DEBUGGER
2022-09-23 20:02:42 -04:00
FString FStateTreeExecutionContext : : GetDebugInfoString ( ) const
2022-05-31 04:51:18 -04:00
{
2024-08-16 09:07:06 -04:00
TStringBuilder < 2048 > DebugString ;
DebugString < < TEXT ( " StateTree (asset: ' " ) ;
RootStateTree . GetFullName ( DebugString ) ;
DebugString < < TEXT ( " ') " ) ;
2022-05-31 04:51:18 -04:00
2024-08-09 09:48:26 -04:00
if ( IsValid ( ) )
2022-05-31 04:51:18 -04:00
{
2024-08-09 09:48:26 -04:00
const FStateTreeExecutionState & Exec = GetExecState ( ) ;
2022-05-31 04:51:18 -04:00
2024-08-16 09:07:06 -04:00
DebugString < < TEXT ( " Status: " ) ;
DebugString < < UEnum : : GetDisplayValueAsText ( Exec . TreeRunStatus ) . ToString ( ) ;
DebugString < < TEXT ( " \n " ) ;
2024-08-09 09:48:26 -04:00
// Active States
2024-08-16 09:07:06 -04:00
DebugString < < TEXT ( " Current State: \n " ) ;
2024-08-09 09:48:26 -04:00
for ( const FStateTreeExecutionFrame & CurrentFrame : Exec . ActiveFrames )
{
const UStateTree * CurrentStateTree = CurrentFrame . StateTree ;
if ( CurrentFrame . bIsGlobalFrame )
2023-11-17 11:42:17 -05:00
{
2024-08-16 09:07:06 -04:00
DebugString . Appendf ( TEXT ( " \n Evaluators \n [ %-30s | %8s | %15s ] \n " ) ,
2024-08-09 09:48:26 -04:00
TEXT ( " Name " ) , TEXT ( " Bindings " ) , TEXT ( " Data Handle " ) ) ;
for ( int32 EvalIndex = CurrentStateTree - > EvaluatorsBegin ; EvalIndex < ( CurrentStateTree - > EvaluatorsBegin + CurrentStateTree - > EvaluatorsNum ) ; EvalIndex + + )
2023-11-17 07:41:08 -05:00
{
2024-08-09 09:48:26 -04:00
const FStateTreeEvaluatorBase & Eval = CurrentStateTree - > Nodes [ EvalIndex ] . Get < const FStateTreeEvaluatorBase > ( ) ;
2024-08-16 09:07:06 -04:00
DebugString . Appendf ( TEXT ( " | %-30s | %8d | %15s | \n " ) ,
2024-08-09 09:48:26 -04:00
* Eval . Name . ToString ( ) , Eval . BindingsBatch . Get ( ) , * Eval . InstanceDataHandle . Describe ( ) ) ;
}
2024-08-16 09:07:06 -04:00
DebugString < < TEXT ( " \n Global Tasks \n " ) ;
2024-08-09 09:48:26 -04:00
for ( int32 TaskIndex = CurrentStateTree - > GlobalTasksBegin ; TaskIndex < ( CurrentStateTree - > GlobalTasksBegin + CurrentStateTree - > GlobalTasksNum ) ; TaskIndex + + )
{
const FStateTreeTaskBase & Task = CurrentStateTree - > Nodes [ TaskIndex ] . Get < const FStateTreeTaskBase > ( ) ;
if ( Task . bTaskEnabled )
{
2024-08-16 09:07:06 -04:00
FString TempString ;
Task . AppendDebugInfoString ( TempString , * this ) ;
DebugString < < TempString ;
2024-08-09 09:48:26 -04:00
}
2023-11-22 04:08:33 -05:00
}
}
2024-08-09 09:48:26 -04:00
for ( int32 Index = 0 ; Index < CurrentFrame . ActiveStates . Num ( ) ; Index + + )
{
FStateTreeStateHandle Handle = CurrentFrame . ActiveStates [ Index ] ;
if ( Handle . IsValid ( ) )
2023-11-22 04:08:33 -05:00
{
2024-08-09 09:48:26 -04:00
const FCompactStateTreeState & State = RootStateTree . States [ Handle . Index ] ;
2024-08-16 09:07:06 -04:00
DebugString < < TEXT ( ' [ ' ) ;
DebugString < < State . Name ;
DebugString < < TEXT ( " ] \n " ) ;
2024-08-09 09:48:26 -04:00
if ( State . TasksNum > 0 )
2023-06-05 13:12:19 -04:00
{
2024-08-09 09:48:26 -04:00
DebugString + = TEXT ( " \n Tasks: \n " ) ;
for ( int32 TaskIndex = State . TasksBegin ; TaskIndex < ( State . TasksBegin + State . TasksNum ) ; TaskIndex + + )
2023-11-22 04:08:33 -05:00
{
2024-08-09 09:48:26 -04:00
const FStateTreeTaskBase & Task = RootStateTree . Nodes [ TaskIndex ] . Get < const FStateTreeTaskBase > ( ) ;
if ( Task . bTaskEnabled )
{
2024-08-16 09:07:06 -04:00
FString TempString ;
Task . AppendDebugInfoString ( TempString , * this ) ;
DebugString < < TempString ;
2024-08-09 09:48:26 -04:00
}
2023-11-22 04:08:33 -05:00
}
2023-06-05 13:12:19 -04:00
}
2022-05-31 04:51:18 -04:00
}
}
}
}
2024-08-09 09:48:26 -04:00
else
{
2024-08-16 09:07:06 -04:00
DebugString < < TEXT ( " StateTree context is not initialized properly. " ) ;
2024-08-09 09:48:26 -04:00
}
2022-05-31 04:51:18 -04:00
2024-08-16 09:07:06 -04:00
return DebugString . ToString ( ) ;
2022-05-31 04:51:18 -04:00
}
# endif // WITH_GAMEPLAY_DEBUGGER
# if WITH_STATETREE_DEBUG
2022-09-23 20:02:42 -04:00
void FStateTreeExecutionContext : : DebugPrintInternalLayout ( )
2022-05-31 04:51:18 -04:00
{
LOG_SCOPE_VERBOSITY_OVERRIDE ( LogStateTree , ELogVerbosity : : Log ) ;
2024-08-16 09:07:06 -04:00
UE_LOG ( LogStateTree , Log , TEXT ( " %s " ) , * RootStateTree . DebugInternalLayoutAsString ( ) ) ;
2022-05-31 04:51:18 -04:00
}
2022-09-23 20:02:42 -04:00
int32 FStateTreeExecutionContext : : GetStateChangeCount ( ) const
2022-05-31 04:51:18 -04:00
{
2024-08-09 09:48:26 -04:00
if ( ! IsValid ( ) )
{
STATETREE_LOG ( Warning , TEXT ( " %hs: StateTree context is not initialized properly ('%s' using StateTree '%s') " ) ,
__FUNCTION__ , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & RootStateTree ) ) ;
return 0 ;
}
2022-09-23 20:02:42 -04:00
const FStateTreeExecutionState & Exec = GetExecState ( ) ;
2022-05-31 04:51:18 -04:00
return Exec . StateChangeCount ;
}
# endif // WITH_STATETREE_DEBUG
2022-09-23 20:02:42 -04:00
FString FStateTreeExecutionContext : : GetActiveStateName ( ) const
2024-08-09 09:48:26 -04:00
{
if ( ! IsValid ( ) )
{
STATETREE_LOG ( Warning , TEXT ( " %hs: StateTree context is not initialized properly ('%s' using StateTree '%s') " ) ,
__FUNCTION__ , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & RootStateTree ) ) ;
return FString ( ) ;
}
2022-09-23 20:02:42 -04:00
const FStateTreeExecutionState & Exec = GetExecState ( ) ;
2022-05-31 04:51:18 -04:00
2024-08-16 09:07:06 -04:00
TStringBuilder < 1024 > FullStateName ;
2022-05-31 04:51:18 -04:00
2023-11-22 04:08:33 -05:00
const UStateTree * LastStateTree = & RootStateTree ;
int32 Indent = 0 ;
for ( int32 FrameIndex = 0 ; FrameIndex < Exec . ActiveFrames . Num ( ) ; FrameIndex + + )
2022-05-31 04:51:18 -04:00
{
2023-11-22 04:08:33 -05:00
const FStateTreeExecutionFrame & CurrentFrame = Exec . ActiveFrames [ FrameIndex ] ;
const UStateTree * CurrentStateTree = CurrentFrame . StateTree ;
// Append linked state marker at the end of the previous line.
if ( Indent > 0 )
2022-05-31 04:51:18 -04:00
{
2024-08-16 09:07:06 -04:00
FullStateName < < TEXT ( " > " ) ;
2023-11-22 04:08:33 -05:00
}
// If tree has changed, append that too.
if ( CurrentFrame . StateTree ! = LastStateTree )
{
2024-08-16 09:07:06 -04:00
FullStateName < < TEXT ( " [ " ) ;
FullStateName < < CurrentFrame . StateTree . GetFName ( ) ;
FullStateName < < TEXT ( ' ] ' ) ;
2023-11-22 04:08:33 -05:00
LastStateTree = CurrentFrame . StateTree ;
}
for ( int32 Index = 0 ; Index < CurrentFrame . ActiveStates . Num ( ) ; Index + + )
{
const FStateTreeStateHandle Handle = CurrentFrame . ActiveStates [ Index ] ;
if ( Handle . IsValid ( ) )
2022-05-31 04:51:18 -04:00
{
2023-11-22 04:08:33 -05:00
const FCompactStateTreeState & State = CurrentStateTree - > States [ Handle . Index ] ;
if ( Indent > 0 )
{
FullStateName + = TEXT ( " \n " ) ;
}
2024-08-16 09:07:06 -04:00
FullStateName . Appendf ( TEXT ( " %*s- " ) , Indent * 3 , TEXT ( " " ) ) ; // Indent
FullStateName < < State . Name ;
2023-11-22 04:08:33 -05:00
Indent + + ;
2022-05-31 04:51:18 -04:00
}
}
}
switch ( Exec . TreeRunStatus )
{
case EStateTreeRunStatus : : Failed :
2024-08-16 09:07:06 -04:00
FullStateName < < TEXT ( " FAILED \n " ) ;
2022-05-31 04:51:18 -04:00
break ;
case EStateTreeRunStatus : : Succeeded :
2024-08-16 09:07:06 -04:00
FullStateName < < TEXT ( " SUCCEEDED \n " ) ;
2022-05-31 04:51:18 -04:00
break ;
case EStateTreeRunStatus : : Running :
// Empty
break ;
default :
2024-08-16 09:07:06 -04:00
FullStateName < < TEXT ( " -- \n " ) ;
2022-05-31 04:51:18 -04:00
}
2024-08-16 09:07:06 -04:00
return FullStateName . ToString ( ) ;
2022-05-31 04:51:18 -04:00
}
2022-09-23 20:02:42 -04:00
TArray < FName > FStateTreeExecutionContext : : GetActiveStateNames ( ) const
2022-05-31 04:51:18 -04:00
{
2024-08-09 09:48:26 -04:00
if ( ! IsValid ( ) )
{
STATETREE_LOG ( Warning , TEXT ( " %hs: StateTree context is not initialized properly ('%s' using StateTree '%s') " ) ,
__FUNCTION__ , * GetNameSafe ( & Owner ) , * GetFullNameSafe ( & RootStateTree ) ) ;
return TArray < FName > ( ) ;
}
TArray < FName > Result ;
2022-09-23 20:02:42 -04:00
const FStateTreeExecutionState & Exec = GetExecState ( ) ;
2022-05-31 04:51:18 -04:00
// Active States
2023-11-22 04:08:33 -05:00
for ( const FStateTreeExecutionFrame & CurrentFrame : Exec . ActiveFrames )
2022-05-31 04:51:18 -04:00
{
2023-11-22 04:08:33 -05:00
const UStateTree * CurrentStateTree = CurrentFrame . StateTree ;
for ( int32 Index = 0 ; Index < CurrentFrame . ActiveStates . Num ( ) ; Index + + )
2022-05-31 04:51:18 -04:00
{
2023-11-22 04:08:33 -05:00
const FStateTreeStateHandle Handle = CurrentFrame . ActiveStates [ Index ] ;
if ( Handle . IsValid ( ) )
{
const FCompactStateTreeState & State = CurrentStateTree - > States [ Handle . Index ] ;
Result . Add ( State . Name ) ;
}
2022-05-31 04:51:18 -04:00
}
}
return Result ;
}
# undef STATETREE_LOG
# undef STATETREE_CLOG