有限状态机FSM(finite state machine) 二

延续上一篇继续有限状态机

上一篇中的状态切换判断是在每一个状态类的 OnExecute 方法中各种 if else 硬编码逻辑 当状态比较多、条件比较多的时候,就会显得非常的臃肿,且又不利于阅读以及扩展了。
那么我们能不能通过配置来省略各种 if else if else 硬编码呢 ? 答案:有的。
如上图每一条状态转换的连线,我们称之为转换 Transition

休息状态 Transition 吃饭状态
休息状态 Transition 打篮球
休息状态 Transition 写作业
那也就是 休息有三个 Transition,每一个 Transition 上有 0 到多个条件
上图中转换条件是抽象的,下面将各种条件转换为具体的可判断的值
如下
设置属性饥饿感值 _senseHunger 取值范围 [0, 10]_senseHunger > 8 表示 饥饿
设置属性体力值 _energy 取值范围 [0, 10]_energy < 1 表示 累了
设置属性作业量 _homeWorkCount 取值范围 [0, 10]_homeWorkCount < 1 表示作业做完了
设置属性写作业强迫性 _needHomeWork 取值范围 [0, 10]_needHomeWork > 8 表示该写作业了
设置属性打篮球渴望值 _wantBasketball 取值范围 [0, 10]_wantBasketball > 8 表示想打篮球了

每个属性有一个初始值,在不同状态对某些值做不同增量(正、负),根据判断不同属性的值来判断每个 Transition是否成立
下面是各种状态之间转换的 Transition 条件

将配置转换为代码逻辑条件
上图中可以看出,
休息转换到吃饭条件:
Hunger(饥饿感 float值 ) > 8
Energy (精力 float 值) > 9.5
我们将每个属性的值视为角色的环境变量
建立一个条件参数类,包含参数名、参数类型、默认值、比较值

// 条件参数
public class Parameter
{// 参数名字public string _parameterName;// 参数类型public ParameterType _parameterType;// 参数比较类型public ParameterCompare _compare;// int 参考值public int intValue;// float 参考值public float floatValue;// bool 参考值public bool boolValue;// string参考值public string stringValue;/*Parameter parameter = new Parameter();// 参数名为 Ageparameter._parameterName = "Age";// 参数类型为 int 类型parameter._parameterType = ParameterType.INT;// 参数 int参考值是 5parameter.intValue = 5;// 参数比较类型是 大于等于parameter._compare = ParameterCompare.GREATER_EQUALS;// 参数意思就是,我要和一个属性字段为Age的int类型值做比较,如果Age >= 5 则返回true*/
}

参数类型

// 参数类型
public enum ParameterType
{Int = 1,    // int 类型Float = 2,  // float 类型Bool = 3,   // bool 类型String = 4, // 字符串类型
}

比较类型

// 参数比较类型
public enum ParameterCompare
{/// <summary>/// 无效值/// </summary>INVALID = 0,/// <summary>/// 大于/// </summary>GREATER = 1 << 0,/// <summary>/// 小于/// </summary>LESS = 1 << 1,/// <summary>/// 等于/// </summary>EQUALS = 1 << 2,/// <summary>/// 不等于/// </summary>NOT_EQUAL = 1 << 3,/// <summary>/// 大于等于/// </summary>GREATER_EQUALS = 1 << 4,/// <summary>/// 小于等于/// </summary>LESS_EQUAL = 1 << 5,
}

然后就是 Transition
可以包含多个参数则加一个参数列表List<Parameter> _parameterList
要知道将要跳转的状态 StateEnum ToState
有时候可能一个特殊逻辑,不太好通过几个参数来配置转换条件,需要加一个函数,则给 Transition 添加一个判断能否转换的函数 bool CanTransition()

