我正在视频游戏开发的背景下实现State模式。例如,玩家有多种状态:
Idle
、Run
、Attack
。每个状态都在自己的类中实现。
重要的是,每个状态负责转换到另一个状态(例如,Idle
状态可以转换到Attack
,但是Jump
状态无法转换到Idle
,直到玩家接触地面)。请参阅下面我如何实现它的高级想法:
class Player
{
private State _state = null;
public Player()
{
this.TransitionTo(new IdleState(this));
}
public void TransitionTo(State state)
{
this._state = state;
this._state.SetPlayer(this);
}
public void Jump()
{
this._state.jump();
}
}
abstract class State
{
protected Player _player;
public void SetPlayer(Player player)
{
this._player = player;
}
public abstract void Jump();
}
class JumpState : State
{
public override void Jump()
{
// double jump is not allowed
}
}
class IdleState : State
{
public override void Jump()
{
this._player.TransitionTo(new JumpState());
}
}
这很有效,但我可以看到这种方法有两个很大的局限性:
override
状态就可以做到,但是在我的实现中,一个状态负责转换到另一个状态,并且它通过引用另一个状态的具体实现来实现这一点。例如。在上面的代码片段中,请注意 Jump
如何转换为特定的 IdleState
。如果存在 JumpState
,我还必须创建一个使用它的 JumpNoDamageState
。状态与特定角色紧密耦合。例如,假设一个敌人的行为与另一个敌人完全相同,只不过该敌人也可以 IdleNoDamageState
Escape
、< 10% after taking damage. To do this, I need to recreate all Idle
和 Attack
状态来解释这一点),因为每个单独的状态都需要意识到这一点这种新状态以及过渡到它的可能性。
(FSM)的面向对象配方。如果你想引入新的状态,你就需要改变 FSM 的定义。如果你想区分不同的 FSM,你可能必须实现不同的 State-based API。 另一个问题可能是
Jump
对象保持状态的结果。也许令人惊讶的是,
“状态对象通常是单例”Context-
设计模式您通常会通过将所谓的
对象传递给每个 State 对象来设计 State 实现。在这种特殊情况下,您应该考虑更改 API,使其看起来像这样:
State
您经常需要维护单独对象的状态,以便不同的对象在各自的路径中通过 FSM 进行转换。您将状态信息保存在Context
对象(此处为 class Player
{
private State _state;
public Player()
{
_state = new IdleState(this));
}
internal void TransitionTo(State state)
{
_state = state;
}
public void Jump()
{
_state.jump(this);
}
}
abstract class State
{
public abstract void Jump(Player player);
}
class JumpState : State
{
// Singleton
public static readonly State Instance = new JumpState();
private JumpState() {}
public override void Jump(Player player)
{
// double jump is not allowed
}
}
class IdleState : State
{
// Singleton
public static readonly State Instance = new IdleState();
private IdleState() {}
public override void Jump(Player player)
{
player.TransitionTo(JumpState.Instance);
}
}
)上,以便不同的
Player
对象可以具有不同的状态,并以不同的方式通过相同的 FSM。您也可以使 Context对象具有多态性。
例如,假设一个敌人的行为与另一个敌人完全相同Context这听起来好像
确实可能需要比 Player
更通用 - 也许
Player
?
除了这个敌人如果健康的话也可以逃跑 Context< 10% after taking damage
您可以重复使用相同的 FSM,但让它检查
(Character
?) 是否可能尝试逃脱。这可以像在
Context对象上拥有布尔值一样简单。