一、何为“有限状态机”
1.FSM的名词解释
“有限状态机”(Finite State Machine,简称FSM),是一个较为宽泛的抽象系统概念,顾名思义,“有限”是指状态机中的各种状态都是已经提前预设好的,不存在跳脱出该系统预定状态以外的其他情况;而“状态机”则是对各个预设状态的“执行”以及“状态切换”。
2.FSM的简单理解
简单讲,有限状态机其实就相当于一张离散数学图论中的“强连通图”(图中的任一顶点的出度和入度都不为0),即从任一顶点(状态)出发,都能通过有向路径(判定条件)到达图中的任意其他顶点(状态),相对的,任意其他顶点(状态)也能通过有向路径到达该顶点(状态)。
总得来说,FSM是帮助将复杂的状态和行为分解为离散的单元模块,从而更好地理解与管理系统的行为。
二、FSM在unity动画系统的使用
1.为什么选择使用FSM来管理动画系统
相信不少unity小白,在刚接触Animator系统时,肯定多多少少都编过“蜘蛛网”
但笔者在此不是说完全否定了Animator动画连连看不行,而是不应该将大量的精力,或者说逻辑放在连连看上,首先这本身就超出了动画系统作为“view”层的负责权限,其次,维护起来也是相当的麻烦。
而FSM很好的解决了这个问题,将逻辑分放在各个状态中,让Animator专心负责“播放对应动画”的职责。
2.使用FSM的好处
-
化繁为简:单元化动画系统,将复杂的动作拆解成一个个独立单元,能专心处理当前正在编写状态的逻辑。
-
容易维护:能以较小的代价随时增删动作状态,假如项目在开发过程中突然需要增加/删掉跳跃动作,那么只需要进入到各个已经定义好的状态中将有关于“跳跃状态”的切换逻辑增加/删掉即可。
-
对合作项目友好:不论是新进人员还是临时外援,FSM系统都很好的约束了其他人对于项目动画系统的改动,如果需要增加/删除动作状态,那必须严格遵守项目已经定义好的FSM框架,这对于项目管理而言是高效率的。
3.简单FSM的实现
状态基类BaseState定义
namespace FSM
{
public abstract class BaseState
{
public StateEnum state;
protected BaseStateMachine stateMachine;
public BaseState(BaseStateMachine sm, StateEnum state_)
{
this.state = state_;
this.stateMachine = sm;
}
public virtual void Enter() { }
public virtual void UpdateLogic() { }
public virtual void FixUpdateLogic() { }
public virtual void UpdatePhysics() { }
public virtual void UpdateAnimatorMove() { }
public virtual void Exit() { }
}
}
状态机基类BaseStateMachine定义
using UnityEngine;
using System;
namespace FSM
{
public abstract class BaseStateMachine : MonoBehaviour
{
private BaseState currentState = null;
private void Start()
{
currentState = GetInitialState();
if (currentState != null)
currentState.Enter();
}
private void Update()
{
if (currentState != null)
currentState.UpdateLogic();
}
private void FixedUpdate()
{
if (currentState != null)
currentState.FixUpdateLogic();
}
private void LateUpdate()
{
if (currentState != null)
currentState.UpdatePhysics();
}
private void OnAnimatorMove()
{
if (currentState != null)
currentState.UpdateAnimatorMove();
}
public void ChangeState(BaseState targetState,bool exit = true)
{
if (currentState != targetState)
{
if (currentState != null && exit)
currentState.Exit();
currentState = targetState;
if (currentState != null)
currentState.Enter();
}
}
protected virtual BaseState GetInitialState()
{
return null;
}
}
}
4.简单FSM的使用技巧
①关于Unity声明周期函数的继承与执行关系
继承于BaseStateMachine的类并不需要再次定义Start,Update,OnAnimatorMove等Unity生命周期函数,
因为当一个类的继承链上存在mono类,则在父级定义过的生命周期函数是会自动调用的,
若再次定义,则执行的是子类中定义的生命周期函数,
故继承于BaseStateMachine类的实例化状态机脚本中是不能再次定义Start,Update等生命周期函数的。
②状态分层
为了避免逻辑重复而导致的代码冗余,在进行状态定义时应该事先对状态进行分析,判断是否存在其他状态与将要定义的状态存在逻辑重复,如果有重复则应该提取出来作为这些有相同逻辑状态的“基状态”。
例如idle,walk,run,sprint等状态均为在地表上,Y轴并没有移动,则应该抽象出“Grounded”基状态;
又例如jump,falling等应该抽象出“Air”基状态。
③同级状态之间的切换是否执行Exit()
在满足切换条件后执行ChangeState(BaseState targetState,bool exit = true)方法时,根据你定义的状态层级关系,判断是否真的需要执行exit方法。