【UE4 设计模式】状态模式 State Pattern

概述

描述

  • 允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。

  • 其别名为状态对象(Objects for States),状态模式是一种对象行为型模式。

    image

  • 有限状态机(FSMs)

    • 拥有状态机所有可能状态的集合
    • 状态机同时只能在一个状态
    • 一连串的输入或事件被发送给状态机
    • 每个状态都有一系列的转移,每个转移与输入和另一状态相关
    • 动画状态机系统、行为树系统来理解
  • 并发状态机

    • 有些状态需要并行执行,例如动画状态机,经常分为上半身动画与下半身动画融合,如装备动作、射击动作、换弹动作与行走动作并行
  • 层次状态机

    • 状态中嵌套子状态,可以使用继承实现或者状态栈来实现
  • 下推自动机

    • 用栈来存储一系列状态。有限状态机有一个指向状态的指针,下推自动机有一栈指针
    • 新状态压入栈中,“当前的”状态总是在栈顶
    • 弹出最上面的状态,这个状态会被销毁,它下面的状态成为新状态。如UI界面管理

套路

  • 环境类 Context,用于改变状态
  • 抽象状态类 State
  • 具体状态类 ConcreteState

使用场景

  • 对象的行为依赖于它的状态(属性)并且可以根据它的状态改变而改变它的相关行为。
  • 代码中包含大量与对象状态有关的条件语句,这些条件语句的出现,会导致代码的可维护性和灵活性变差,不能方便地增加和删除状态,使客户类与类库之间的耦合增强。在这些条件语句中包含了对象的行为,而且这些条件对应于对象的各种状态。
  • 示例
    • OA办公系统中多种状态:尚未办理;正在办理;正在批示;正在审核
    • TCP 连接状态
    • 动画系统、AI行为树
    • UI界面管理

优缺点

  • 优点
    • 封装了转换规则。
    • 枚举可能的状态,在枚举状态之前需要确定状态种类。
    • 将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
    • 允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。
    • 可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。
  • 缺点
    • 状态模式的使用必然会增加系统类和对象的个数。
    • 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
    • 状态模式对“开闭原则”的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态;而且修改某个状态类的行为也需修改对应类的源代码。

UE4 实践

  • 在UE4中,实现切换UI状态

  • 创建状态抽象类,以接口的形式

    UINTERFACE(MinimalAPI)
    class UStateInterface : public UInterface
    {
    	GENERATED_BODY()
    };
    
    /**
     * 
     */
    class DESIGNPATTERNS_API IStateInterface
    {
    	GENERATED_BODY()
    
    	// Add interface functions to this class. This is the class that will be inherited to implement this interface.
    public:
    	virtual void EnterState() = 0;
    	virtual void ExitState() = 0;
    };
    
  • 创建状态具体类UBaseStateWidget ,作为UI的父类,切换时使用

    UCLASS()
    class DESIGNPATTERNS_API UBaseStateWidget : public UUserWidget, public IStateInterface
    {
    	GENERATED_BODY()
    public:
    	virtual void EnterState() override;
    	virtual void ExitState() override;
    
    	UFUNCTION(BlueprintNativeEvent,BlueprintCallable,Category="State Pattern")
    		void OnEnterState();
    	UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "State Pattern")
    		void OnExitState();
    };
    
    #include "BaseStateWidget.h"
    
    void UBaseStateWidget::EnterState()
    {
    	OnEnterState();
    }
    
    void UBaseStateWidget::ExitState()
    {
    	OnExitState();
    }
    
    void UBaseStateWidget::OnEnterState_Implementation()
    {
    	AddToViewport();
    }
    
    void UBaseStateWidget::OnExitState_Implementation()
    {
    	RemoveFromParent();
    }
    
  • 创建状态管理类

    UCLASS()
    class DESIGNPATTERNS_API AUIStateManager : public AActor
    {
    	GENERATED_BODY()
    	
    public:	
    	// Sets default values for this actor's properties
    	AUIStateManager();
    
    protected:
    	// Called when the game starts or when spawned
    	virtual void BeginPlay() override;
    
    public:	
    	// Called every frame
    	virtual void Tick(float DeltaTime) override;
    
    	// 改变状态
    	UFUNCTION(BlueprintCallable, Category = "State Pattern")
    	void EnterState(TSubclassOf<UBaseStateWidget> StateWidgetClass);
    
    	// 退出所有状态
    	UFUNCTION(BlueprintCallable, Category = "State Pattern")
    	void ExitAllState();	
    
    	// 当前状态实例
    	UPROPERTY(BlueprintReadWrite, Category = "State Pattern")
    	UBaseStateWidget* CurrentStateWidget;
    
    private:
    	// 存储状态实例
    	TMap<TSubclassOf<UBaseStateWidget>, UBaseStateWidget*> WidgetInstances;
    };
    
    void AUIStateManager::EnterState(TSubclassOf<UBaseStateWidget> StateWidgetClass)
    {
    	if (CurrentStateWidget != nullptr)
    	{
    		CurrentStateWidget->ExitState();
    	}
    
    	if (WidgetInstances.Contains(StateWidgetClass))
    	{
    		CurrentStateWidget = WidgetInstances.FindRef(StateWidgetClass);		
    	}
    	else
    	{
    		APlayerController* PC = UGameplayStatics::GetPlayerController(GetWorld(), 0);
    		CurrentStateWidget = CreateWidget<UBaseStateWidget>(PC, StateWidgetClass);
    		WidgetInstances.Add(StateWidgetClass,CurrentStateWidget);
    	}
    	CurrentStateWidget->EnterState();
    }
    
    void AUIStateManager::ExitAllState()
    {
    	for (auto& Elem : WidgetInstances)
    	{
    		(Elem.Value)->ExitState();
    	}
    }
    
  • 创建状态类UBaseStateWidget 的蓝图派生类

    • WBP_Start —— 开始界面
    • WBP_LoadingScreen —— 加载界面
    • WBP_Save —— 存档界面
    • WBP_Option —— 选项界面
      image
      image
  • 创建 UIStateManager 蓝图派生类
    image

  • 效果
    image

  • 扩展,可进一步用栈状态机实现界面管理


参考

posted @ 2021-06-14 19:31  砥才人  阅读(961)  评论(0编辑  收藏  举报