【UE客户端/技术策划】- 工具链篇:增强型有限状态机框架 #低耦合 #高扩展性 #模块化

本文在博客园原创,转载请标注出处

前言

在项目的GamePlay构建中,经常存在状态切换的需求,而根据需求,通常会采用状态机/状态表做处理。
GamePlay流程中,对状态机的需求频率和种类也在变多,同时还要有一定的数据驱动能力,例如剧情编辑器/对话编辑器,动画驱动角色等。通常情况下,状态机会在某一个模块中不断被特化,逐渐变成与模块较为耦合的特化状态机(例如UE中的动画蓝图)。
所以本文的需求是,开发一个处于特化前的通用分层状态机,旨在提供一个完全可供各种扩展方向,模块化的状态机框架

此状态机框架(本文后续称之为EFSM),拥有以下特点:

  • 原生框架中不提供原HFSM中的状态机嵌套状态机行为,防止过度递归导致的性能查询落后。
  • 提供条件上下文,条件评估器两大模块用于状态过渡,同时状态机不提供主动式更新策略(类似动画蓝图状态机中主动获取/绑定外部信息做更新)
  • 状态机更新逻辑策略化,状态机载体能够切换不同的更新策略,提供分割的逻辑更新规律,并且支持完全的扩展自定义
  • 框架大部分采用模块化,最大限度提供状态机本身扩展性
  • 状态本身作为“定义层”,核心是处理代理转发和状态携带文,同时可以扩展状态携带的数据(例如同一种状态机可以出现携带文本的剧情状态和携带数据的数据状态)

框架总览

核心框架思路如下(蓝框代表抽象类并且使用策略模式)
image

State(状态)

EFSM状态机中的状态主要由UEFSMState(状态) + UEFSMLayer(层)构成

UEFSMState

  • 在状态机中具有唯一ID,被UEFSMLayer(层)拥有。
  • 内建型设计,内部提供运行时模式和对应映射的运行函数(OnInit,OnEnter,OnUpdating等)。同时对外提供此状态对应模式的绑定多播和且可扩展映射
  • 非继承型,如果想使用对应状态只能通过绑定对应状态的对应运行模式的多播,保证低耦合设计。

DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnStateEvent);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnStatePausingEvent,float,DeltaSeconds);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnStateUpdatingEvent,float,DeltaSeconds);


UCLASS(Abstract,Blueprintable,BlueprintType,EditInlineNew)
class ENHANCEDFINITESTATEMACHINE_API UEFSMStateNode : public UObject
{
	GENERATED_BODY()
public:
	//状态ID,在状态机中唯一
	UPROPERTY(BlueprintReadOnly)
	FName StateID = NAME_None;

	UPROPERTY(BlueprintAssignable)
	FOnStateEvent OnPreEnterEvent;
	
	UPROPERTY(BlueprintAssignable)
	FOnStateEvent OnEnterEvent;

	UPROPERTY(BlueprintAssignable)
	FOnStateUpdatingEvent OnPreUpdatingEvent;
	
	UPROPERTY(BlueprintAssignable)
	FOnStateUpdatingEvent OnUpdatingEvent;

	UPROPERTY(BlueprintAssignable)
	FOnStateUpdatingEvent OnPostUpdatingEvent;

	UPROPERTY(BlueprintAssignable)
	FOnStatePausingEvent OnPausingEvent;
	
	UPROPERTY(BlueprintAssignable)
	FOnStateEvent OnPreExitEvent;

	UPROPERTY(BlueprintAssignable)
	FOnStateEvent OnExitEvent;

	
	virtual void OnInit();
	
	virtual void OnPreEnter();
	virtual void OnEnter();
	
	virtual void OnPreUpdating(float DeltaSeconds);
	virtual void OnUpdating(float DeltaSeconds);
	virtual void OnPostUpdating(float DeltaSeconds);

	virtual void OnPausing(float DeltaSeconds);

	virtual void OnPreExit();
	virtual void OnExit();
	
};