/// <summary>
/// 状态装换条件
/// </summary>
public class Transition
{// 切换状态事件private TransitionCallback _transitionCallback;private List<Parameter> _parameterList = new List<Parameter>();public Transition(StateEnum toState, TransitionCallback transitionCallback){ToState = toState;_transitionCallback = transitionCallback;}public Transition(StateEnum toState, List<Parameter> parameterList){ToState = toState;_parameterList = parameterList;}public StateEnum ToState { get; set; }public List<Parameter> ParameterList{get { return _parameterList; }}public bool CanTransition(){if (null == _transitionCallback){return false;}return _transitionCallback();}
}

Transition 属于每个状态的,所以在 StateBase 中加入 List<Transition> _transitionList = new List<Transition>();来存储每个状态的转换
然后我们将 Excel 配置表导出为.csv文件,放入项目中,读取 .csv 配置将 每一行的转换实例化为一个 Transition,添加到所在的状态类中,这个过程放在 StateMachine 中处理了

角色的每个属性的值转换为 Parameter(角色的环境变量)存储在 StateMachine
每次执行 StateMachine.OnExecute 方法后,判断当前状态的 List<Transition> _transitionList 是否有满足转换条件的,如果有则转换状态。
Transition中的 Parameter则需要跟 StateMachine中存储的环境变量Parameter做比较
StateMachine 类修改如下

using System.Collections.Generic;
using UnityEngine;public enum StateEnum
{EAT = 0,        // 吃饭RESET = 1,      // 休息BASKETBALL = 2, // 打篮球HOMEWORK = 3,   // 写作业
}public class StateMachine
{// 保存所有的状态private Dictionary<StateEnum, StateBase> _stateDic = new Dictionary<StateEnum, StateBase>();// 记录当前状态private StateBase _currentState;// 环境变量private Dictionary<string, Parameter> _parameterDic = new Dictionary<string, Parameter>();public StateMachine(){// 初始化状态、并存储_stateDic[StateEnum.EAT] = new StateEat();_stateDic[StateEnum.RESET] = new StateReset();_stateDic[StateEnum.BASKETBALL] = new StateBasketball();_stateDic[StateEnum.HOMEWORK] = new StateHomeWork();}public Dictionary<StateEnum, StateBase> StateDic{get { return _stateDic; }}// 获取当前状态public StateBase CurrentState{get { return _currentState; }private set { _currentState = value; }}// 状态转换方法public void TransitionState(StateEnum stateEnum){// 如果当前状态不为空,先退出当前状态if (null != CurrentState){CurrentState.OnExit();}// 令当前状态等于转换的新状态CurrentState = _stateDic[stateEnum];// 转换的新状态执行 进入方法CurrentState.OnEnter();CurrentState.OnExecute();}// 每帧执行的方法public void OnExecute(){if (null != CurrentState){CurrentState.OnExecute();}// 判断CurrentState所有转换条件for (int i = 0; i < CurrentState.TransitionList.Count; ++i){Transition transition = CurrentState.TransitionList[i];// 如果转换条件为 true,则转换状态if (transition.CanTransition() || CompareTransition(transition)){TransitionState(transition.ToState);break;}}}// 判断 Transition 是否满足private bool CompareTransition(Transition transition){if (transition.ParameterList.Count <= 0){return false;}for (int i = 0; i < transition.ParameterList.Count; ++i){Parameter parameter = transition.ParameterList[i];Parameter environment = null;if (!_parameterDic.TryGetValue(parameter._parameterName, out environment)|| !ConditionCompare.CompareParameter(environment, parameter)){return false;}}return true;}// 更新环境变量的值public void UpdateParameter(string parameterName, int intValue){Parameter parameter = GetParameter(parameterName);parameter._parameterType = ParameterType.Int;parameter.intValue = intValue;}// 更新环境变量的值public void UpdateParameter(string parameterName, float floatValue){Parameter parameter = GetParameter(parameterName);parameter._parameterType = ParameterType.Float;parameter.floatValue = floatValue;}// 更新环境变量的值public void UpdateParameter(string parameterName, bool boolValue){Parameter parameter = GetParameter(parameterName);parameter._parameterType = ParameterType.Bool;parameter.boolValue = boolValue;}// 更新环境变量的值public void UpdateParameter(string parameterName, string stringValue){Parameter parameter = GetParameter(parameterName);parameter._parameterType = ParameterType.String;parameter.stringValue = stringValue;}// 获取环境变量private Parameter GetParameter(string parameterName){Parameter parameter = null;if (!_parameterDic.TryGetValue(parameterName, out parameter)){parameter = new Parameter();_parameterDic[parameterName] = parameter;}return parameter;}#region Config/// <summary>/// 配置表,对于不同的角色可以有不同的配置表,灵活多变/// </summary>/// <param name="fileName">配置文件名</param>public void SetConfigFile(string fileName){TableRead.Instance.Init();TableRead.Instance.ReadCustomPath(Application.streamingAssetsPath);List<int> list = TableRead.Instance.GetKeyList(fileName);foreach (var key in list){AnalysisTransition(fileName, key);}}// 解析每一行的 Transitionprivate void AnalysisTransition(string fileName, int key){int currentState = int.Parse(TableRead.Instance.GetData(fileName, key, "CurrentState"));int toState = int.Parse(TableRead.Instance.GetData(fileName, key, "ToState"));List<Parameter> parameterList = new List<Parameter>();for (int i = 1; i <= 2; ++i){string name = TableRead.Instance.GetData(fileName, key, string.Format("Name{0}", i));if (string.IsNullOrEmpty(name)){continue;}Parameter parameter = new Parameter();parameter._parameterName = name;parameter._parameterType = (ParameterType)(int.Parse(TableRead.Instance.GetData(fileName, key, string.Format("Type{0}", i))));string value = TableRead.Instance.GetData(fileName, key, string.Format("Value{0}", i));// 根据类型将 value 解析为 int/float/bool/string if (parameter._parameterType == ParameterType.Int){parameter.intValue = int.Parse(value);}else if (parameter._parameterType == ParameterType.Float){parameter.floatValue = float.Parse(value);}else if (parameter._parameterType == ParameterType.Bool){parameter.boolValue = bool.Parse(value);}else if (parameter._parameterType == ParameterType.String){parameter.stringValue = value;}parameter._compare = (ParameterCompare)(int.Parse(TableRead.Instance.GetData(fileName, key, string.Format("Compare{0}", i))));//将每个参数添加到集合中parameterList.Add(parameter);}// 实例化 TransitionTransition transition = new Transition((StateEnum)toState, parameterList);// 根据配置 currentState 获取状态StateBase stateBase = _stateDic[(StateEnum)currentState];// 将Transition 添加到状态中stateBase.TransitionList.Add(transition);}#endregion
}

