设计模式:状态模式(state)

状态模式的定义及其简单的游戏应用

状态模式(State)

定义

Context:状态拥有者,制定接口让外界改变状态或者得知状态已经改变。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Context
{
//表示当前状态
State m_state = null;

//外界接口:改变状态
public void Request(int Value)
{
m_state.Handle(Value);
}

//指定类当前状态,本实例里用于state具体类里面进行状态转换操作用
public void SetState(State theState)
{
Debug.Log("Context.SetState:" + theState);
m_state = theState;
}
}

State:状态接口,负责规范Context在特定状态下要表现的行为

1
2
3
4
5
6
7
8
9
10
11
public abstract class State
{
//传入Context类对象,指定给m_Context,用来获取Context类对象信息或者操作Context对象
protected Context m_Context = null;

public State(Context theContext)
{
m_Context = theContext;
}
public abstract void Handle(int value);
}

ConcreteState:具体状态,继承State,实现具体状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class ConcreteStateA : State
{
//确保Context对象是同一个(?)大概
public ConcreteStateA(Context theContext) : base(theContext) { }

public override void Handle(int Value)
{
Debug.Log("ConcreteStateA.Handle");
if (Value > 10)
m_Context.SetState(new ConcreteStateB(m_Context));
}
}
public class ConcreteStateB : State
{
public ConcreteStateB(Context theContext) : base(theContext) { }

public override void Handle(int Value)
{
Debug.Log("ConcreteStateB.Handle");
if (Value > 20)
m_Context.SetState(new ConcreteStateC(m_Context));
}
}
public class ConcreteStateC : State
{
public ConcreteStateC(Context theContext) : base(theContext) { }

public override void Handle(int Value)
{
Debug.Log("ConcreteStateC.Handle");
if (Value > 30)
m_Context.SetState(new ConcreteStateA(m_Context));
}
}

游戏应用:简单实现场景切换

场景控制:Context

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

public class SceneStateController
{
private ISceneState m_State;
private bool m_bRunBegin = false;
//构造器,也可以不要(?)
public SceneStateController() { }

//设置状态
public void SetState(ISceneState State,string LoadSceneName)
{
Debug.Log("SetState:" + State.ToString());
m_bRunBegin = false;

//载入场景
LoadScene(LoadSceneName);

//前一个场景结束
if (m_State != null)
m_State.StateEnd();

m_State = State;

}
private void LoadScene(string LoadSceneName)
{
if (LoadSceneName == null || LoadSceneName.Length == 0)
return;
SceneManager.LoadScene(LoadSceneName);

}

//更新
public void StateUpdate()
{

//通知新的State开始
if (m_State != null && m_bRunBegin == false)
{
m_State.StateBegin();
m_bRunBegin = true;
}

if (m_State != null)
m_State.StateUpdate();
}
}

场景接口:State

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ISceneState
{
//状态名称
private string m_StateName = "ISceneState";
public string StateName
{
get { return m_StateName; }
set { m_StateName = value; }
}

//控制者
protected SceneStateController m_Controller = null;

//建造者,建造时
public ISceneState(SceneStateController Controller)
{
m_Controller = Controller;
}

//开始
public virtual void StateBegin() { }

//结束
public virtual void StateEnd() { }

//更新
public virtual void StateUpdate() { }

public override string ToString()
{
return string.Format("I_SceneState: StateName={0}]", StateName);
}

}

具体场景实现

开始场景

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class StartState : ISceneState
{
public StartState(SceneStateController Controller):base(Controller)
{
this.StateName = "StartState";
}

//开始
public override void StateBegin()
{
//进行游戏数据加载和初始化

}

//更新
public override void StateUpdate()
{
m_Controller.SetState(new MainMenuState(m_Controller), "MainMenuScene");
}
}

主菜单场景

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
using UnityEngine;
using UnityEngine.UI;

public class MainMenuState : ISceneState
{
public MainMenuState(SceneStateController Controller) : base(Controller)
{
this.StateName = "MainMenuState";
}

public override void StateBegin()
{
//开始

}


public override void StateUpdate()
{
//按下B进入战斗界面
if (Input.GetKeyDown(KeyCode.B))
{
m_Controller.SetState(new BattleState(m_Controller), "BattleScene");
}
}
}

战斗场景

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
using UnityEngine;

public class BattleState : ISceneState
{
public BattleState(SceneStateController Controller) : base(Controller)
{
this.StateName = "BattleState";
}

//开始,待补充
public override void StateBegin()
{
}

//结束,待补充
public override void StateEnd()
{
}

//更新
public override void StateUpdate()
{
//按下空格切换到主菜单界面
if (Input.GetKeyDown(KeyCode.Space))
{
m_Controller.SetState(new MainMenuState(m_Controller), "MainMenuScene");
}
}

}

GameLoop

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameLoop : MonoBehaviour
{
//场景状态
SceneStateController m_SceneStateController = new SceneStateController();

private void Awake()
{
//转换场景不删除
GameObject.DontDestroyOnLoad(this.gameObject);

//随机数种子
UnityEngine.Random.InitState((int)DateTime.Now.Ticks);
}

private void Start()
{
//设置起始场景
m_SceneStateController.SetState(new StartState(m_SceneStateController), "");
}

private void Update()
{
//使具体场景不需要继承MonoBehaviour也可以实现持续更新
m_SceneStateController.StateUpdate();
}
}

个人理解

context是状态和外部的连接,状态的控制器,设置(改变)状态在这里面,里面有一个state类的实体,用来保存当前存在的状态。外部通过context来改变状态。

state是具体状态的父类,规范状态要做什么,以及引用了控制器实体。

具体状态通过控制器里面的函数切换状态,切换各个状态的具体条件也写在这里面,通过引用控制器实体里的函数来转换当前存在的状态。