//状态
UCLASS(Blueprintable,BlueprintType)
class ENHANCEDFINITESTATEMACHINE_API UEFSMState : public UEFSMStateNode
{
	GENERATED_BODY()

public:
	//			流程			//
	//状态进行的事件映射 因为枚举不具备扩展性 一旦使用很难扩展因此采用命名,手动硬编码构建映射
	UPROPERTY(BlueprintReadOnly)
	FName RunningMode = NAME_None;
	//当前所属的状态层
	UPROPERTY(BlueprintReadOnly)
	TObjectPtr<UEFSMLayer> OwnerLayer;
	//当前层内状态与状态之间的过渡
	UPROPERTY(EditAnywhere, BlueprintReadOnly,Instanced)
	TArray<TObjectPtr<UEFSMStateTransition>> StateTransition;

	virtual void RunningModeEvent(float DeltaSeconds);
}

RunningMode即代表和对应函数做了映射,只需要切换当前的RunnigMode即可通过RunningModeEvent运行对应函数。Map表应该由构造内设计
UEFSMStateNode是抽象类且编辑器友好,目的是将真正的状态类分离出来。
UEFSMState不推荐由状态机外部继承,所有的状态都须通过外界主动绑定状态机内部进行,保证框架的分离,同时还能分离Editor和Runtime下数据问题

UEFSMLayer

  • 本身不具备UEFSMState(状态)的功能性,属于一个包容状态以及额外的层过渡的容器
  • 拥有区别与UEFSMState(状态)的层过渡,跨层过渡需要满足层过渡
UCLASS(Blueprintable,BlueprintType,EditInlineNew)
class ENHANCEDFINITESTATEMACHINE_API UEFSMLayer : public UObject
{
	GENERATED_BODY()

public:
	//层ID
	UPROPERTY(BlueprintReadOnly)
	FName LayerID = NAME_None;

	//默认状态ID/初始状态ID
	UPROPERTY(EditAnywhere, BlueprintReadOnly,meta = (GetOptions = "GetDefaultState"))
	FName DefaultStateID = NAME_None;
	
	UPROPERTY(EditAnywhere, BlueprintReadOnly,Instanced)
	TMap<FName,TObjectPtr<UEFSMState>> States;
	//层过渡
	UPROPERTY(EditAnywhere, BlueprintReadOnly,Instanced)
	TArray<TObjectPtr<UEFSMLayerTransition>> LayerTransition;

	
	virtual void InitLayer();
	virtual void ReLinkLayer();
	
	UPROPERTY()
	TObjectPtr<UEFSMStateMachineContext> OwnerStateMachine;

#if WITH_EDITOR
	UFUNCTION()
	TArray<FString> GetDefaultState() const;
#endif
};

Transition(过渡)

EFSM状态机中的过渡判定主要由两大方面构成:UEFSMTransition(过渡) + UEFSMTransitionContext(过渡上下文)
UEFSMTransition(过渡)会依赖传递进来的UEFSMTransitionContext(过渡上下文)去匹配当前需要判定的字段,通过其中的条件来判断是否完成过渡

  • UEFSMTransition(过渡)的结果由选择的UEFSMConditionEvaluatorModeUtils(条件评估策略)结果来决定
  • UEFSMConditionEvaluatorModeUtils(条件评估策略)的结果由不同的策略综合复数UEFSMConditionEvaluatorModeUtils(条件评估器)来判定,通常有三种策略:简单(只判定单一条件),复杂(能混合复数条件结果,同时提供复数条件评估器(AND,OR,NOT,NOR,自定义表达式)),回调。

UEFSMTransitionContext(过渡上下文)

UEFSMTransitionContext(过渡上下文)通常由状态机载体拥有并创建,每个状态机在运行时会独立存在一份过渡上下文。

//值信息
template<typename TValue>
struct TValueData
{
	TValue _value;
	//时间戳
	double Timestamp = -1;
};