StateReset、StateBasketball、StateEat、StateHomeWork 的 OnExecute 中的 if else 状态切换逻辑删除掉

代码搜然不多,全部粘贴到这里显得臃肿,可读性查,代码下载地址,是一个Unity项目,如果只看代码在目录 FiniteStateMachine\Assets\Scripts 下,代码逻辑是跟引擎无关的,如有问题请评论留言,谢谢

有限状态机FSM(finite state machine) 二相关推荐

  1. 有限状态机FSM(finite state machine) 一

    有限状态机FSM(finite state machine) 一 有限状态机又称有限自动状态机,它拥有有限数量的状态,每个状态代表不同的意义,每个状态可以切换到 零-多 个状态.任意时刻状态机有且只能 ...

  2. 有限状态机FSM(Finite State Machine)及实现方式介绍(转)

    原文:https://www.cnblogs.com/barrywxx/p/12860573.html 一.为什么引入有限状态机? 最近做一个项目,项目中很多实体(Entity),每个实体都有很多状态 ...

  3. 有限状态机 FSM——Finite State Machine

    有限状态机 1.状态机的结构 2.Mealy状态机和Moore状态机 3.用Verilog来描述可综合的状态机 实例 序列检测器 ADC采样控制电路 按键消抖 1.状态机的结构 其中F和G是两个有关状 ...

  4. 计算机态,(计算机)有限态自动机,FSM(finite state machine),音标,读音,翻译,英文例句,英语词典...

    补充资料:ω-有限自动机 ω-有限自动机 ω-finite state automata 1094·.一youx一anz}dongJ-..有限自动机(.一rinite state automata)一 ...

  5. 【决策状态机FSM(finite state machine)梳理】

    背景:写论文需要,对需要有限状态机部分进行整理.论文内容是关于自动驾驶避撞决策. 参考Junior: The Stanford Entry in the Urban Challenge的有限状态机 1 ...

  6. FSM(Finite State Machine,有限状态机)设计

    有限状态机(Finite State Machine, FSM),根据状态机的输出是否与输入有关,可分为Moore型状态机和Mealy型状态机.Moore型状态机输出仅仅与现态有关和Mealy型状态机 ...

  7. 《计算机组成与CPU设计实验》5有限状态机的Verilog HDL描述(Finite State Machine,FSM)

    多数控制逻辑都可以用有限状态机描述 状态机 状态机是组合逻辑和时序逻辑的特殊组合 时序逻辑用于存储状态 组合逻辑用于产生次态和产生输出 状态的数量是有限的,故称为有限状态机(Finite State ...

  8. 证明与计算(7): 有限状态机(Finite State Machine)

    什么是有限状态机(Finite State Machine)? 什么是确定性有限状态机(deterministic finite automaton, DFA )? 什么是非确定性有限状态机(nond ...

  9. 【FPGA入门教程】(七)FSM(Finite State Machine,有限状态机)设计

    FPGA设计中,最重要的设计思想就是状态机的设计思想!状态机的本质就是对具有逻辑顺序和时序规律的事件的一种描述方法,它有三个要素:状态.输入.输出:状态也叫做状态变量(比如可以用电机的不同转速作为状态 ...

最新文章

  1. 我的新浪微博,顺便总结下近况
  2. windows中安装Node.js-安装angular-cli
  3. 20165331 第六周学习总结
  4. ma应用、超级短线、分钟短线买卖和看盘心得
  5. 我被“非结构化数据包围了”,请求支援!
  6. 计算机设计大赛国奖作品_5. 模拟退火求解旅行商问题
  7. echart x轴 倾斜_「技术帖」轮毂轴承单元轴铆合装配的铆头优化设计
  8. 哪个行业既锻炼人又便于今后创业?
  9. [转载] python中numpy模块的around方法_更好地舍入Python的NumPy.around:舍入numpy的数组
  10. 修改mysql端口以及出现的问题
  11. 过来康康,一起来学VScode插件
  12. 服务器协议密码,Radius协议 - 如何将密码发送到服务器?
  13. [ps视频教程][ps6 完全自学教程][iso/pdf/2.7G]
  14. Python 自带slic代码分析
  15. C++的35个技巧阅读笔记(五)
  16. 轻型异常在线检测器LODA:Lightweight on-line detector of anomalies
  17. python实现scp功能_如何在Python中进行scp?
  18. 解决:Linux nohup命令不再默认输出日志文件
  19. iOS release版本
  20. 游戏AI行为选择算法一览

热门文章

  1. 离散数学:n元素上的各种关系数目推导
  2. linux下查看磁盘空间
  3. 图解机器学习算法(6) | 决策树模型详解(机器学习通关指南·完结)
  4. From Shadow Generation to Shadow Removal (CVPR2021)阅读笔记
  5. 如何去掉Qt布局(Layout)内控件之间的空隙
  6. 【AI基础】图解手算BatchNorm、LayerNorm和GroupNorm
  7. Android 网络框架之Retrofit源码解析,flutter边框特效
  8. [经验教程]2022京东618红包活动时间是什么时候开始什么时候结束及怎么领取京东618红包?
  9. 大数据可视化的方法、挑战及进展
  10. 紫薇圣人的程序员人生[原创IT小说]