详细请见:https://www.runoob.com/design-pattern/state-pattern.html

此为FSM有限状态机的基础

什么是状态模式?

如果一个角色现在有如下几个状态,待机、攻击、巡逻等,且当前只有一个状态。

正常在书写代码时进行状态切换往往会维护一个超级史山的ifelse语句非常不便。

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
public class Player
{
public enum PlayerState { Idle, Move, Attack, Hurt, Dead }
public PlayerState currentState;

public void Update()
{
switch (currentState)
{
case PlayerState.Idle:
Debug.Log("角色站立不动,恢复体力");
// 状态转换判断:比如按下移动键切换到Move
if (Input.GetKeyDown(KeyCode.W)) currentState = PlayerState.Move;
else if (Input.GetKeyDown(KeyCode.J)) currentState = PlayerState.Attack;
break;
case PlayerState.Move:
Debug.Log("角色移动中,消耗体力");
if (Input.GetKeyUp(KeyCode.W)) currentState = PlayerState.Idle;
else if (Input.GetKeyDown(KeyCode.J)) currentState = PlayerState.Attack;
else if (GetHurt()) currentState = PlayerState.Hurt;
break;
case PlayerState.Attack:
Debug.Log("角色攻击中,消耗法力");
if (AttackEnd()) currentState = PlayerState.Idle;
else if (GetHurt()) currentState = PlayerState.Hurt;
break;
case PlayerState.Hurt:
Debug.Log("角色受伤,血量减少");
if (HurtEnd()) currentState = PlayerState.Idle;
if (Hp <= 0) currentState = PlayerState.Dead;
break;
case PlayerState.Dead:
Debug.Log("角色死亡,游戏结束");
break;
// 新增状态时,需在此处添加新的case分支
}
}
}

在状态模式中,类的行为是基于它的状态改变的,这种类型的设计模式属于行为型模式。

在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。

状态模式允许对象在内部状态改变时改变其行为,使得对象在不同的状态下有不同的行为表现。通过将每个状态封装成独立的类,可以避免使用大量的条件语句来实现状态切换。

缺优点

优点:

  • 封装状态转换规则:将状态转换逻辑封装在状态对象内部。
  • 易于扩展:增加新的状态类不会影响现有代码。
  • 集中状态相关行为:将所有与特定状态相关的行为集中到一个类中。
  • 简化条件语句:避免使用大量的条件语句来切换行为。
  • 状态共享:允许多个上下文对象共享同一个状态对象

缺点:

  • 增加类和对象数量:每个状态都需要一个具体的状态类。
  • 实现复杂:模式结构和实现相对复杂。
  • 开闭原则支持不足:增加新状态或修改状态行为可能需要修改现有代码。

具体实现

1
2
3
4
5
public interface State
{
void DoAction(Context context);

}
1
2
3
4
5
6
7
8
9
10
11
12
public class Context
{
private State state;
public void SetState(State state)
{
this.state = state;
}
public State GetState()
{
return this.state;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Idle : State
{

public void DoAction(Context context)
{
Debug.Log("当前为空闲状态");
context.SetState(this);
}
}
public class Atk : State
{
public void DoAction(Context context)
{
Debug.Log("当前为攻击状态");
context.SetState(this);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
    void Start()
{
Context context = new Context();
Idle idle = new Idle();
idle.DoAction(context);

Atk atk = new Atk();
atk.DoAction(context);
}
//打印
//当前为空闲状态
//当前为攻击状态