//过渡上下文 
UCLASS(Blueprintable, BlueprintType, EditInlineNew)
class ENHANCEDFINITESTATEMACHINE_API UEFSMTransitionContext: public UObject
{
	GENERATED_BODY()

public:
	TMap<FName, TValueData<bool>> _BoolValues;
	TMap<FName, TValueData<int32>> _IntValues;
	TMap<FName, TValueData<float>> _FloatValues;
	TMap<FName, TValueData<double>> _DoubleValues;
	TMap<UClass*, TValueData<UObject*>> _ObjectValues;
	TMap<FName, TValueData<FString>> _EnumValues;
	TMap<FName, TValueData<FName>> _TagValues;

	bool FindAndGetBool(const FName& Key,bool& OutBool) const;
	bool FindAndGetInt(const FName& Key,int32& OutInt) const;
	bool FindAndGetFloat(const FName& Key,float& OutFloat) const;
	bool FindAndGetDouble(const FName& Key,double& OutDouble) const;
	bool FindAndGetObject(const UClass* ClassType,UObject*& OutObject) const;
	bool FindAndGetEnum(const FName& EnumType, FString& OutEnumValue) const;
	bool FindAndGetTag(const FName& Tag, FName& OutTag) const;

	bool RemoveBool(const FName& Key);
	bool RemoveInt(const FName& Key);
	bool RemoveFloat(const FName& Key);
	bool RemoveDouble(const FName& Key);
	bool RemoveObject(const UClass* ClassType);      
	bool RemoveEnum(const FName& EnumType);            
	bool RemoveTag(const FName& Tag);
	int32 RemoveTags(const TSet<FName>& Tags);

	void EmptyContext();
};

UEFSMConditionEvaluatorUtils(条件评估器)

条件评估器同样属于策略类的一种,此类的每一种子类均提供过渡上下文中对应类型(每一个类型对应一个子类),同时基类提供一个通用的结果返回

image

选择不同条件评估器时会出现对应过度上下文中的key值,条件值和判定标准:
image
image
image
image
image
image
image

UCLASS(Abstract,Blueprintable, BlueprintType, EditInlineNew)
class ENHANCEDFINITESTATEMACHINE_API UEFSMConditionEvaluatorUtils : public UObject
{
    GENERATED_BODY()
public:
    //条件判定结果	
    virtual bool ConditionEvaluatorResult(const UEFSMTransitionContext* Context) { return false; }
};

// 上下文数值类型判断标准
UENUM(BlueprintType, meta = (Bitflags, UseEnumValuesAsMaskValuesInEditor = "true"))
enum class EEFSMContextNumericalTypeCriteria : uint8
{
    Equal       = 0, // 等于(注意:0 表示默认等于)
    NotEqual    = 1 << 0,
    Greater     = 1 << 1,
    Less        = 1 << 2,
};
ENUM_CLASS_FLAGS(EEFSMContextNumericalTypeCriteria)


//////////////////////////////////////////////////////////////////////////
// Bool
UCLASS(Blueprintable, BlueprintType)
class ENHANCEDFINITESTATEMACHINE_API UEFSMConditionEvaluatorUtils_Bool : public UEFSMConditionEvaluatorUtils
{
    GENERATED_BODY()
public:
    // 对应上下文所需 Key
    UPROPERTY(EditAnywhere, BlueprintReadOnly,Category= "Conditions")
    FName _BoolContextKey;

    UPROPERTY(EditAnywhere, BlueprintReadWrite,Category= "Conditions")
    bool DesiredBoolValue = true;

    virtual bool ConditionEvaluatorResult(const UEFSMTransitionContext* Context) override;
};

//////////////////////////////////////////////////////////////////////////
// Int
UCLASS(Blueprintable, BlueprintType)
class ENHANCEDFINITESTATEMACHINE_API UEFSMConditionEvaluatorUtils_Int : public UEFSMConditionEvaluatorUtils
{
    GENERATED_BODY()

public:
    UPROPERTY(EditAnywhere, BlueprintReadOnly,Category= "Conditions")
    FName _IntContextKey;

