学习的来源:《设计模式与游戏完美开发》
定义
状态模式在GOF中的解释:
让一个对象的行为随着内部状态的改变而变化,而该对象也像是换了一个类一样
以游戏的方式来解释:
当德鲁伊(对象)由人型变为兽性(内部状态改变)时,他所施展的技能(对象的行为)也会有所改变,玩家此时就像是在操作另一个不同的角色(就像换了一个类一样)
- Context(状态拥有者):
- 是一个具有“状态”的属性的类,可以指定相关的接口,让外界能够得知状态的改变或通过操作让状态改变
- 有状态属性的类例如:游戏角色有潜行,攻击,施法等状态;好友上线,脱机,忙碌等状态;GOF使用TCP联网为例,有已连接,等待链接,断线等状态。这些类中会有一个ConcrateState[X]子类的对象为其成员,用来代表当前的状态。
- State(状态接口类):
- 制定状态的接口,负责规范Context(状态拥有者)在特定状态下要表现的行为
- ConcrateState(具体状态类):
- 继承自State(状态接口类)
- 实现Context(状态拥有者)在特定状态下该有的行为,例如,实现角色在潜行状态时该有的行动变换,3D模型变半透明,不能被敌方角色察觉等行为。
状态模式下的实现案例
state.cs
public class Context
{
//用来表示当前的状态
State m_State = null;
/// <summary>
/// 外界可以通过Request来呈现当前状态该有的行为
/// </summary>
/// <param name="value"></param>
public void Request(int value)
{
m_State.Handle(value);
}
/// <summary>
/// 指定Context当前的状态
/// </summary>
/// <param name="state">设置状态</param>
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;
}
/// <summary>
/// 抽象方法,让子类继承此方法,重写该方法,来呈现不同状态的行为
/// </summary>
/// <param name="value"></param>
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了
使用案例:使用状态模式下实现游戏场景的转换
- 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
- 游戏服务器连线状态
- 关卡进行状态