Unity游戏框架设计之有限状态机
Unity游戏框架设计之有限状态机
简单介绍
如果满足一个物体拥有若干的状态,状态之间可根据状态转移条件进行转移,任意时刻物体只能处于一种状态三个条件,那么此物体在程序中就可以使用有限状态机(FiniteStateMachine)进行描述。
在游戏中,一个物体的 Animator 组件的动画将根据其当前的状态而确定。使用有限状态机后,就可以在 OnEnter()
中通过 Animator
类的 CrossFade()
方法直接设置当前物体的动画,实现 Animator 编辑器关于状态与动画的解耦,此时 Animator 编辑器完全不必关心动画的过渡方向和过渡的条件。
当然有限状态机也是存在缺陷的,如果物体状态数量越多,则物体可能转移的方向和状态的条件就复杂。
代码设计
public class FiniteStateMachine
{
private int _currentStateType = -1;
private IState _currentState;
private readonly Dictionary<int, IState> _stateTable = new();
public void AddState(int stateType, IState state)
{
_stateTable.TryAdd(stateType, state)
}
public void ChangeState(int stateType, bool allowToCurrent = false)
{
if (!allowToCurrent && _currentStateType != -1 && _currentStateType == stateType)
{
return;
}
if (!_stateTable.TryGetValue(stateType, out IState state) || state is null)
{
return;
}
_currentState?.OnExit();
_currentStateType = stateType;
_currentState = state;
_currentState.OnEnter();
}
public void ChangeState<T>(int stateType, T param, bool allowToCurrent = false)
{
if (!allowToCurrent && _currentStateType != -1 && _currentStateType == stateType)
{
return;
}
if (!_stateTable.TryGetValue(stateType, out IState state) || state is null)
{
return;
}
_currentState?.OnExit();
_currentStateType = stateType;
_currentState = state;
_currentState.OnEnter(param);
}
public bool IsState(int stateType)
{
return _currentStateType == stateType;
}
public void OnUpdate()
{
_currentState.OnUpdate();
}
public void OnFixedUpdate()
{
_currentState.OnFixedUpdate();
}
public interface IState
{
void OnEnter();
void OnEnter<T>(T param);
void OnExit();
void OnUpdate();
void OnFixedUpdate();
}
public abstract class BaseParameter
{
}
public abstract class BaseState<TParam> : IState where TParam : BaseParameter
{
protected readonly FiniteStateMachine Fsm;
protected readonly TParam Parameter;
protected BaseState(FiniteStateMachine fsm, TParam parameter)
{
Fsm = fsm;
Parameter = parameter;
}
public virtual void OnEnter()
{
}
public virtual void OnEnter<TData>(TData param)
{
}
public virtual void OnExit()
{
}
public virtual void OnUpdate()
{
}
public virtual void OnFixedUpdate()
{
}
}
}
使用经验
(一)任何输入系统的事件回调函数,都必须在主脚本中绑定。因为输入事件可能导致状态立即转移。
(二)所有状态都可能使用的公有字段和方法,在主脚本中编写。
(三)公有字段的更新方法,也必须在主脚本中编写。
(四)无需在 Animator 编辑器中设置动画过渡以及转移条件。当发生状态转移时,在状态入口处 OnEnter()
通过 Animator
类的 CrossFade()
方法直接设置当前物体的动画即可。
(五)状态转移条件判断和状态转移过程的代码必须在 Update() 函数中编写,保证状态切换的实时性。
(六)不满足第五点条件的代码推荐在 FixedUpdate() 函数中编写,防止性能消耗过大。
(七)使用的有限状态机基本格式。
public class FiniteStateMachineDemo : MonoBehaviour
{
private FiniteStateMachine _fsm;
public DemoParameter parameter;
private void Update()
{
UpdateParameterState();
_fsm.OnUpdate();
}
private void FixedUpdate()
{
_fsm.OnFixedUpdate();
}
private void UpdateParameterState()
{
}
[Serializable]
public class DemoParameter : FiniteStateMachine.BaseParameter
{
}
public abstract class DemoState : FiniteStateMachine.BaseState<DemoParameter>
{
protected DemoState(FiniteStateMachine fsm, DemoParameter parameter) : base(fsm, parameter)
{
}
}
}
后记
由于个人能力有限,文中不免存在疏漏之处,恳求大家斧正,一起交流,共同进步。