    UPROPERTY(EditAnywhere, BlueprintReadWrite,Category= "Conditions")
    int32 DesiredIntValue = 0;

    /** 用作容差(可为 0) */
    UPROPERTY(EditAnywhere, BlueprintReadWrite,Category= "Conditions")
    float Precision = 0.0f;

    UPROPERTY(EditAnywhere, BlueprintReadWrite,Category= "Conditions")
    EEFSMContextNumericalTypeCriteria ConditionCriteria = EEFSMContextNumericalTypeCriteria::Equal;

    virtual bool ConditionEvaluatorResult(const UEFSMTransitionContext* Context) override;
};

//////////////////////////////////////////////////////////////////////////
// Float
UCLASS(Blueprintable, BlueprintType)
class ENHANCEDFINITESTATEMACHINE_API UEFSMConditionEvaluatorUtils_Float : public UEFSMConditionEvaluatorUtils
{
    GENERATED_BODY()

public:
    UPROPERTY(EditAnywhere, BlueprintReadOnly,Category= "Conditions")
    FName _FloatContextKey;

    UPROPERTY(EditAnywhere, BlueprintReadWrite,Category= "Conditions")
    float DesiredFloatValue = 0.0f;

    /** 容差(默认 0.001f) */
    UPROPERTY(EditAnywhere, BlueprintReadWrite,Category= "Conditions")
    float Precision = 0.001f;

    UPROPERTY(EditAnywhere, BlueprintReadWrite,Category= "Conditions")
    EEFSMContextNumericalTypeCriteria ConditionCriteria = EEFSMContextNumericalTypeCriteria::Equal;

    virtual bool ConditionEvaluatorResult(const UEFSMTransitionContext* Context) override;
};

//////////////////////////////////////////////////////////////////////////
// Double
UCLASS(Blueprintable, BlueprintType)
class ENHANCEDFINITESTATEMACHINE_API UEFSMConditionEvaluatorUtils_Double : public UEFSMConditionEvaluatorUtils
{
    GENERATED_BODY()

public:
    UPROPERTY(EditAnywhere, BlueprintReadOnly,Category= "Conditions")
    FName _DoubleContextKey;

    UPROPERTY(EditAnywhere, BlueprintReadWrite,Category= "Conditions")
    double DesiredDoubleValue = 0.0f;

    /** 容差(默认 0.001f) */
    UPROPERTY(EditAnywhere, BlueprintReadWrite,Category= "Conditions")
    double Precision = 0.001f;

    UPROPERTY(EditAnywhere, BlueprintReadWrite,Category= "Conditions")
    EEFSMContextNumericalTypeCriteria ConditionCriteria = EEFSMContextNumericalTypeCriteria::Equal;

    virtual bool ConditionEvaluatorResult(const UEFSMTransitionContext* Context) override;
};


// 上下文标签类型判断标准
UENUM(BlueprintType, meta = (Bitflags, UseEnumValuesAsMaskValuesInEditor = "true"))
enum class EEFSMContextTagTypeCriteria : uint8
{
    // 所有指定标签均存在
    ExactMatch		= 0, 
    PartialMatch	= 1 << 0, // 任一标签存在
    UnMatch			= 1 << 1 // 任一都不存在(即交集为空)
};
ENUM_CLASS_FLAGS(EEFSMContextTagTypeCriteria)


//////////////////////////////////////////////////////////////////////////
// Tag (集合)
UCLASS(Blueprintable, BlueprintType)
class ENHANCEDFINITESTATEMACHINE_API UEFSMConditionEvaluatorUtils_Tag : public UEFSMConditionEvaluatorUtils
{
    GENERATED_BODY()

public:
    /** 条件标签集合(编辑器中可配置多个标签) */
    UPROPERTY(EditAnywhere, BlueprintReadWrite,Category= "Conditions")
    TSet<FName> DesiredTagsValue;
    
