状态设计模式
State Design Pattern
需求
制作一个游戏中玩家操控的角色的动画控制器
将以上状态变化用代码实现
需要定义一系列的状态
1
2
3
4
5public const int Idle = 0;
public const int Run = 1;
public const int Attack = 2;
public const int Rise = 3;
public const int Fall = 4;定义一个存储当前状态的变量
1
public int State = Idle;
根据当前的状态, 执行该动作对应的效果
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("当前不处于攻击中");
}
}
......通过调用对应的动作就完成状态的转换了
但是, 如果需要加入一个新的状态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
7public 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
18public 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).