FSM(Finite State Machine)有限状态机,广泛应用于状态类、流程类、步骤类程序的处理。
1. 定义状态接口、抽象状态类,状态包含的五个基本行为:
· OnInitialization 状态初始化事件
· OnEnter 状态进入事件
· OnStay 状态停留事件
· OnExit 状态退出事件
· OnTermination 状态终止事件
using System;
namespace SK.Framework
{
///
/// 状态接口
///
public interface IState
{
///
/// 状态名称
///
string Name { get; set; }
///
/// 状态初始化事件
///
void OnInitialization();
///
/// 状态进入事件
///
void OnEnter();
///
/// 状态停留事件(Update)
///
void OnStay();
///
/// 状态退出事件
///
void OnExit();
///
/// 状态终止事件
///
void OnTermination();
///
/// 状态切换条件
///
/// 切换条件
/// 目标状态名称
void SwitchWhen(Func predicate, string targetStateName);
}
}
using System;
namespace SK.Framework
{
///
/// 抽象状态类
///
public class State : IState
{
///
/// 状态名称
///
public string Name { get; set; }
///
/// 所属状态机
///
public StateMachine machine;
///
/// 状态初始化事件
///
public Action onInitialization;
///
/// 状态进入事件
///
public Action onEnter;
///
/// 状态停留事件
///
public Action onStay;
///
/// 状态退出事件
///
public Action onExit;
///
/// 状态终止事件
///
public Action onTermination;
///
/// 状态初始化事件
///
public virtual void OnInitialization()
{
onInitialization?.Invoke();
}
///
/// 状态进入事件
///
public virtual void OnEnter()
{
onEnter?.Invoke();
}
///
/// 状态停留事件
///
public virtual void OnStay()
{
onStay?.Invoke();
}
///
/// 状态退出事件
///
public virtual void OnExit()
{
onExit?.Invoke();
}
///
/// 状态终止事件
///
public virtual void OnTermination()
{
onTermination?.Invoke();
}
///
/// 设置状态切换条件
///
/// 切换条件
/// 目标状态名称
public void SwitchWhen(Func predicate, string targetStateName)
{
machine.SwitchWhen(predicate, Name, targetStateName);
}
}
}
SwitchWhen函数用于为该状态切换到其他指定状态添加切换条件,当条件满足时,状态机会自动切换到目标状态。
2. 定义状态机类,状态机包含的基本行为:
· Add 添加状态
· Remove 移除状态
· Switch 切换状态
using System;
using UnityEngine;
using System.Collections.Generic;
namespace SK.Framework
{
///
/// 状态机
///
public class StateMachine
{
//状态列表 存储状态机内所有状态
protected readonly List states = new List();
//状态切换条件列表
protected List conditions = new List();
///
/// 状态机名称
///
public string Name { get; set; }
///
/// 当前状态
///
public IState CurrentState { get; protected set; }
///
/// 添加状态
///
/// 状态
/// 添加成功返回true 否则返回false
public bool Add(IState state)
{
//判断是否已经存在
if (!states.Contains(state))
{
//判断是否存在同名状态
if (states.Find(m => m.Name == state.Name) == null)
{
//存储到列表
states.Add(state);
//执行状态初始化事件
state.OnInitialization();
return true;
}
}
return false;
}
///
/// 添加状态
///
/// 状态类型
/// 状态命名
/// 添加成功返回true 否则返回false
public bool Add(string stateName = null) where T : IState, new()
{
Type type = typeof(T);
T t = (T)Activator.CreateInstance(type);
t.Name = string.IsNullOrEmpty(stateName) ? type.Name : stateName;
return Add(t);
}
///
/// 移除状态
///
/// 状态
/// 移除成功返回true 否则返回false
public bool Remove(IState state)
{
//判断是否存在
if (states.Contains(state))
{
//如果要移除的状态为当前状态 首先执行当前状态退出事件
if (CurrentState == state)
{
CurrentState.OnExit();
CurrentState = null;
}
//执行状态终止事件
state.OnTermination();
return states.Remove(state);
}
return false;
}
///
/// 移除状态
///
/// 状态名称
/// 移除成功返回true 否则返回false
public bool Remove(string stateName)
{
var targetIndex = states.FindIndex(m => m.Name == stateName);
if (targetIndex != -1)
{
var targetState = states[targetIndex];
if (CurrentState == targetState)
{
CurrentState.OnExit();
CurrentState = null;
}
targetState.OnTermination();
return states.Remove(targetState);
}
return false;
}
///
/// 移除状态
///
/// 状态类型
/// 移除成返回true 否则返回false
public bool Remove() where T : IState
{
return Remove(typeof(T).Name);
}
///
/// 切换状态
///
/// 状态
/// 切换成功返回true 否则返回false
public bool Switch(IState state)
{
//如果当前状态已经是切换的目标状态 无需切换 返回false
if (CurrentState == state) return false;
//当前状态不为空则执行状态退出事件
CurrentState?.OnExit();
//判断切换的目标状态是否存在于列表中
if (!states.Contains(state)) return false;
//更新当前状态
CurrentState = state;
//更新后 当前状态不为空则执行状态进入事件
CurrentState?.OnEnter();
return true;
}
///
/// 切换状态
///
/// 状态名称
/// 切换成功返回true 否则返回false
public bool Switch(string stateName)
{
//根据状态名称在列表中查询
var targetState = states.Find(m => m.Name == stateName);
return Switch(targetState);
}
///
/// 切换状态
///
/// 状态类型
/// 切换成返回true 否则返回false
public bool Switch() where T : IState
{
return Switch(typeof(T).Name);
}
///
/// 切换至下一状态
///
public void Switch2Next()
{
if (states.Count != 0)
{
//如果当前状态不为空 则根据当前状态找到下一个状态
if (CurrentState != null)
{
int index = states.IndexOf(CurrentState);
//当前状态的索引值+1后若小于列表中的数量 则下一状态的索引为index+1
//否则表示当前状态已经是列表中的最后一个 下一状态则回到列表中的第一个状态 索引为0
index = index + 1 < states.Count ? index + 1 : 0;
IState targetState = states[index];
//首先执行当前状态的退出事件 再更新到下一状态
CurrentState.OnExit();
CurrentState = targetState;
}
//当前状态为空 则直接进入列表中的第一个状态
else
{
CurrentState = states[0];
}
//执行状态进入事件
CurrentState.OnEnter();
}
}
///
/// 切换至上一状态
///
public void Switch2Last()
{
if (states.Count != 0)
{
//如果当前状态不为空 则根据当前状态找到上一个状态
if (CurrentState != null)
{
int index = states.IndexOf(CurrentState);
//当前状态的索引值-1后若大等于0 则下一状态的索引为index-1
//否则表示当前状态是列表中的第一个 上一状态则回到列表中的最后一个状态
index = index - 1 >= 0 ? index - 1 : states.Count - 1;
IState targetState = states[index];
//首先执行当前状态的退出事件 再更新到上一状态
CurrentState.OnExit();
CurrentState = targetState;
}
//当前状态为空 则直接进入列表中的最后一个状态
else
{
CurrentState = states[states.Count - 1];
}
//执行状态进入事件
CurrentState.OnEnter();
}
}
///
/// 切换至空状态(退出当前状态)
///
public void Switch2Null()
{
if (CurrentState != null)
{
CurrentState.OnExit();
CurrentState = null;
}
}
///
/// 获取状态
///
/// 状态类型
/// 状态名称
/// 状态
public T GetState(string stateName) where T : IState
{
return (T)states.Find(m => m.Name == stateName);
}
///
/// 获取状态
///
/// 状态类型
/// 状态
public T GetState() where T : IState
{
return (T)states.Find(m => m.Name == typeof(T).Name);
}
///
/// 状态机刷新事件
///
public void OnUpdate()
{
//若当前状态不为空 执行状态停留事件
CurrentState?.OnStay();
//检测所有状态切换条件
for (int i = 0; i < conditions.Count; i++)
{
var condition = conditions[i];
//条件满足
if (condition.predicate.Invoke())
{
//源状态名称为空 表示从任意状态切换至目标状态
if (string.IsNullOrEmpty(condition.sourceStateName))
{
Switch(condition.targetStateName);
}
//源状态名称不为空 表示从指定状态切换至目标状态
else
{
//首先判断当前的状态是否为指定的状态
if (CurrentState.Name == condition.sourceStateName)
{
Switch(condition.targetStateName);
}
}
}
}
}
///
/// 状态机销毁事件
///
public void OnDestroy()
{
//执行状态机内所有状态的状态终止事件
for (int i = 0; i < states.Count; i++)
{
states[i].OnTermination();
}
}
///
/// 设置状态切换条件
///
/// 切换条件
/// 目标状态名称
/// 状态机
public StateMachine SwitchWhen(Func predicate, string targetStateName)
{
conditions.Add(new StateSwitchCondition(predicate, null, targetStateName));
return this;
}
///
/// 设置状态切换条件
///
/// 切换条件
/// 源状态名称
/// 目标状态名称
///
public StateMachine SwitchWhen(Func predicate, string sourceStateName, string targetStateName)
{
conditions.Add(new StateSwitchCondition(predicate, sourceStateName, targetStateName));
return this;
}
///
/// 构建状态
///
/// 状态类型
/// 状态名称
/// 状态构建器
public StateBuilder Build(string stateName = null) where T : State, new()
{
Type type = typeof(T);
T t = (T)Activator.CreateInstance(type);
t.Name = string.IsNullOrEmpty(stateName) ? type.Name : stateName;
if (states.Find(m => m.Name == t.Name) == null)
{
states.Add(t);
}
return new StateBuilder(t, this);
}
///
/// 创建状态机
///
/// 状态机名称
/// 状态机
public static StateMachine Create(string stateMachineName = null)
{
return FSMMaster.Instance.Create(stateMachineName);
}
///
/// 创建状态机
///
/// 状态机类型
/// 状态机名称
/// 状态机
public static T Create(string stateMachineName = null) where T : StateMachine, new()
{
return FSMMaster.Instance.Create(stateMachineName);
}
///
/// 销毁状态机
///
/// 状态机名称
/// 销毁成功返回true 否则返回false
public static bool Destroy(string stateMachineName)
{
return FSMMaster.Instance.Destroy(stateMachineName);
}
///
/// 销毁状态机
///
/// 状态机类型
/// 销毁成功返回true 否则返回false
public static bool Destroy() where T : StateMachine
{
return FSMMaster.Instance.Destroy(typeof(T).Name);
}
///
/// 获取状态机
///
/// 状态机名称
/// 状态机
public StateMachine Get(string stateMachineName)
{
return FSMMaster.Instance.GetMachine(stateMachineName);
}
///
/// 获取状态机
///
/// 状态机类型
/// 状态机名称
/// 状态机
public static T Get(string stateMachineName) where T : StateMachine
{
return FSMMaster.Instance.GetMachine(stateMachineName);
}
///
/// 获取状态机
///
/// 状态机类型
/// 状态机
public static T Get() where T : StateMachine
{
return FSMMaster.Instance.GetMachine(typeof(T).Name);
}
}
}
3. StateSwitchCondition类用于设置状态的切换条件,其包含的字段:
· predicate 切换条件
· sourceStateName 源状态名称
· targetStateName 目标状态名称
该类用于表示当条件predicate满足时,从状态sourceState切换到targetState目标状态
using System;
namespace SK.Framework
{
///
/// 状态切换条件
///
public class StateSwitchCondition
{
///
/// 条件
///
public readonly Func predicate;
///
/// 源状态名称
///
public readonly string sourceStateName;
///
/// 目标状态名称
///
public readonly string targetStateName;
///
/// 构造函数
///
/// 切换条件
/// 源状态名称
/// 目标状态名称
public StateSwitchCondition(Func predicate, string sourceStateName, string targetStateName)
{
this.predicate = predicate;
this.sourceStateName = sourceStateName;
this.targetStateName = targetStateName;
}
}
}
4. 定义管理类,其包含的基本行为:
· Create 创建状态机
· Destroy 销毁状态机
· Get 获取状态机
using System;
using UnityEngine;
using System.Collections.Generic;
namespace SK.Framework
{
///
/// 有限状态机管理器
///
public class FSMMaster : MonoBehaviour
{
#region NonPublic Variables
private static FSMMaster instance;
//状态机列表
private List machines;
#endregion
#region Public Properties
public static FSMMaster Instance
{
get
{
if (instance == null)
{
instance = new GameObject("[SKFramework.FSM]").AddComponent();
instance.machines = new List();
DontDestroyOnLoad(instance);
}
return instance;
}
}
#endregion
#region NonPublic Methods
private void Update()
{
for (int i = 0; i < machines.Count; i++)
{
//更新状态机
machines[i].OnUpdate();
}
}
private void OnDestroy()
{
instance = null;
}
#endregion
#region Public Methods
///
/// 创建状态机
///
/// 状态机类型
/// 状态机名称
/// 状态机
public T Create(string stateMachineName = null) where T : StateMachine, new()
{
Type type = typeof(T);
stateMachineName = string.IsNullOrEmpty(stateMachineName) ? type.Name : stateMachineName;
if (machines.Find(m => m.Name == stateMachineName) == null)
{
T machine = (T)Activator.CreateInstance(type);
machine.Name = stateMachineName;
machines.Add(machine);
return machine;
}
return default;
}
///
/// 销毁状态机
///
/// 状态机名称
/// 销毁成功返回true 否则返回false
public bool Destroy(string stateMachineName)
{
var targetMachine = machines.Find(m => m.Name == stateMachineName);
if (targetMachine != null)
{
targetMachine.OnDestroy();
machines.Remove(targetMachine);
return true;
}
return false;
}
///
/// 获取状态机
///
/// 状态机类型
/// 状态机名称
/// 状态机
public T GetMachine(string stateMachineName) where T : StateMachine
{
return (T)machines.Find(m => m.Name == stateMachineName);
}
#endregion
}
}
5. StateBuilder类用于构建状态,便于链式编程
using System;
namespace SK.Framework
{
///
/// 状态构建器
///
/// 状态类型
public class StateBuilder where T : State, new()
{
//构建的状态
private readonly T state;
//构建的状态所属的状态机
private readonly StateMachine stateMachine;
///
/// 构造函数
///
///
///
public StateBuilder(T state, StateMachine stateMachine)
{
this.state = state;
this.stateMachine = stateMachine;
}
///
/// 设置状态初始化事件
///
/// 状态初始化事件
/// 状态构建器
public StateBuilder OnInitialization(Action onInitialization)
{
state.onInitialization = () => onInitialization(state);
return this;
}
///
/// 设置状态进入事件
///
/// 状态进入事件
/// 状态构建器
public StateBuilder OnEnter(Action onEnter)
{
state.onEnter = () => onEnter(state);
return this;
}
///
/// 设置状态停留事件
///
/// 状态停留事件
/// 状态构建器
public StateBuilder OnStay(Action onStay)
{
state.onStay = () => onStay(state);
return this;
}
///
/// 设置状态退出事件
///
/// 状态退出事件
/// 状态构建器
public StateBuilder OnExit(Action onExit)
{
state.onExit = () => onExit(state);
return this;
}
///
/// 设置状态终止事件
///
/// 状态终止事件
/// 状态构建器
public StateBuilder OnTermination(Action onTermination)
{
state.onTermination = () => onTermination(state);
return this;
}
///
/// 设置状态切换条件
///
/// 切换条件
/// 目标状态名称
/// 状态构建器
public StateBuilder SwitchWhen(Func predicate, string targetStateName)
{
state.SwitchWhen(predicate, targetStateName);
return this;
}
///
/// 构建完成
///
/// 状态机
public StateMachine Complete()
{
state.OnInitialization();
return stateMachine;
}
}
}
6. Example 编码示例:
using UnityEngine;
using SK.Framework;
public class Foo : MonoBehaviour
{
public class TestState : State
{
public string stringValue;
}
private void Start()
{
//创建状态机
var machine = StateMachine.Create()
//构建状态一
.Build("状态一")
//设置状态一初始化事件
.OnInitialization(state => state.stringValue = "A")
//设置状态一进入事件
.OnEnter(state => Debug.Log("进入状态一"))
//设置状态一停留事件
.OnStay(state => Debug.Log("状态一"))
//设置状态一推出事件
.OnExit(state => Debug.Log("退出状态一"))
//设置状态一销毁事件
.OnTermination(state => state.stringValue = null)
//状态一构建完成
.Complete()
//构建状态二
.Build("状态二")
//设置状态二进入事件
.OnEnter(state => Debug.Log("进入状态二"))
//设置状态二停留事件
.OnStay(state => Debug.Log("状态二"))
//设置状态二退出事件
.OnExit((state => Debug.Log("退出状态二")))
//状态二构建完成
.Complete()
//构建状态三
.Build("状态三")
//设置状态三进入事件
.OnEnter(state => Debug.Log("进入状态三"))
//设置状态三停留事件
.OnStay(state => Debug.Log("状态三"))
//设置状态三退出事件
.OnExit((state => Debug.Log("退出状态三")))
//状态三构建完成
.Complete()
//添加状态切换条件 当按下快捷键1时 切换至状态一
.SwitchWhen(() => Input.GetKeyDown(KeyCode.Alpha1), "状态一")
//添加状态切换条件 当按下快捷键2时 切换至状态二
.SwitchWhen(() => Input.GetKeyDown(KeyCode.Alpha2), "状态二")
//添加状态切换条件 当按下快捷键3时 切换至状态三
.SwitchWhen(() => Input.GetKeyDown(KeyCode.Alpha3), "状态三")
//为状态一至状态二添加切换条件:若当前状态为状态一时 按下快捷键4 切换至状态二
.SwitchWhen(() => Input.GetKeyDown(KeyCode.Alpha4), "状态一", "状态二");
//切换到指定状态
machine.Switch("状态一");
//切换到下一状态
machine.Switch2Next();
//切换到上一状态
machine.Switch2Last();
}
}