    UPROPERTY(EditAnywhere, BlueprintReadWrite,Category= "Conditions")
    EEFSMContextTagTypeCriteria ConditionCriteria = EEFSMContextTagTypeCriteria::ExactMatch;

    virtual bool ConditionEvaluatorResult(const UEFSMTransitionContext* Context) override;
};


// 枚举类型判断标准 只保留一个等于或者不等于?因为有位掩码存在,我觉得用字符串保存比较好
UENUM(BlueprintType, meta = (Bitflags, UseEnumValuesAsMaskValuesInEditor = "true"))
enum class EEFSMContextEnumTypeCriteria : uint8
{
    Equal       = 0, // 等于(注意:0 表示默认等于)
    NotEqual    = 1 << 0,
};
ENUM_CLASS_FLAGS(EEFSMContextEnumTypeCriteria)


UCLASS(Blueprintable, BlueprintType)
class ENHANCEDFINITESTATEMACHINE_API UEFSMConditionEvaluatorUtils_Enum : public UEFSMConditionEvaluatorUtils
{
    GENERATED_BODY()


public:
    // 选择“枚举类型” 
    UPROPERTY(EditAnywhere, BlueprintReadOnly, meta = (GetOptions = "GetDesiredEnumType"))
    FString DesiredEnumType;

    // 选择“枚举值” 
    UPROPERTY(EditAnywhere, BlueprintReadOnly, meta = (GetOptions = "GetDesiredEnumValue"))
    FString DesiredEnumValue;

    UPROPERTY(EditAnywhere, BlueprintReadWrite,Category= "Conditions")
    EEFSMContextEnumTypeCriteria ConditionCriteria = EEFSMContextEnumTypeCriteria::Equal;


private:
    // GetOptions 必须返回 TArray<FString> 且是 UFUNCTION
#if WITH_EDITOR
    UFUNCTION()
    TArray<FString> GetDesiredEnumType() const;
    UFUNCTION()
    TArray<FString> GetDesiredEnumValue() const;
#endif
    
    virtual bool ConditionEvaluatorResult(const UEFSMTransitionContext* Context) override;
};


UENUM(BlueprintType, meta = (Bitflags, UseEnumValuesAsMaskValuesInEditor = "true"))
enum class EEFSMContextObjectTypeCriteria : uint8
{
    Valid       = 0, //有效
    NoValid     = 1 << 0,//无效
};
ENUM_CLASS_FLAGS(EEFSMContextObjectTypeCriteria)


//检查上下文中是否存在指定类型的实例对象
UCLASS(Blueprintable, BlueprintType)
class ENHANCEDFINITESTATEMACHINE_API UEFSMConditionEvaluatorUtils_Object : public UEFSMConditionEvaluatorUtils
{
    GENERATED_BODY()


public:
    // 选择对象类型
    UPROPERTY(EditAnywhere, BlueprintReadOnly, meta = (GetOptions = "GetDesiredEnumType"))
    TSubclassOf<UObject> DesiredObejctClass;

    UPROPERTY(EditAnywhere, BlueprintReadWrite,Category= "Conditions")
    EEFSMContextObjectTypeCriteria ConditionCriteria = EEFSMContextObjectTypeCriteria::Valid;


    virtual bool ConditionEvaluatorResult(const UEFSMTransitionContext* Context) override;
};

UEFSMConditionEvaluatorModeUtils(条件评估策略)

条件评估器仅判定一项条件,而如果遇到复数条件就需要使用条件评估器,条件评估策略分为三种:简易,复杂,回调
image

简单模式下就是单一条件判定,速度极快
image
复杂模式下会先具有复杂判定标准,其次需要添加复数条件评估器,评估策略会根据复杂评估判定标准综合计算最终结果,速度较快
image
image
同时提供一个可以输入条件表达式的评估模式(此模式会缓存表达式因此消耗较高,速度中等):
image
例如
5cae749d938b1cffdcbdc1c39a2b74c8


