FSM有限状态机
一个角色 简单分为 待机 移动 攻击 受击等 状态
每个状态都需要更新 和 改变,当前状态中 每一帧 都需要更新位置、检测玩家的案件等这些信息
还有一个人机交互,比如 监听 玩家是否对其发送了 指令(攻击 受击),如果监听发生,需要从一个状态切换到另一个状态
StartGame、MainScene、GameScene、GameOverScene等,也可以使用有限状态机实现
每个状态 包含
- 初始化状态(给角色添加状态对象时出发,数据准备) Awake
- 进入状态(播放动画 获取数据等)Start
- 执行状态(数据更新 人机交互等)Update
- 结束状态(对结束的逻辑进行处理,数据释放等)Destroy
应用场景
- 游戏敌人AI
- 需要根据不同状态进行改变,有不同行为。
- 如果某个类需要根据成员变量的当前值改变自身行为,需要大量的判断条件时,可以使用状态模式。
优缺点
优点
- 将状态分离,单一职责原则
- 简化条件的判断
缺点
- 实现过于繁琐
- 类数量过多,容易造成系统复杂
代码实现
Manager类
public class PlayerStateManager : MonoBehaviour
{
private Dictionary<Type, PlayerStateBase> _states = new Dictionary<Type, PlayerStateBase>();
private PlayerStateBase _currentState;
private void Awake()
{
AddState<PlayerStateIdle>();
AddState<PlayerStateRun> ();
AddState<PlayerStateAttack1>();
AddState<PlayerStateAttack2>();
AddState<PlayerStateAttack3>();
AddState<PlayerStateJump>();
}
private void Start()
{
ChangeState<PlayerStateIdle>();
}
/// <summary>
/// 将指定状态注册到字典中
/// </summary>
/// <typeparam name="T"></typeparam>
private void AddState<T>() where T : PlayerStateBase
{
PlayerStateBase state = gameObject.AddComponent<T>();
state.OnInit();
_states.Add(typeof(T), state);
}
/// <summary>
/// 切换状态
/// </summary>
public void ChangeState<T>() where T : PlayerStateBase
{
if (_currentState != null)
{
_currentState.OnExit();
}
_currentState = _states[typeof(T)];
_currentState.OnEnter();
}
private void Update()
{
if (_currentState != null)
{
_currentState.OnExcute();
}
}
}
StateBase类
public class PlayerStateBase : MonoBehaviour
{
// 动画切换
protected Animator Ani;
protected string AniName;
protected PlayerStateManager Manager;
protected Rigidbody Rig;
public virtual void OnInit()
{
Ani = GetComponent<Animator>();
Manager = GetComponent<PlayerStateManager>();
Rig = GetComponent<Rigidbody>();
}
public virtual void OnEnter()
{
}
public virtual void OnExcute()
{
}
public virtual void OnExit()
{
}
}
普通类
public class PlayerStateIdle : PlayerStateBase
{
public override void OnInit()
{
base.OnInit();
AniName = "Idle";
}
public override void OnEnter()
{
Ani.SetInteger("state", 0);
}
public override void OnExcute()
{
if (Input.GetMouseButtonDown(0))
{
Manager.ChangeState<PlayerStateAttack1>();
return;
}
if (Input.GetKeyDown(KeyCode.Space))
{
Manager.ChangeState<PlayerStateJump>();
return;
}
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
Vector3 dir = new Vector3(h, 0, v);
if (dir != Vector3.zero)
{
Manager.ChangeState<PlayerStateRun>();
}
}
}