State模式,允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。State模式的一个典型应用是TCP连接,GoF的《设计模式》上有TCP实现的一个简化版本,这里写一个游戏开发中常见的根据输入控制玩家运动的例子。
interface IPlayerState
{
void Enter(Player player); // enter state
IPlayerState HandleInput(Player player, KeyCode keyCode); // handle input event
IPlayerState Update(Player player); // update state
void Exit(); // destroy state
}
class PlayerIdleState : IPlayerState
{
public void Enter(Player player)
{
player.Idle();
}
public IPlayerState HandleInput(Player player, KeyCode keyCode)
{
//Debug.Log("PlayerIdleState HandleInput:" + keyCode.ToString());
if (keyCode == KeyCode.J)
return new PlayerJumpState();
else if (keyCode == KeyCode.R)
return new PlayerRunState();
return null;
}
public IPlayerState Update(Player player)
{
return null;
}
public void Exit()
{
}
}
class PlayerRunState : IPlayerState
{
const float RUN_TIME = 2.0f;
float runTime = 0.0f;
public void Enter(Player player)
{
player.Run();
}
public IPlayerState HandleInput(Player player, KeyCode keyCode)
{
//Debug.Log("PlayerRunState HandleInput:" + keyCode.ToString());
if (Input.GetKeyUp(KeyCode.R))
{
runTime = Time.realtimeSinceStartup;
return null;
}
if (keyCode == KeyCode.J)
return new PlayerJumpState();
else if (keyCode == KeyCode.I)
return new PlayerIdleState();
return null;
}
public IPlayerState Update(Player player)
{
if (runTime > 0.0f && Time.realtimeSinceStartup - runTime >= RUN_TIME)
{
runTime = 0.0f;
return new PlayerIdleState();
}
return null;
}
public void Exit()
{
}
}
class PlayerJumpState : IPlayerState
{
const float JUMP_TIME = 2.0f;
float jumpTime = 0.0f;
public void Enter(Player player)
{
jumpTime = Time.realtimeSinceStartup;
player.Jump();
}
public IPlayerState HandleInput(Player player, KeyCode keyCode)
{
//Debug.Log("PlayerJumpState HandleInput:" + keyCode.ToString());
if (keyCode == KeyCode.R)
return new PlayerRunState();
else if (keyCode == KeyCode.I)
return new PlayerIdleState();
return null;
}
public IPlayerState Update(Player player)
{
if (jumpTime > 0.0f && Time.realtimeSinceStartup - jumpTime >= JUMP_TIME)
{
jumpTime = 0.0f;
return new PlayerIdleState();
}
return null;
}
public void Exit()
{
}
}
class Player
{
private IPlayerState state;
private Animator animator;
private CharacterController controller;
private Vector3 movement;
public Player(Animator anim, CharacterController ctrl)
{
animator = anim;
controller = ctrl;
state = new PlayerIdleState();
movement = new Vector3(0.1f, 0, 0);
}
public void HandleInput(KeyCode keyCode)
{
IPlayerState newState = state.HandleInput(this, keyCode);
ChangeState(newState);
}
public void Update()
{
IPlayerState newState = state.Update(this);
ChangeState(newState);
}
private void ChangeState(IPlayerState newState)
{
if (newState != null)
{
state.Exit();
state = newState;
state.Enter(this);
}
}
public void Idle()
{
Debug.Log("Player Idle");
animator.Play("Idle");
}
public void Run()
{
Debug.Log("Player Run");
animator.Play("Run");
}
public void Jump()
{
Debug.Log("Player Jump");
animator.Play("Jump");
}
}
public class StatePattern : MonoBehaviour
{
private Animator animator;
private CharacterController controller;
private Player player;
// Start is called before the first frame update
void Start()
{
animator = GetComponent<Animator>();
controller = GetComponent<CharacterController>();
player = new Player(animator, controller);
}
// Update is called once per frame
void Update()
{
if (Input.GetKey(KeyCode.I))
player.HandleInput(KeyCode.I);
else if (Input.GetKey(KeyCode.J))
player.HandleInput(KeyCode.J);
else if (Input.GetKey(KeyCode.R) || Input.GetKeyUp(KeyCode.R))
player.HandleInput(KeyCode.R);
player.Update();
}
}
PS. 我的设计模式系列blog,《设计模式》专栏,通过简单的示例演示设计模式,对于初学者很容易理解入门。深入学习请看GoF的《设计模式》。