UCLASS(Abstract)
class ENHANCEDFINITESTATEMACHINE_API UEFSMConditionEvaluatorModeUtils : public UObject
{
	GENERATED_BODY()

public:
	virtual bool ConditionEvaluatorModeResult(const UEFSMTransitionContext* Context) { return false; }
	
};
//简易条件模式 单一条件,拥有单一条件的评估器工具类
UCLASS(Blueprintable, BlueprintType, EditInlineNew)
class ENHANCEDFINITESTATEMACHINE_API UEFSMConditionEvaluatorModeUtils_SimpleCondition : public UEFSMConditionEvaluatorModeUtils
{
	GENERATED_BODY()

public:
	UPROPERTY(EditAnywhere, Instanced, BlueprintReadWrite)
	TObjectPtr<UEFSMConditionEvaluatorUtils> ConditionEvaluator;
	
	virtual bool ConditionEvaluatorModeResult(const UEFSMTransitionContext* Context) override;
};

//复杂条件模式 拥有复数条件,且拥有判定复数条件的评估器工具类
UCLASS(Blueprintable, BlueprintType, EditInlineNew)
class ENHANCEDFINITESTATEMACHINE_API UEFSMConditionEvaluatorModeUtils_ComplexCondition : public UEFSMConditionEvaluatorModeUtils
{
	GENERATED_BODY()

public:
	//复杂条件评估器,当存在>1的条件数量时,用于整合所有条件整体判断评估器
	UPROPERTY(EditAnywhere, Instanced, BlueprintReadWrite)
	TObjectPtr<UEFSMComplexConditionEvaluatorUtils> ComplexConditionEvaluator;
	
	virtual bool ConditionEvaluatorModeResult(const UEFSMTransitionContext* Context) override;
};

//回调评估器模式 不再由编辑器中处理,绑定回调,此模式下只允许回调
UCLASS(Blueprintable, BlueprintType, EditInlineNew)
class ENHANCEDFINITESTATEMACHINE_API UEFSMConditionEvaluatorModeUtils_RuntimeCondition : public UEFSMConditionEvaluatorModeUtils
{
	GENERATED_BODY()

public:
	
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	FName EvaluatorId = NAME_None;

	virtual bool ConditionEvaluatorModeResult(const UEFSMTransitionContext* Context) override;
	
};

UEFSMTransition(过渡)

UEFSMTransition(过渡)通常被UEFSMState(状态)和EFSMLayer(层)拥有,其主要目的是提供匹配对应过渡上下文中对应字段的条件判定结果。
其子类具有状态过渡和层过渡两种:
image
image

UCLASS(Abstract,Blueprintable, BlueprintType, EditInlineNew)
class ENHANCEDFINITESTATEMACHINE_API UEFSMTransition : public UObject
{
	GENERATED_BODY()
	
public:
	//条件评估策略
	UPROPERTY(EditAnywhere, BlueprintReadWrite,Instanced,Category="Transition")
	TObjectPtr<UEFSMConditionEvaluatorModeUtils> ConditionEvaluatorMode;
	//过渡结果
	virtual bool TransitionResult(const UEFSMTransitionContext* Context);
};
//为保证Runtime时期的速度,条件类均采用在初始化时注入数据,运行期如果要更改状态机结构,需要重新初始化
UCLASS(Blueprintable, BlueprintType)
class ENHANCEDFINITESTATEMACHINE_API UEFSMLayerTransition : public UEFSMTransition
{
	GENERATED_BODY()
	
public:
	//层ID 
	UPROPERTY(EditAnywhere, BlueprintReadOnly,Category="Transition")
	FName ToLayerID = NAME_None;
	//状态ID	指定过渡到层中的某一状态 如果查询状态失败则自动转入默认状态,除非严格约束
	UPROPERTY(EditAnywhere, BlueprintReadOnly,Category="Transition")
	FName ToStateID = NAME_None;
	//过渡约束 如果开启过渡约束则必须过渡到指定状态,如果不存在则不进行过渡
	UPROPERTY(EditAnywhere, BlueprintReadOnly,Category="Transition")
	bool bTransitionConstraint = false;
	//此过渡的优先级,用于处理同状态过渡冲突
	UPROPERTY(EditAnywhere, BlueprintReadOnly,Category="Transition")
	int32 Priority = 0;

