2021-09-28 13:33:17 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# pragma once
# include "StateTree.h"
# include "InstancedStruct.h"
# include "Containers/StaticArray.h"
# include "StateTreePropertyBindings.h"
2022-02-24 08:19:23 -05:00
# include "StateTreeInstanceData.h"
2021-09-28 13:33:17 -04:00
# include "StateTreeExecutionContext.generated.h"
2021-11-12 05:48:11 -05:00
struct FStateTreeEvaluatorBase ;
struct FStateTreeTaskBase ;
struct FStateTreeConditionBase ;
2021-09-28 13:33:17 -04:00
USTRUCT ( )
struct STATETREEMODULE_API FStateTreeExecutionState
{
GENERATED_BODY ( )
/** Currently active state */
FStateTreeHandle CurrentState = FStateTreeHandle : : Invalid ;
2021-12-09 05:26:00 -05:00
/** The index of the task that failed during enter state. Exit state uses it to call ExitState() symmetrically. */
uint16 EnterStateFailedTaskIndex = INDEX_NONE ;
2021-09-28 13:33:17 -04:00
/** Result of last tick */
EStateTreeRunStatus LastTickStatus = EStateTreeRunStatus : : Failed ;
/** Running status of the instance */
EStateTreeRunStatus TreeRunStatus = EStateTreeRunStatus : : Unset ;
/** Delayed transition handle, if exists */
int16 GatedTransitionIndex = INDEX_NONE ;
/** Running time of the delayed transition */
float GatedTransitionTime = 0.0f ;
2021-11-24 04:26:12 -05:00
/** Object instances, ref counting handled manually. */
TArray < UObject * > InstanceObjects ;
2021-09-28 13:33:17 -04:00
} ;
UENUM ( )
enum class EStateTreeStorage : uint8
{
2021-11-24 04:26:12 -05:00
/** Execution context has internal storage */
2021-09-28 13:33:17 -04:00
Internal ,
/** Execution context assumes external storage */
External ,
} ;
/**
* Runs StateTrees defined in UStateTree asset .
* Uses constant data from StateTree , keeps local storage of variables , and creates instanced Evaluators and Tasks .
*/
USTRUCT ( )
struct STATETREEMODULE_API FStateTreeExecutionContext
{
GENERATED_BODY ( )
public :
FStateTreeExecutionContext ( ) ;
virtual ~ FStateTreeExecutionContext ( ) ;
/** Initializes the StateTree instance to be used with specific owner and StateTree asset. */
bool Init ( UObject & InOwner , const UStateTree & InStateTree , const EStateTreeStorage InStorageType ) ;
2022-02-24 08:19:23 -05:00
/** Resets the instance to initial empty state. Note: Does not call ExitState(). */
void Reset ( ) ;
/** Initializes external instance data (no need to call, if using internal storage). */
static bool InitInstanceData ( UObject & InOwner , const UStateTree & InStateTree , FStateTreeInstanceData & OutInstanceData ) ;
2021-11-24 04:26:12 -05:00
2021-09-28 13:33:17 -04:00
/** Returns the StateTree asset in use. */
const UStateTree * GetStateTree ( ) const { return StateTree ; }
2021-11-24 04:26:12 -05:00
/** @return The owner of the context */
2021-09-28 13:33:17 -04:00
UObject * GetOwner ( ) const { return Owner ; }
2021-11-24 04:26:12 -05:00
/** @return The world of the owner or nullptr if the owner is not set. */
UWorld * GetWorld ( ) const { return Owner ? Owner - > GetWorld ( ) : nullptr ; } ;
2021-09-28 13:33:17 -04:00
2021-11-24 04:26:12 -05:00
/** @return True of the the execution context is valid and initialized. */
bool IsValid ( ) const { return Owner ! = nullptr & & StateTree ! = nullptr ; }
2021-09-28 13:33:17 -04:00
/** Start executing. */
2022-02-24 08:19:23 -05:00
EStateTreeRunStatus Start ( FStateTreeInstanceData * ExternalInstanceData = nullptr ) ;
2021-09-28 13:33:17 -04:00
/** Stop executing. */
2022-02-24 08:19:23 -05:00
EStateTreeRunStatus Stop ( FStateTreeInstanceData * ExternalInstanceData = nullptr ) ;
2021-09-28 13:33:17 -04:00
/** Tick the state tree logic. */
2022-02-24 08:19:23 -05:00
EStateTreeRunStatus Tick ( const float DeltaTime , FStateTreeInstanceData * ExternalInstanceData = nullptr ) ;
2021-09-28 13:33:17 -04:00
/** @return Pointer to a State or null if state not found */
const FBakedStateTreeState * GetStateFromHandle ( const FStateTreeHandle StateHandle ) const
{
return ( StateTree & & StateTree - > States . IsValidIndex ( StateHandle . Index ) ) ? & StateTree - > States [ StateHandle . Index ] : nullptr ;
}
2021-11-12 05:48:11 -05:00
/** @return Array view to external data descriptors associated with this context. Note: Init() must be called before calling this method. */
TConstArrayView < FStateTreeExternalDataDesc > GetExternalDataDescs ( ) const
2021-09-28 13:33:17 -04:00
{
check ( StateTree ) ;
2021-11-12 05:48:11 -05:00
return StateTree - > ExternalDataDescs ;
2021-09-28 13:33:17 -04:00
}
2021-11-12 05:48:11 -05:00
/** @return True if all required external data pointers are set. */
bool AreExternalDataViewsValid ( ) const
2021-09-28 13:33:17 -04:00
{
check ( StateTree ) ;
bool bResult = true ;
2021-11-12 05:48:11 -05:00
for ( const FStateTreeExternalDataDesc & DataDesc : StateTree - > ExternalDataDescs )
2021-09-28 13:33:17 -04:00
{
2021-11-12 05:48:11 -05:00
const FStateTreeDataView & DataView = DataViews [ DataDesc . Handle . DataViewIndex ] ;
2021-10-27 06:10:12 -04:00
2021-11-12 05:48:11 -05:00
if ( DataDesc . Requirement = = EStateTreeExternalDataRequirement : : Required )
2021-09-28 13:33:17 -04:00
{
2021-10-27 06:10:12 -04:00
// Required items must have valid pointer and expected type.
2021-11-24 04:26:12 -05:00
if ( ! DataView . IsValid ( ) | | ! DataView . GetStruct ( ) - > IsChildOf ( DataDesc . Struct ) )
2021-10-27 06:10:12 -04:00
{
bResult = false ;
break ;
}
}
else
{
// Optional items must have same type if they are set.
2021-11-24 04:26:12 -05:00
if ( DataView . IsValid ( ) & & ! DataView . GetStruct ( ) - > IsChildOf ( DataDesc . Struct ) )
2021-10-27 06:10:12 -04:00
{
bResult = false ;
break ;
}
2021-09-28 13:33:17 -04:00
}
}
return bResult ;
}
2021-11-12 05:48:11 -05:00
/** @return Handle to external data of type InStruct, or invalid handle if struct not found. */
FStateTreeExternalDataHandle GetExternalDataHandleByStruct ( const UStruct * InStruct ) const
2021-09-28 13:33:17 -04:00
{
check ( StateTree ) ;
2021-11-12 05:48:11 -05:00
const FStateTreeExternalDataDesc * DataDesc = StateTree - > ExternalDataDescs . FindByPredicate ( [ InStruct ] ( const FStateTreeExternalDataDesc & Item ) { return Item . Struct = = InStruct ; } ) ;
return DataDesc ! = nullptr ? DataDesc - > Handle : FStateTreeExternalDataHandle : : Invalid ;
2021-09-28 13:33:17 -04:00
}
2021-11-12 05:48:11 -05:00
/** Sets external data view value for specific item. */
void SetExternalData ( const FStateTreeExternalDataHandle Handle , FStateTreeDataView DataView )
2021-09-28 13:33:17 -04:00
{
check ( StateTree ) ;
2021-11-12 05:48:11 -05:00
check ( Handle . IsValid ( ) ) ;
DataViews [ Handle . DataViewIndex ] = DataView ;
2021-09-28 13:33:17 -04:00
}
2021-10-27 06:10:12 -04:00
/**
2021-11-12 05:48:11 -05:00
* Returns reference to external data based on provided handle . The return type is deduced from the handle ' s template type .
* @ param Handle Valid TStateTreeExternalDataHandle < > handle .
* @ return reference to external data based on handle or null if data is not set .
2021-10-27 06:10:12 -04:00
*/
template < typename T >
2021-11-12 05:48:11 -05:00
typename T : : DataType & GetExternalData ( const T Handle ) const
2021-09-28 13:33:17 -04:00
{
check ( StateTree ) ;
2021-11-12 05:48:11 -05:00
check ( Handle . IsValid ( ) ) ;
checkSlow ( StateTree - > ExternalDataDescs [ Handle . DataViewIndex - StateTree - > ExternalDataBaseIndex ] . Requirement ! = EStateTreeExternalDataRequirement : : Optional ) ; // Optionals should query pointer instead.
return DataViews [ Handle . DataViewIndex ] . template GetMutable < typename T : : DataType > ( ) ;
2021-09-28 13:33:17 -04:00
}
2021-10-27 06:10:12 -04:00
/**
2021-11-12 05:48:11 -05:00
* Returns pointer to external data based on provided item handle . The return type is deduced from the handle ' s template type .
* @ param Handle Valid TStateTreeExternalDataHandle < > handle .
* @ return pointer to external data based on handle or null if item is not set or handle is invalid .
2021-10-27 06:10:12 -04:00
*/
template < typename T >
2021-11-12 05:48:11 -05:00
typename T : : DataType * GetExternalDataPtr ( const T Handle ) const
2021-10-27 06:10:12 -04:00
{
check ( StateTree ) ;
2021-11-12 05:48:11 -05:00
return Handle . IsValid ( ) ? DataViews [ Handle . DataViewIndex ] . template GetMutablePtr < typename T : : DataType > ( ) : nullptr ;
2021-10-27 06:10:12 -04:00
}
2021-11-12 05:48:11 -05:00
2021-11-24 04:26:12 -05:00
FStateTreeDataView GetExternalDataView ( const FStateTreeExternalDataHandle Handle )
{
check ( StateTree ) ;
if ( Handle . IsValid ( ) )
{
return DataViews [ Handle . DataViewIndex ] ;
}
return FStateTreeDataView ( ) ;
}
2021-11-12 05:48:11 -05:00
/**
2021-11-24 04:26:12 -05:00
* Returns reference to instance data property based on provided handle . The return type is deduced from the handle ' s template type .
* @ param Handle Valid FStateTreeInstanceDataPropertyHandle < > handle .
* @ return reference to instance data property based on handle .
*/
2021-11-12 05:48:11 -05:00
template < typename T >
typename T : : DataType & GetInstanceData ( const T Handle ) const
{
check ( StateTree ) ;
check ( Handle . IsValid ( ) ) ;
return * ( typename T : : DataType * ) ( DataViews [ Handle . DataViewIndex ] . GetMemory ( ) + Handle . PropertyOffset ) ;
}
/**
2021-11-24 04:26:12 -05:00
* Returns pointer to instance data property based on provided handle . The return type is deduced from the handle ' s template type .
* @ param Handle Valid FStateTreeInstanceDataPropertyHandle < > handle .
* @ return pointer to instance data property based on handle or null if item is not set or handle is invalid .
*/
2021-11-12 05:48:11 -05:00
template < typename T >
typename T : : DataType * GetInstanceDataPtr ( const T Handle ) const
{
check ( StateTree ) ;
return Handle . IsValid ( ) ? ( typename T : : DataType * ) ( DataViews [ Handle . DataViewIndex ] . GetMemory ( ) + Handle . PropertyOffset ) : nullptr ;
}
2021-11-24 04:26:12 -05:00
/**
* Used internally by the Blueprint wrappers to get wrapped instance objects .
* @ param DataViewIndex Index to a data view
* @ return Pointer to an instance object based .
*/
template < typename T >
T * GetInstanceObjectInternal ( const int32 DataViewIndex ) const
{
const UStruct * Struct = DataViews [ DataViewIndex ] . GetStruct ( ) ;
if ( Struct ! = nullptr & & Struct - > IsChildOf < T > ( ) )
{
return DataViews [ DataViewIndex ] . template GetMutablePtr < T > ( ) ;
}
return nullptr ;
}
2021-10-27 06:10:12 -04:00
2022-02-24 08:19:23 -05:00
EStateTreeRunStatus GetLastTickStatus ( const FStateTreeInstanceData * ExternalInstanceData = nullptr ) const ;
2021-09-28 13:33:17 -04:00
# if WITH_GAMEPLAY_DEBUGGER
/** @return Debug string describing the current state of the execution */
2022-02-24 08:19:23 -05:00
FString GetDebugInfoString ( const FStateTreeInstanceData * ExternalInstanceData = nullptr ) const ;
2021-09-28 13:33:17 -04:00
# endif // WITH_GAMEPLAY_DEBUGGER
# if WITH_STATETREE_DEBUG
2022-02-24 08:19:23 -05:00
FString GetActiveStateName ( const FStateTreeInstanceData * ExternalInstanceData = nullptr ) const ;
2021-09-28 13:33:17 -04:00
2022-02-24 08:19:23 -05:00
void DebugPrintInternalLayout ( const FStateTreeInstanceData * ExternalInstanceData = nullptr ) ;
2021-09-28 13:33:17 -04:00
# endif
2021-11-24 04:26:12 -05:00
2022-02-24 08:19:23 -05:00
void AddStructReferencedObjects ( class FReferenceCollector & Collector ) const ;
void AddStructReferencedObjects ( const FStateTreeInstanceData * ExternalInstanceData , class FReferenceCollector & Collector ) const ;
2021-11-24 04:26:12 -05:00
2021-09-28 13:33:17 -04:00
protected :
/** @return Prefix that will be used by STATETREE_LOG and STATETREE_CLOG, empty by default. */
virtual FString GetInstanceDescription ( ) const { return TEXT ( " " ) ; }
/** Callback when gated transition is triggered. Contexts that are event based can use this to trigger a future event. */
virtual void BeginGatedTransition ( const FStateTreeExecutionState & Exec ) { } ;
/**
* Handles logic for entering State . EnterState is called on new active Evaluators and Tasks that are part of the re - planned tree .
* Re - planned tree is from the transition target up to the leaf state . States that are parent to the transition target state
* and still active after the transition will remain intact .
* @ return Run status returned by the tasks .
*/
2022-02-24 08:19:23 -05:00
EStateTreeRunStatus EnterState ( FStateTreeInstanceData & InstanceData , const FStateTreeTransitionResult & Transition ) ;
2021-09-28 13:33:17 -04:00
/**
* Handles logic for exiting State . ExitState is called on current active Evaluators and Tasks that are part of the re - planned tree .
* Re - planned tree is from the transition target up to the leaf state . States that are parent to the transition target state
* and still active after the transition will remain intact .
*/
2022-02-24 08:19:23 -05:00
void ExitState ( FStateTreeInstanceData & InstanceData , const FStateTreeTransitionResult & Transition ) ;
2021-09-28 13:33:17 -04:00
/**
* Handles logic for exiting State . ExitState is called on current active Evaluators and Tasks in reverse order ( from leaf to root ) .
*/
2022-02-24 08:19:23 -05:00
void StateCompleted ( FStateTreeInstanceData & InstanceData , const FStateTreeHandle CurrentState , const EStateTreeRunStatus CompletionStatus ) ;
2021-09-28 13:33:17 -04:00
/**
* Ticks evaluators of all active states starting from current state by delta time .
* If TickEvaluators ( ) is called multiple times per frame ( i . e . during selection when visiting new states ) , each state and evaluator is ticked only once .
*/
2022-02-24 08:19:23 -05:00
void TickEvaluators ( FStateTreeInstanceData & InstanceData , const FStateTreeHandle CurrentState , const EStateTreeEvaluationType EvalType , const float DeltaTime ) ;
2021-09-28 13:33:17 -04:00
/**
* Ticks tasks of all active states starting from current state by delta time .
* @ return Run status returned by the tasks .
*/
2022-02-24 08:19:23 -05:00
EStateTreeRunStatus TickTasks ( FStateTreeInstanceData & InstanceData , const FStateTreeHandle CurrentState , const float DeltaTime ) ;
2021-09-28 13:33:17 -04:00
/**
* Checks all conditions at given range
* @ return True if all conditions pass .
*/
2022-02-24 08:19:23 -05:00
bool TestAllConditions ( FStateTreeInstanceData & InstanceData , const uint32 ConditionsOffset , const uint32 ConditionsNum ) ;
2021-09-28 13:33:17 -04:00
/**
* Triggers transitions based on current run status . CurrentStatus is used to select which transitions events are triggered .
* If CurrentStatus is " Running " , " Conditional " transitions pass , " Completed/Failed " will trigger " OnCompleted/OnSucceeded/OnFailed " transitions .
* Transition target state can point to a selector state . For that reason the result contains both the target state , as well ass
* the actual next state returned by the selector .
* @ return Transition result describing the source state , state transitioned to , and next selected state .
*/
2022-02-24 08:19:23 -05:00
FStateTreeTransitionResult TriggerTransitions ( FStateTreeInstanceData & InstanceData , const FStateTreeStateStatus CurrentStatus , const int Depth ) ;
2021-09-28 13:33:17 -04:00
/**
* Runs state selection logic starting at the specified state , walking towards the leaf states .
* If the preconditions of NextState are not met , " Invalid " is returned .
* If NextState is a selector state , SelectState is called recursively ( depth - first ) to all child states ( where NextState will be one of child states ) .
* If NextState is a leaf state , the NextState is returned .
2021-11-12 05:48:11 -05:00
* @ param Storage View representing all instance data used by tasks and evaluators
2021-09-28 13:33:17 -04:00
* @ param InitialStateStatus Describes the current state and running status ( will be passed intact to next selector )
* @ param InitialTargetState The state the initial transition target state ( will be passed intact to next selector )
* @ param NextState The state which we try to select next .
* @ param Depth Depth of recursion .
* @ return Transition result describing the source state , transition target state , and next selected state .
*/
2022-02-24 08:19:23 -05:00
FStateTreeTransitionResult SelectState ( FStateTreeInstanceData & InstanceData , const FStateTreeStateStatus InitialStateStatus , const FStateTreeHandle InitialTargetState , const FStateTreeHandle NextState , const int Depth ) ;
2021-09-28 13:33:17 -04:00
/** @return State handles from specified state handle back to the root, specified handle included. */
int32 GetActiveStates ( const FStateTreeHandle StateHandle , TStaticArray < FStateTreeHandle , 32 > & OutStateHandles ) const ;
/** @return Mutable storage based on storage settings. */
2022-02-24 08:19:23 -05:00
FStateTreeInstanceData & SelectMutableInstanceData ( FStateTreeInstanceData * ExternalInstanceData )
2021-09-28 13:33:17 -04:00
{
2022-02-24 08:19:23 -05:00
check ( StorageType ! = EStateTreeStorage : : External | | ( StorageType = = EStateTreeStorage : : External & & ExternalInstanceData ! = nullptr ) ) ;
return StorageType = = EStateTreeStorage : : External ? * ExternalInstanceData : InternalInstanceData ;
2021-09-28 13:33:17 -04:00
}
/** @return Const storage based on storage settings. */
2022-02-24 08:19:23 -05:00
const FStateTreeInstanceData & SelectInstanceData ( const FStateTreeInstanceData * ExternalInstanceData ) const
2021-09-28 13:33:17 -04:00
{
2022-02-24 08:19:23 -05:00
check ( StorageType ! = EStateTreeStorage : : External | | ( StorageType = = EStateTreeStorage : : External & & ExternalInstanceData ! = nullptr ) ) ;
return StorageType = = EStateTreeStorage : : External ? * ExternalInstanceData : InternalInstanceData ;
2021-09-28 13:33:17 -04:00
}
2021-11-12 05:48:11 -05:00
/** @return View to an Evaluator, a Task, or a Condition instance data. */
2022-02-24 08:19:23 -05:00
FStateTreeDataView GetInstanceData ( FStateTreeInstanceData & InstanceData , const bool bIsObject , const int32 Index ) const
2021-09-28 13:33:17 -04:00
{
2021-11-24 04:26:12 -05:00
if ( UNLIKELY ( bIsObject = = true ) )
{
2022-02-24 08:19:23 -05:00
const FStateTreeExecutionState & Exec = GetExecState ( InstanceData ) ;
2021-11-24 04:26:12 -05:00
return FStateTreeDataView ( Exec . InstanceObjects [ Index ] ) ;
}
2022-02-24 08:19:23 -05:00
return FStateTreeDataView ( InstanceData . GetMutable ( Index ) ) ;
2021-09-28 13:33:17 -04:00
}
2021-11-12 05:48:11 -05:00
/** @return StateTree execution state from the instance storage. */
2022-02-24 08:19:23 -05:00
static FStateTreeExecutionState & GetExecState ( FStateTreeInstanceData & InstanceData )
2021-09-28 13:33:17 -04:00
{
2022-02-24 08:19:23 -05:00
return InstanceData . GetMutable < FStateTreeExecutionState > ( 0 ) ;
2021-09-28 13:33:17 -04:00
}
2022-02-24 08:19:23 -05:00
/** @return const StateTree execution state from the instance storage. */
static const FStateTreeExecutionState & GetExecState ( const FStateTreeInstanceData & InstanceData )
2021-11-12 05:48:11 -05:00
{
2022-02-24 08:19:23 -05:00
return InstanceData . Get < FStateTreeExecutionState > ( 0 ) ;
}
/** @return StateTree node at specified index. */
template < typename T >
const T & GetNode ( const int32 Index ) const
{
return StateTree - > Nodes [ Index ] . template Get < T > ( ) ;
2021-11-12 05:48:11 -05:00
}
2021-09-28 13:33:17 -04:00
/** @return String describing state status for logging and debug. */
FString GetStateStatusString ( const FStateTreeStateStatus StateStatus ) const ;
/** @return String describing state name for logging and debug. */
FString GetSafeStateName ( const FStateTreeHandle State ) const ;
/** @return String describing full path of an activate state for logging and debug. */
FString DebugGetStatePath ( TArrayView < FStateTreeHandle > ActiveStateHandles , int32 ActiveStateIndex ) const ;
/** The StateTree asset the context is initialized for */
UPROPERTY ( )
const UStateTree * StateTree = nullptr ;
UPROPERTY ( )
UObject * Owner = nullptr ;
/** States visited during a tick while updating evaluators. Initialized to match the number of states in the asset. */
TArray < bool > VisitedStates ;
2021-11-12 05:48:11 -05:00
/** Array of data pointers (external data, tasks, evaluators, conditions), used during evaluation. Initialized to match the number of items in the asset. */
TArray < FStateTreeDataView > DataViews ;
2021-09-28 13:33:17 -04:00
/** Optional Instance of the storage */
2022-02-24 08:19:23 -05:00
FStateTreeInstanceData InternalInstanceData ;
2021-09-28 13:33:17 -04:00
/** Storage type of the context */
EStateTreeStorage StorageType = EStateTreeStorage : : Internal ;
} ;
2021-11-24 04:26:12 -05:00
template < >
struct TStructOpsTypeTraits < FStateTreeExecutionContext > : public TStructOpsTypeTraitsBase2 < FStateTreeExecutionContext >
{
enum
{
WithAddStructReferencedObjects = true ,
} ;
} ;