You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
- Changed FStateTreeHandle to FStateTreeStateHandle, used only for indexing states - Added uint16 and uin8 index types, which can represent invalid index (aka INDEX_NONE) - Changed indices that can be optional to the index types above - Added validation and error loggic when index types overflow during compile - Removed 2 indirections (in common case) and halved the memory usage of property copies #jira none #rb Mieszko.Zielinski #preflight 6295d26e91004dd61ced370b [CL 20434838 by mikko mononen in ue5-main branch]
412 lines
17 KiB
C++
412 lines
17 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#pragma once
|
|
|
|
#include "StateTree.h"
|
|
#include "StateTreePropertyBindings.h"
|
|
#include "StateTreeInstanceData.h"
|
|
#include "StateTreeExecutionContext.generated.h"
|
|
|
|
struct FStateTreeEvaluatorBase;
|
|
struct FStateTreeTaskBase;
|
|
struct FStateTreeConditionBase;
|
|
|
|
USTRUCT()
|
|
struct STATETREEMODULE_API FStateTreeExecutionState
|
|
{
|
|
GENERATED_BODY()
|
|
|
|
/** Currently active states */
|
|
FStateTreeActiveStates ActiveStates;
|
|
|
|
/** Index of the first task struct in the currently initialized instance data. */
|
|
FStateTreeIndex16 FirstTaskStructIndex = FStateTreeIndex16::Invalid;
|
|
|
|
/** Index of the first task object in the currently initialized instance data. */
|
|
FStateTreeIndex16 FirstTaskObjectIndex = FStateTreeIndex16::Invalid;
|
|
|
|
/** The index of the task that failed during enter state. Exit state uses it to call ExitState() symmetrically. */
|
|
FStateTreeIndex16 EnterStateFailedTaskIndex = FStateTreeIndex16::Invalid;
|
|
|
|
/** Result of last tick */
|
|
EStateTreeRunStatus LastTickStatus = EStateTreeRunStatus::Failed;
|
|
|
|
/** Running status of the instance */
|
|
EStateTreeRunStatus TreeRunStatus = EStateTreeRunStatus::Unset;
|
|
|
|
/** Delayed transition handle, if exists */
|
|
FStateTreeIndex16 GatedTransitionIndex = FStateTreeIndex16::Invalid;
|
|
|
|
/** Number of times a new state has been changed. */
|
|
uint16 StateChangeCount = 0;
|
|
|
|
/** Running time of the delayed transition */
|
|
float GatedTransitionTime = 0.0f;
|
|
};
|
|
|
|
UENUM()
|
|
enum class EStateTreeStorage : uint8
|
|
{
|
|
/** Execution context has internal storage */
|
|
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);
|
|
|
|
/** Updates data view of the parameters by using the default values defined in the StateTree asset. */
|
|
void SetDefaultParameters();
|
|
|
|
/**
|
|
* Updates data view of the parameters by replacing the default values defined in the StateTree asset by the provided values.
|
|
* Note: caller is responsible to make sure external parameters lifetime matches the context.
|
|
*/
|
|
void SetParameters(const FInstancedPropertyBag& Parameters);
|
|
|
|
/** Resets the instance to initial empty state. Note: Does not call ExitState(). */
|
|
void Reset();
|
|
|
|
/** Returns the StateTree asset in use. */
|
|
const UStateTree* GetStateTree() const { return StateTree; }
|
|
|
|
/** @return The owner of the context */
|
|
UObject* GetOwner() const { return Owner; }
|
|
/** @return The world of the owner or nullptr if the owner is not set. */
|
|
UWorld* GetWorld() const { return Owner ? Owner->GetWorld() : nullptr; };
|
|
|
|
/** @return True of the the execution context is valid and initialized. */
|
|
bool IsValid() const { return Owner != nullptr && StateTree != nullptr; }
|
|
|
|
/** Start executing. */
|
|
EStateTreeRunStatus Start(FStateTreeInstanceData* ExternalInstanceData = nullptr);
|
|
/** Stop executing. */
|
|
EStateTreeRunStatus Stop(FStateTreeInstanceData* ExternalInstanceData = nullptr);
|
|
|
|
/** Tick the state tree logic. */
|
|
EStateTreeRunStatus Tick(const float DeltaTime, FStateTreeInstanceData* ExternalInstanceData = nullptr);
|
|
|
|
/** @return Pointer to a State or null if state not found */
|
|
const FCompactStateTreeState* GetStateFromHandle(const FStateTreeStateHandle StateHandle) const
|
|
{
|
|
return (StateTree && StateTree->States.IsValidIndex(StateHandle.Index)) ? &StateTree->States[StateHandle.Index] : nullptr;
|
|
}
|
|
|
|
/** @return Array view to external data descriptors associated with this context. Note: Init() must be called before calling this method. */
|
|
TConstArrayView<FStateTreeExternalDataDesc> GetExternalDataDescs() const
|
|
{
|
|
check(StateTree);
|
|
return StateTree->ExternalDataDescs;
|
|
}
|
|
|
|
/** @return Array view to named external data descriptors associated with this context. Note: Init() must be called before calling this method. */
|
|
TConstArrayView<FStateTreeExternalDataDesc> GetNamedExternalDataDescs() const
|
|
{
|
|
check(StateTree);
|
|
return StateTree->GetNamedExternalDataDescs();
|
|
}
|
|
|
|
/** @return True if all required external data pointers are set. */
|
|
bool AreExternalDataViewsValid() const
|
|
{
|
|
check(StateTree);
|
|
bool bResult = true;
|
|
for (const FStateTreeExternalDataDesc& DataDesc : StateTree->ExternalDataDescs)
|
|
{
|
|
const FStateTreeDataView& DataView = DataViews[DataDesc.Handle.DataViewIndex.Get()];
|
|
|
|
if (DataDesc.Requirement == EStateTreeExternalDataRequirement::Required)
|
|
{
|
|
// Required items must have valid pointer of the expected type.
|
|
if (!DataView.IsValid() || !DataView.GetStruct()->IsChildOf(DataDesc.Struct))
|
|
{
|
|
bResult = false;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Optional items must have same type if they are set.
|
|
if (DataView.IsValid() && !DataView.GetStruct()->IsChildOf(DataDesc.Struct))
|
|
{
|
|
bResult = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (const FStateTreeExternalDataDesc& DataDesc : StateTree->GetNamedExternalDataDescs())
|
|
{
|
|
const FStateTreeDataView& DataView = DataViews[DataDesc.Handle.DataViewIndex.Get()];
|
|
|
|
// Items must have valid pointer of the expected type.
|
|
if (!DataView.IsValid() || !DataView.GetStruct()->IsChildOf(DataDesc.Struct))
|
|
{
|
|
bResult = false;
|
|
break;
|
|
}
|
|
}
|
|
return bResult;
|
|
}
|
|
|
|
/** @return Handle to external data of type InStruct, or invalid handle if struct not found. */
|
|
FStateTreeExternalDataHandle GetExternalDataHandleByStruct(const UStruct* InStruct) const
|
|
{
|
|
check(StateTree);
|
|
const FStateTreeExternalDataDesc* DataDesc = StateTree->ExternalDataDescs.FindByPredicate([InStruct](const FStateTreeExternalDataDesc& Item) { return Item.Struct == InStruct; });
|
|
return DataDesc != nullptr ? DataDesc->Handle : FStateTreeExternalDataHandle::Invalid;
|
|
}
|
|
|
|
/** Sets external data view value for specific item. */
|
|
void SetExternalData(const FStateTreeExternalDataHandle Handle, FStateTreeDataView DataView)
|
|
{
|
|
check(StateTree);
|
|
check(Handle.IsValid());
|
|
DataViews[Handle.DataViewIndex.Get()] = DataView;
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
template <typename T>
|
|
typename T::DataType& GetExternalData(const T Handle) const
|
|
{
|
|
check(StateTree);
|
|
check(Handle.IsValid());
|
|
checkSlow(StateTree->ExternalDataDescs[Handle.DataViewIndex.Get() - StateTree->ExternalDataBaseIndex].Requirement != EStateTreeExternalDataRequirement::Optional); // Optionals should query pointer instead.
|
|
return DataViews[Handle.DataViewIndex.Get()].template GetMutable<typename T::DataType>();
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
template <typename T>
|
|
typename T::DataType* GetExternalDataPtr(const T Handle) const
|
|
{
|
|
check(StateTree);
|
|
return Handle.IsValid() ? DataViews[Handle.DataViewIndex.Get()].template GetMutablePtr<typename T::DataType>() : nullptr;
|
|
}
|
|
|
|
FStateTreeDataView GetExternalDataView(const FStateTreeExternalDataHandle Handle)
|
|
{
|
|
check(StateTree);
|
|
if (Handle.IsValid())
|
|
{
|
|
return DataViews[Handle.DataViewIndex.Get()];
|
|
}
|
|
return FStateTreeDataView();
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
template <typename T>
|
|
typename T::DataType& GetInstanceData(const T Handle) const
|
|
{
|
|
check(StateTree);
|
|
check(Handle.IsValid());
|
|
return *(typename T::DataType*)(DataViews[Handle.DataViewIndex.Get()].GetMemory() + Handle.PropertyOffset);
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
template <typename T>
|
|
typename T::DataType* GetInstanceDataPtr(const T Handle) const
|
|
{
|
|
check(StateTree);
|
|
return Handle.IsValid() ? (typename T::DataType*)(DataViews[Handle.DataViewIndex.Get()].GetMemory() + Handle.PropertyOffset) : nullptr;
|
|
}
|
|
|
|
/**
|
|
* 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 FStateTreeIndex16 DataViewIndex) const
|
|
{
|
|
const UStruct* Struct = DataViews[DataViewIndex.Get()].GetStruct();
|
|
if (Struct != nullptr && Struct->IsChildOf<T>())
|
|
{
|
|
return DataViews[DataViewIndex.Get()].template GetMutablePtr<T>();
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
EStateTreeRunStatus GetLastTickStatus(const FStateTreeInstanceData* ExternalInstanceData = nullptr) const;
|
|
|
|
const FStateTreeActiveStates& GetActiveStates(const FStateTreeInstanceData* ExternalInstanceData = nullptr) const;
|
|
|
|
#if WITH_GAMEPLAY_DEBUGGER
|
|
/** @return Debug string describing the current state of the execution */
|
|
FString GetDebugInfoString(const FStateTreeInstanceData* ExternalInstanceData = nullptr) const;
|
|
#endif // WITH_GAMEPLAY_DEBUGGER
|
|
|
|
#if WITH_STATETREE_DEBUG
|
|
int32 GetStateChangeCount(const FStateTreeInstanceData* ExternalInstanceData = nullptr) const;
|
|
|
|
void DebugPrintInternalLayout(const FStateTreeInstanceData* ExternalInstanceData = nullptr);
|
|
#endif
|
|
|
|
FString GetActiveStateName(const FStateTreeInstanceData* ExternalInstanceData = nullptr) const;
|
|
TArray<FName> GetActiveStateNames(const FStateTreeInstanceData* ExternalInstanceData = nullptr) const;
|
|
|
|
protected:
|
|
|
|
/** @return Prefix that will be used by STATETREE_LOG and STATETREE_CLOG, empty by default. */
|
|
virtual FString GetInstanceDescription() const;
|
|
|
|
/** 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) {};
|
|
|
|
void UpdateInstanceData(FStateTreeInstanceData& InstanceData, const FStateTreeActiveStates& CurrentActiveStates, const FStateTreeActiveStates& NextActiveStates);
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
EStateTreeRunStatus EnterState(FStateTreeInstanceData& InstanceData, const FStateTreeTransitionResult& Transition);
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
void ExitState(FStateTreeInstanceData& InstanceData, const FStateTreeTransitionResult& Transition);
|
|
|
|
/**
|
|
* Handles logic for signalling State completed. StateCompleted is called on current active Evaluators and Tasks in reverse order (from leaf to root).
|
|
*/
|
|
void StateCompleted(FStateTreeInstanceData& InstanceData);
|
|
|
|
/**
|
|
* Ticks global evaluators by delta time.
|
|
*/
|
|
void TickEvaluators(FStateTreeInstanceData& InstanceData, const float DeltaTime);
|
|
|
|
void StartEvaluators(FStateTreeInstanceData& InstanceData);
|
|
|
|
void StopEvaluators(FStateTreeInstanceData& InstanceData);
|
|
|
|
/**
|
|
* Ticks tasks of all active states starting from current state by delta time.
|
|
* @return Run status returned by the tasks.
|
|
*/
|
|
EStateTreeRunStatus TickTasks(FStateTreeInstanceData& InstanceData, const float DeltaTime);
|
|
|
|
/**
|
|
* Checks all conditions at given range
|
|
* @return True if all conditions pass.
|
|
*/
|
|
bool TestAllConditions(const int32 ConditionsOffset, const int32 ConditionsNum);
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
bool TriggerTransitions(FStateTreeInstanceData& InstanceData, FStateTreeTransitionResult& OutTransition);
|
|
|
|
/**
|
|
* Runs state selection logic starting at the specified state, walking towards the leaf states.
|
|
* If a state cannot be selected, false is returned.
|
|
* If NextState is a selector state, SelectStateInternal is called recursively (depth-first) to all child states (where NextState will be one of child states).
|
|
* If NextState is a leaf state, the active states leading from root to the leaf are returned.
|
|
* @param InstanceData Reference to the instance data
|
|
* @param NextState The state which we try to select next.
|
|
* @param OutNewActiveStates Active states that got selected.
|
|
* @return True if succeeded to select new active states.
|
|
*/
|
|
bool SelectState(FStateTreeInstanceData& InstanceData, const FStateTreeStateHandle NextState, FStateTreeActiveStates& OutNewActiveStates);
|
|
|
|
/**
|
|
* Used internally to do the recursive part of the SelectState().
|
|
*/
|
|
bool SelectStateInternal(FStateTreeInstanceData& InstanceData, const FStateTreeStateHandle NextState, FStateTreeActiveStates& OutNewActiveStates);
|
|
|
|
/** @return Mutable storage based on storage settings. */
|
|
FStateTreeInstanceData& SelectMutableInstanceData(FStateTreeInstanceData* ExternalInstanceData)
|
|
{
|
|
check(StorageType != EStateTreeStorage::External || (StorageType == EStateTreeStorage::External && ExternalInstanceData != nullptr));
|
|
return StorageType == EStateTreeStorage::External ? *ExternalInstanceData : InternalInstanceData;
|
|
}
|
|
|
|
/** @return Const storage based on storage settings. */
|
|
const FStateTreeInstanceData& SelectInstanceData(const FStateTreeInstanceData* ExternalInstanceData) const
|
|
{
|
|
check(StorageType != EStateTreeStorage::External || (StorageType == EStateTreeStorage::External && ExternalInstanceData != nullptr));
|
|
return StorageType == EStateTreeStorage::External ? *ExternalInstanceData : InternalInstanceData;
|
|
}
|
|
|
|
/** @return StateTree execution state from the instance storage. */
|
|
static FStateTreeExecutionState& GetExecState(FStateTreeInstanceData& InstanceData)
|
|
{
|
|
return InstanceData.GetMutableStruct(0).GetMutable<FStateTreeExecutionState>();
|
|
}
|
|
|
|
/** @return const StateTree execution state from the instance storage. */
|
|
static const FStateTreeExecutionState& GetExecState(const FStateTreeInstanceData& InstanceData)
|
|
{
|
|
return InstanceData.GetStruct(0).Get<FStateTreeExecutionState>();
|
|
}
|
|
|
|
/** Sets up parameter data view for a linked state and copies bound properties. */
|
|
void UpdateLinkedStateParameters(const FStateTreeInstanceData& InstanceData, const FCompactStateTreeState& State, const uint16 ParameterInstanceIndex);
|
|
|
|
/** Sets up parameter data view for subtree state. */
|
|
void UpdateSubtreeStateParameters(const FStateTreeInstanceData& InstanceData, const FCompactStateTreeState& State);
|
|
|
|
/** @return String describing state status for logging and debug. */
|
|
FString GetStateStatusString(const FStateTreeExecutionState& ExecState) const;
|
|
|
|
/** @return String describing state name for logging and debug. */
|
|
FString GetSafeStateName(const FStateTreeStateHandle State) const;
|
|
|
|
/** @return String describing full path of an activate state for logging and debug. */
|
|
FString DebugGetStatePath(const FStateTreeActiveStates& ActiveStates, int32 ActiveStateIndex) const;
|
|
|
|
/** The StateTree asset the context is initialized for */
|
|
UPROPERTY(Transient)
|
|
TObjectPtr<const UStateTree> StateTree = nullptr;
|
|
|
|
UPROPERTY(Transient)
|
|
TObjectPtr<UObject> Owner = nullptr;
|
|
|
|
/** Optional Instance of the storage */
|
|
UPROPERTY(Transient)
|
|
FStateTreeInstanceData InternalInstanceData;
|
|
|
|
/** 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;
|
|
|
|
/** Storage type of the context */
|
|
EStateTreeStorage StorageType = EStateTreeStorage::Internal;
|
|
};
|