	UPROPERTY()
	TObjectPtr<UEFSMLayer> FromLayer;
	UPROPERTY()
	TObjectPtr<UEFSMLayer> ToLayer;
	UPROPERTY()
	TObjectPtr<UEFSMState> ToState;
	
	virtual bool TransitionResult(const UEFSMTransitionContext* Context) override;
};
UCLASS(Blueprintable, BlueprintType)
class ENHANCEDFINITESTATEMACHINE_API UEFSMStateTransition : public UEFSMTransition
{
	GENERATED_BODY()
	
public:

	UPROPERTY(EditAnywhere, BlueprintReadOnly,Category="Transition")
	FName ToStateID = NAME_None;

	//此过渡的优先级,用于处理同状态过渡冲突
	UPROPERTY(EditAnywhere, BlueprintReadOnly,Category="Transition")
	int32 Priority = 0;
	
	UPROPERTY()
	TObjectPtr<UEFSMState> FromState;
	UPROPERTY()
	TObjectPtr<UEFSMState> ToState;

	
	virtual bool TransitionResult(const UEFSMTransitionContext* Context) override;
};

Update(更新)

状态机的更新由剩下的四部分构成:

  • UEFSMStateMachineComponent :状态机载体。对外提供状态机过渡上下文接口和状态机更新策略,同时负责将资产反序列化为Runtime下的数据并放入状态机上下文中,并不负责更新
  • UEFSMStateMachineUpdateModeUtils : 状态机更新策略。真正更新Runtime下状态机的类,掌管如何过渡,何时触发状态代理等一切Runtime下状态机行为。
  • UEFSMStateMachineContext : 状态机上下文。Runtime下真正的状态机数据。
  • UEFSMStateMachineAsset : 状态机资产。编辑器下的状态机数据,由载体解构并反序列化为状态机上下文

UEFSMStateMachineComponent(载体)

状态机载体。对外提供状态机过渡上下文接口和状态机更新策略,同时负责将资产反序列化为Runtime下的数据并放入状态机上下文中,运行时状态机更新由策略决定
image

UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent))
class ENHANCEDFINITESTATEMACHINE_API UEFSMStateMachineComponent : public UActorComponent
{
	GENERATED_BODY()

public:
	// Sets default values for this component's properties
	UEFSMStateMachineComponent();

protected:
	// Called when the game starts
	virtual void BeginPlay() override;

public:
	// Called every frame
	virtual void TickComponent(float DeltaTime, ELevelTick TickType,FActorComponentTickFunction* ThisTickFunction) override;

public:
	//		状态机过渡更新上下文	//
	void UpdateTransitionContextBool(FName StateMachineID,FName Key, bool Value);
	void UpdateTransitionContextInt(FName StateMachineID,FName Key, int32 Value);
	void UpdateTransitionContextFloat(FName StateMachineID,FName Key, float Value);
	void UpdateTransitionContextDouble(FName StateMachineID,FName Key, double Value);
	void UpdateTransitionContextObject(FName StateMachineID,UClass* ClassType,UObject* ObjectValue);
	void UpdateTransitionContextEnum(FName StateMachineID,UEnum* EnumType,uint8 EnumValue);
	void UpdateTransitionContextTag(FName StateMachineID,FName Tag);
	void UpdateTransitionContextTags(FName StateMachineID,TSet<FName> Tags);

