在状态模式(State Pattern)中,类的行为是基于它的状态改变的。这种类型的设计模式属于行为型模式。
简单来说,就是一个对象会有很多种状态,每一种状态都会做不同地事情。
比如人,有走路、跑步、吃饭、睡觉等等状态,它们可以做的事都不一样。
更详细的介绍可以看看这个:状态模式
假设玩家有三种状态
我们可以把所有的状态用枚举来表示,新建一个脚本,叫做State,代码如下
public enum State
{
StandState,
JumpState,
CrouchState,
}
新建一个PlayerController脚本,目的是根据玩家输入修改当前状态。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
private State currentState;//当前状态
private void Awake()
{
//初始为站立状态
currentState = State.StandState;
}
private void Update()
{
//监听输入
HandleInput();
}
public void HandleInput()
{
switch (currentState)
{
//玩家在站立状态可以-->跳跃、下蹲
case State.StandState:
if (Input.GetKeyDown(KeyCode.UpArrow))
{
SetState(State.JumpState);
}
if (Input.GetKeyDown(KeyCode.DownArrow))
{
SetState(State.CrouchState);
}
break;
//玩家在跳跃状态可以-->站立
case State.JumpState:
if (Input.GetKeyDown(KeyCode.DownArrow))
{
SetState(State.StandState);
}
break;
//玩家在下蹲状态可以-->站立
case State.CrouchState:
if (Input.GetKeyDown(KeyCode.UpArrow))
{
SetState(State.StandState);
}
break;
default:
break;
}
}
//修改当前状态
public void SetState(State targetState)
{
currentState = targetState;
}
}
我们运行游戏,按下对应的键,就可以修改当前状态了。
这样我们就实现了一个简单的状态模式。
哈哈开个玩笑,但是玩笑归玩笑,这种情况在现实生活中是很常见的。那假设我们就用的是上面的代码,现在我们来加需求。
最后代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum State
{
StandState,
JumpState,
CrouchState,
Swim,
Fly,
Dive,
}
public class PlayerController : MonoBehaviour
{
private State currentState;
private void Awake()
{
currentState = new StandState(this);
}
private void Update()
{
switch (state)
{
case State.StandState:
if (Input.GetKeyDown(KeyCode.UpArrow))
{
SetState(State.JumpState);
}
if (Input.GetKeyDown(KeyCode.DownArrow))
{
SetState(State.CrouchState);
}
break;
case State.JumpState:
if (Input.GetKeyDown(KeyCode.DownArrow))
{
SetState(State.StandState);
}
break;
case State.CrouchState:
if (Input.GetKeyDown(KeyCode.UpArrow))
{
SetState(State.StandState);
}
break;
case State.Swim:
if (Input.GetKeyDown(KeyCode.DownArrow))
{
SetState(State.Dive);
}
break;
case State.Dive:
if (Input.GetKeyDown(KeyCode.UpArrow))
{
SetState(State.Swim);
}
break;
case State.Fly:
if (Input.GetKeyDown(KeyCode.DownArrow))
{
if (下方是河)
{
SetState(State.Swim);
}
if (下方是陆地)
{
SetState(State.StandState);
}
}
break;
default:
break;
}
}
public void SetState(State targetState)
{
currentState = targetState;
}
}
你会发现,会导致很多问题
我们可不可以这样呢,把每一个状态都设置为一个类,让每一个状态都做自己的事,状态自己决定可以切换到什么状态,而不让PlayerController去控制。
这样我们就解决了上述问题了,也就是这篇文章的重点。
有限状态机,(英语:Finite-state machine, FSM),又称有限状态自动机,简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。
本次实现的是较为简单的FSM,实际上常用的FSM比下面的要稍微复杂点,会多一个管理者,还有每一个状态的生命周期也会增加。
详细的FSM可以看看我的这篇博客:Unity学习笔记–FSM有限状态机的实现
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public interface IBaseState
{
//处理玩家输入
void HandleInput();
//处理状态的更新:比如动画、移动
void Update();
}
新建多个脚本文件。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class StandState : IBaseState
{
private PlayerController m_player;
public StandState(PlayerController player) => m_player = player;
public void HandleInput()
{
if (Input.GetKeyDown(KeyCode.UpArrow))
{
m_player.SetState(new JumpState(m_player));
}
if (Input.GetKeyDown(KeyCode.DownArrow))
{
m_player.SetState(new CrouchState(m_player));
}
}
public void Update()
{
//DoSomething
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class JumpState : IBaseState
{
private PlayerController m_player;
public JumpState(PlayerController player) => m_player = player;
public void HandleInput()
{
if (Input.GetKeyDown(KeyCode.DownArrow))
{
m_player.SetState(new StandState(m_player));
}
}
void IBaseState.Update()
{
//DoSomething
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CrouchState : IBaseState
{
private PlayerController m_player;
public CrouchState(PlayerController player) => m_player = player;
public void HandleInput()
{
if (Input.GetKeyDown(KeyCode.UpArrow))
{
m_player.SetState(new StandState(m_player));
}
}
public void Update()
{
//DoSomething
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
private IBaseState currentState;
private void Awake()
{
currentState = new StandState(this);
}
private void Update()
{
currentState?.HandleInput();
currentState?.Update();
}
public void SetState(IBaseState targetState)
{
currentState = targetState;
}
}
如果我们需要添加多个状态和转换条件,只需要新建一个状态脚本,让这个状态去实现IBaseState这个接口。然后修改状态的HandleInput方法就可以了。
这样代码耦合性就大大降低了,代码易读性也更强了,维护的时候也会很简单。
大家可以试着把之前问题引出:屑策划(bushi)的需求实现一下,来更直观的感受有限状态机的魅力。