状态设计模式

State Design Pattern

需求

制作一个游戏中玩家操控的角色的动画控制器

将以上状态变化用代码实现

  1. 需要定义一系列的状态

    1
    2
    3
    4
    5
    public const int Idle = 0;
    public const int Run = 1;
    public const int Attack = 2;
    public const int Rise = 3;
    public const int Fall = 4;

  2. 定义一个存储当前状态的变量

    1
    public int State = Idle;

  3. 根据当前的状态, 执行该动作对应的效果

    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
    55
    56
    57
    /// <summary>
    /// 动作:输入攻击指令
    /// </summary>
    public void InputAttackCommand()
    {
    if (State == Idle)
    {
    Console.WriteLine("播放Attack动画");
    State = Attack;
    }
    else if (State == Run)
    {
    Console.WriteLine("不能在奔跑时攻击");
    }
    else if (State == Attack)
    {
    Console.WriteLine("不能在攻击时攻击");
    }
    else if (State == Rise)
    {
    Console.WriteLine("不能在跳跃中攻击");
    }
    else if (State == Fall)
    {
    Console.WriteLine("不能在下落中攻击");
    }
    }

    /// <summary>
    /// 动作:攻击动作完成
    /// </summary>
    public void AttackActionComplete()
    {
    if (State == Idle)
    {
    Console.WriteLine("当前不处于攻击中");
    }
    else if (State == Run)
    {
    Console.WriteLine("当前不处于攻击中");
    }
    else if (State == Attack)
    {
    Console.WriteLine("完成Idle动画");
    State = Idle;
    }
    else if (State == Rise)
    {
    Console.WriteLine("当前不处于攻击中");
    }
    else if (State == Fall)
    {
    Console.WriteLine("当前不处于攻击中");
    }
    }

    ......

  4. 通过调用对应的动作就完成状态的转换了

    1
    stateMachine.InputAttackCommand();
    但是, 如果需要加入一个新的状态Walk, 则首先需要加入一个状态值
    1
    public const int Walk = 5;

然后需要对所有的动作都需要加入一个新的状态的判断

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
55
56
57
58
59
60
61
62
63
64
65
/// <summary>
/// 动作:输入攻击指令
/// </summary>
public void InputAttackCommand()
{
if (State == Idle)
{
Console.WriteLine("播放Attack动画");
State = Attack;
}
else if (State == Run)
{
Console.WriteLine("不能在奔跑时攻击");
}
else if (State == Attack)
{
Console.WriteLine("不能在攻击时攻击");
}
else if (State == Rise)
{
Console.WriteLine("不能在跳跃中攻击");
}
else if (State == Fall)
{
Console.WriteLine("不能在下落中攻击");
}
else if (State == Walk)
{
//行走的处理
}
}

/// <summary>
/// 动作:攻击动作完成
/// </summary>
public void AttackActionComplete()
{
if (State == Idle)
{
Console.WriteLine("当前不处于攻击中");
}
else if (State == Run)
{
Console.WriteLine("当前不处于攻击中");
}
else if (State == Attack)
{
Console.WriteLine("完成Idle动画");
State = Idle;
}
else if (State == Rise)
{
Console.WriteLine("当前不处于攻击中");
}
else if (State == Fall)
{
Console.WriteLine("当前不处于攻击中");
}
else if (State == Walk)
{
//行走的处理
}
}

......

很明显这并没有遵守开放-关闭原则, 也不符合面向对象的设计.

改进

因此, 需要改进代码: 1. 定义一个IState接口, 在这个接口内间每个动作都有一个对应的方法. 2. 为每个状态实现一个状态类. 这些类将负责在对应状态下进行的行为. 3. 将状态对应的动作逻辑写入这些方法中.

类图

改进后的设计, 每个状态只需要专注自己的动作, 并且增加状态和动作不会影响到之前的代码, 这符合对扩展开放,对修改关闭.

修改StateMachine类

不再使用整数代表状态, 而是改为状态对象.

  • 原来的代码
    1
    2
    3
    4
    5
    6
    7
    public const int Idle = 0;
    public const int Run = 1;
    public const int Attack = 2;
    public const int Rise = 3;
    public const int Fall = 4;

    public int State = Idle;
  • 修改后的代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public IState IdleState { get; private set; }
    public IState RunState { get; private set; }
    public IState AttackState { get; private set; }
    public IState RiseState { get; private set; }
    public IState FallState { get; private set; }

    public IState State;

    public StateMachine()
    {
    this.IdleState = new IdleState(this);
    this.RunState = new RunState(this);
    this.AttackState = new AttackState(this);
    this.RiseState = new RiseState(this);
    this.FallState = new FallState(this);

    this.State = this.IdleState;
    }

定义

状态模式, 允许对象在内部状态改变时改变它的行为, 对象看起来好像修改了它的类.

构成

Context: 维护对当前状态的引用, 并在状态发生改变的时候切换到新的状态.

State: 定义一个IState接口或者State抽象类, 其中包含状态中的所有行为. 创建特定State类来实现IState接口或继承State抽象类, 在特定State类中实现这些行为(handle).

相关源码