	bool RemoveTransitionContextBool(FName StateMachineID, FName Key);
	bool RemoveTransitionContextInt(FName StateMachineID, FName Key);
	bool RemoveTransitionContextFloat(FName StateMachineID, FName Key);
	bool RemoveTransitionContextDouble(FName StateMachineID, FName Key);
	bool RemoveTransitionContextObject(FName StateMachineID, UClass* ClassType);
	bool RemoveTransitionContextEnum(FName StateMachineID, UEnum* EnumType);
	bool RemoveTransitionContextTag(FName StateMachineID, FName Tag);
	int32 RemoveTransitionContextTags(FName StateMachineID, const TSet<FName>& Tags);
	void EmptyTransitionContext(FName StateMachineID);

	//初始化状态机
	UFUNCTION(BlueprintCallable)
	virtual void InitStateMachine();
	//更新状态机
	UFUNCTION(BlueprintCallable)
	virtual void UpdateStateMachine(float DeltaTime);

	//默认确保状态的正确性和有效性
	UFUNCTION(BlueprintCallable)
	virtual UEFSMState* FindStateInContext(FName StateMachineID,FName StateID);

	UPROPERTY(EditAnywhere)
	TMap<FName,TObjectPtr<UEFSMStateMachineAsset>> StateMachineAsset;

	UPROPERTY(EditAnywhere, BlueprintReadOnly,Instanced)
	TObjectPtr<UEFSMStateMachineUpdateModeUtils> UpdateMode;
	
protected:

	UPROPERTY()
	TMap<FName,TObjectPtr<UEFSMStateMachineContext>> RuntimeStateMachine;
};

UEFSMStateMachineUpdateModeUtils(状态机更新策略)

Runtime下更新状态机的核心类,能够高度扩展自定义更新方式。

UCLASS(Abstract,Blueprintable, BlueprintType, EditInlineNew)
class ENHANCEDFINITESTATEMACHINE_API UEFSMStateMachineUpdateModeUtils : public UObject
{
	GENERATED_BODY()

public:
	virtual void UpdateStateMachine(UEFSMStateMachineContext* StateMachineContext,float DeltaTime) {};
};


UCLASS(Blueprintable, BlueprintType)
class ENHANCEDFINITESTATEMACHINE_API UEFSMStateMachineUpdateModeUtils_Default : public UEFSMStateMachineUpdateModeUtils
{
	GENERATED_BODY()

public:
	virtual void UpdateStateMachine(UEFSMStateMachineContext* StateMachineContext,float DeltaTime) override;
	//状态过渡 
	virtual UEFSMStateTransition* UpdateStateTransitionsResult(UEFSMState* NowState,UEFSMTransitionContext* StateMachineContext);
	virtual UEFSMLayerTransition* UpdateLayerTransitionsResult(UEFSMLayer* NowLayer,UEFSMTransitionContext* StateMachineContext);
};

UEFSMStateMachineAsset(状态机资产) + UEFSMStateMachineContext(状态机上下文)

两个分别为编辑器下的数据和Runtime后的数据,由载体进行反序列化过程。保证运行时和编辑器数据分离。

image

UCLASS()
class ENHANCEDFINITESTATEMACHINE_API UEFSMStateMachineContext : public UObject
{
	GENERATED_BODY()

public:
	//所有层
	UPROPERTY()
	TMap<FName,TObjectPtr<UEFSMLayer>> Layers;

	//初始层
	UPROPERTY()
	FName DefaultLayerID = NAME_None;
	
	//条件上下文
	UPROPERTY()
	TObjectPtr<UEFSMTransitionContext> TransitionContext = nullptr;

	//当前运行状态
	UPROPERTY()
	TObjectPtr<UEFSMState> NowState = nullptr;
	
	UPROPERTY()
	TObjectPtr<UEFSMState> NextState = nullptr;
	
	//状态机已初始化完成
	bool bISInitialized = false;


	virtual void InitStateMachine();
	virtual void ReLinkStateMachine();

	virtual UEFSMState* FindState(FName StateID);
};

总结

本文介绍新状态机框架中的核心要点,同时开发人员可以根据此框架做非常自由的延申

posted @ 2025-11-17 19:52  丨桐  阅读(11)  评论(0)    收藏  举报