游戏设计模式【学习笔记01】——状态模式

 学习的来源:《设计模式与游戏完美开发》

定义

状态模式在GOF中的解释:

让一个对象的行为随着内部状态的改变而变化,而该对象也像是换了一个类一样

以游戏的方式来解释:

当德鲁伊(对象)由人型变为兽性(内部状态改变)时,他所施展的技能(对象的行为)也会有所改变,玩家此时就像是在操作另一个不同的角色(就像换了一个类一样)

游戏设计模式【学习笔记01】——状态模式_第1张图片

  • Context(状态拥有者):
    • 是一个具有“状态”的属性的类,可以指定相关的接口,让外界能够得知状态的改变或通过操作让状态改变
    • 有状态属性的类例如:游戏角色有潜行,攻击,施法等状态;好友上线,脱机,忙碌等状态;GOF使用TCP联网为例,有已连接,等待链接,断线等状态。这些类中会有一个ConcrateState[X]子类的对象为其成员,用来代表当前的状态。
  • State(状态接口类):
    • 制定状态的接口,负责规范Context(状态拥有者)在特定状态下要表现的行为
  • ConcrateState(具体状态类):
    • 继承自State(状态接口类)
    • 实现Context(状态拥有者)在特定状态下该有的行为,例如,实现角色在潜行状态时该有的行动变换,3D模型变半透明,不能被敌方角色察觉等行为。

状态模式下的实现案例

state.cs

public class Context
{
    //用来表示当前的状态
    State m_State = null;

    /// 
    /// 外界可以通过Request来呈现当前状态该有的行为
    /// 
    /// 
    public void Request(int value)
    {
        m_State.Handle(value);
    }

    /// 
    /// 指定Context当前的状态
    /// 
    /// 设置状态
    public void SetState(State state)
    {
        m_State = state;
    }
}
public abstract class State
{
    //设置Context,在后续操作中可以获取Context对象的信息或操作Context对象
    protected Context m_Context = null;
    public State(Context context)
    {
        m_Context = context;
    }

    /// 
    /// 抽象方法,让子类继承此方法,重写该方法,来呈现不同状态的行为
    /// 
    /// 
    public abstract void Handle(int value);
}
public class ConcreateStateA : State
{
    public ConcreateStateA(Context context):base(context)
    {

    }
    public override void Handle(int value)
    {
        Debug.Log("进入A状态");
        if (value >= 0 && value < 10)
        {
            
            m_Context.SetState(new ConcreateStateB(m_Context));
        }
    }
}

public class ConcreateStateB:State
{
    public ConcreateStateB(Context context):base(context)
    { }
    public override void Handle(int value)
    {
        Debug.Log("进入B状态");
        if (value >= 10 && value < 20)
        {
            
            m_Context.SetState(new ConcreateStateA(m_Context));

        }
    }
}

上面的三个类,都需要重写State的Handle方法,用来表示各状态下的不同行为。上述

通过状态的行为来判断是否要通知Context对象转换到另一个状态

状态的转换可以有两种方法:

  • 交给Context类本身,按条件在个状态之间转换;
  • 产生Context类后,马上指定初始状态给Context对象,而在后续执行过程中的状态转换交给state对象负责,Context对象不再介入,上述用到的就是此方法

StateTest.cs 测试范例

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class StateTest : MonoBehaviour
{
    
    void Start()
    {
        UnitTest();
    }

    void UnitTest()
    {
        //创建Context
        Context context = new Context();
        //设置Context初始的状态
        context.SetState(new ConcreateStateB(context));

        //调用Request传入参数
        context.Request(11);
    }

}

结果

首先设置初始状态,此时Context的状态是ConcreateStateB,然后Request(11)后,通过判断此时Context的状态就变为ConcreateStateA了

使用案例:使用状态模式下实现游戏场景的转换

游戏设计模式【学习笔记01】——状态模式_第2张图片

  • ISceneState:场景类的接口,定义游戏中场景转换和执行时所需要调用的方法。
  • StartState,MainMenuState,BattleState:分别对应着不同的场景
  • SceneStateController:场景状态的拥有者,保持当前场景的状态
  • GameLoop:游戏主循环类

ISceneState

public class ISceneState
{
    //控制者
    protected SceneStateController m_Controller ;
    public ISceneState(SceneStateController controller)
    {
        m_Controller = controller;
    }
    //状态开始
    public virtual void StateBegin() { }
    //状态结束
    public virtual void StateEnd() { }
    //状态循环
    public virtual void StateUpdate() { }
}

StartState

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class StartState : ISceneState
{
    public StartState(SceneStateController controller):base(controller)
    {

    }

    public override void StateBegin()
    {
        Debug.Log("游戏开始状态:此时进行游戏数据加载和初始化等");
    }

    public override void StateEnd()
    {
    }

    public override void StateUpdate()
    {
        Debug.Log("切换状态");
        m_Controller.SetState(new MainMenuState(m_Controller), "MainMenu");
    }
}

SceneStateController

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class SceneStateController
{
    private ISceneState m_State;
   
    //设置状态
    public void SetState(ISceneState state,string sceneName)
    {
        //载入场景
        LoadScene(sceneName);
        
        //通知前一个State结束
        if (m_State != null)
            m_State.StateEnd();

        //设置状态
        m_State = state;

    }

    //切换场景
    private void LoadScene(string sceneName)
    {
        if (sceneName == null || sceneName.Length == 0)
        {
            Debug.LogError("场景名为空");
            return;
        }
        SceneManager.LoadScene(sceneName);
    }
    
    //状态更新
    public void StateUpdate()
    {
        if (m_State != null)
        {
            m_State.StateBegin();
        }
        if (m_State != null)
        {
            m_State.StateUpdate();
        }
    }
}

GameLoop

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameLoop : MonoBehaviour
{

    SceneStateController m_SceneStateController = new SceneStateController();

    private void Awake()
    {
        DontDestroyOnLoad(this.gameObject);
    }
    // Start is called before the first frame update
    void Start()
    {
        m_SceneStateController.SetState(new StartState(m_SceneStateController),"");

    }

    // Update is called once per frame
    void Update()
    {
        m_SceneStateController.StateUpdate();
    }
}

状态模式的其他应用

  • 角色AI
  • 游戏服务器连线状态
  • 关卡进行状态

你可能感兴趣的:(状态模式,unity3d)