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

有限状态机又称有限自动状态机,它拥有有限数量的状态,每个状态代表不同的意义,每个状态可以切换到 零-多 个状态。任意时刻状态机有且只能处在一个状态。
有限状态机可以表示为一个有向图。
如下图

从图中可以看出一个学生包含四个状态:吃饭、休息、打篮球、写作业
每种带有箭头的连线,表示可以从当前状态切换到其他的状态,以及切换的条件

  吃饭 休息 打篮球 写作业
吃饭   吃饱了    
休息 饿了   想打球 该写作业
打篮球 饿了 累了   该写作业
写作业   累了 作业写完了  

表格中左侧第一列为当前状态
表格中上方第一行为切换的下一个状态
表格中每行从左到右为状态切换的条件(状态A不能切换到状态A)
如下
吃饭->休息:条件 (吃饱了)
休息->吃饭:条件 (饿了)
休息->打篮球:条件 (想打球)
休息->写作业:条件(该写作业)

几个重要概念
状态(State):当前所处的状态,在当前状态下可以有不同的行为和属性
转移(Transition):状态变更,满足条件是从一个状态转移到另一个状态
动作(Action):表示在给定时刻进行的活动
事件/条件(Event、Condition):触发一个事件、当一个条件满足触发状态转移切换到另一个状态

当状态很少时,可以使用 if else 各种嵌套判断来实现逻辑但是当状态不断增加时,代码的可读性以及可拓展性将会是非常严峻的问题,并且当状态不断增加时,常常需要修改之前的各种判断条件,随着状态增加,代码复杂度将难以预测,bug率将不断上升,最终可能导致代码不可读、无法改。

那么状态机是如何实现如上图几种状态之间的逻辑
首先我们需要定义各种状态
每个状态需要三个接口

    // 进入该状态
void OnEnter(); // 执行该状态的行为
void OnExecute(); // 退出该状态
void OnExit();

1.当切换到状态 A 时先执行 A.OnEnter 方法,说明开始执行状态A 了,可以在 OnEnter 方法里做一些初始化,
2.然后接下来每帧将会调用 A.OnExecute 方法,不断执行在状态 A下的逻辑
3.当从状态A切换到其他状态时,要先执行 A.OnExit 方法,表名要退出状态A了,在这里处理一些状态 A 的收尾工作。比如从写作业 转换到打篮球,在写作业.OnExit() 中:将作业本放入书包,铅笔收入文具盒等等

我们可以定义(interface)接口或者(abstract class)抽象类,这里我采用定义一个抽象类基类

public abstract class StateBase
{
// 当前类型
protected StateEnum _state;
// 状态转换事件,要转换状态的通知
protected Action<StateEnum> _transitionEvent;
public StateBase() { } // 进入该状态
public abstract void OnEnter(); // 执行该状态的行为
public abstract void OnExecute(); // 退出该状态
public abstract void OnExit(); //返回当前类型
public StateEnum State
{
get { return _state; }
} public void SetTransitionEvent(Action<StateEnum> transitionEvent)
{
_transitionEvent = transitionEvent;
}
}

定义一个区分不同状态的枚举

public enum StateEnum
{
EAT = 0, // 吃饭 RESET = 1, // 休息 BASKETBALL = 2, // 休息 HOMEWORK = 3, // 写作业
}

我们还需要一个状态管理类 StateMachine,需要使用的方法如下
1.保存我们所有的状态
2.转换状态的接口
3.获取当前状态的接口
4.执行当前状态的接口

public class StateMachine
{
// 保存所有的状态
private Dictionary<StateEnum, StateBase> _stateDic = new Dictionary<StateEnum, StateBase>();
// 记录当前状态
private StateBase _currentState; public StateMachine()
{
// 初始化状态、并存储
_stateDic[StateEnum.EAT] = new StateEat();
_stateDic[StateEnum.RESET] = new StateReset();
_stateDic[StateEnum.BASKETBALL] = new StateBasketball();
_stateDic[StateEnum.HOMEWORK] = new StateHomeWork(); foreach(var kv in _stateDic)
{
// 给所有状态设置状态转换的回调方法
kv.Value.SetTransitionEvent(TransitionState);
}
} // 获取当前状态
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();
} // 每帧执行的方法
public void OnExecute()
{
if (null != CurrentState)
{
CurrentState.OnExecute();
}
}
}

分别定义各个状态

吃饭状态

public class StateEat : StateBase
{
public StateEat()
{
_state = StateEnum.EAT;
} public override void OnEnter()
{
Debug.Log("开始吃饭啦");
} public override void OnExecute()
{
Debug.Log("吃饭中");
// 如果吃饱了,转换到休息状态
if(吃饱了)
{
_transitionEvent(StateEnum.RESET);
}
} public override void OnExit()
{
Debug.Log("吃的好饱啊,不吃了");
Debug.Log("刷碗、刷锅");
Debug.Log("擦桌子");
Debug.Log("打扫厨房");
}
}

休息状态

public class StateReset : StateBase
{
public StateReset()
{
_state = StateEnum.RESET;
} public override void OnEnter()
{
Debug.Log("我要开始休息了");
} public override void OnExecute()
{
// 如果饿了,转换到吃饭状态
if (饿了)
{
_transitionEvent(StateEnum.EAT);
}
else if (想打球) // 如果想打球了,切换到打球状态
{
_transitionEvent(StateEnum.BASKETBALL);
}
else if (该写作业了) // 如果该写作业了,切换到写作业状态
{
_transitionEvent(StateEnum.HOMEWORK);
}
else
{
Debug.Log("休息中");
}
} public override void OnExit()
{
Debug.Log("美美的睡了一觉,好精神");
Debug.Log("叠被子");
Debug.Log("收拾房间");
}
}

写作业状态

public class StateHomeWork : StateBase
{
public StateHomeWork()
{
_state = StateEnum.HOMEWORK;
} public override void OnEnter()
{
Debug.Log("开始写作业啦");
} public override void OnExecute()
{
// 写作业累了,切换到休息状态
if (累了)
{
_transitionEvent(StateEnum.RESET);
}
// 想打球了,切换到打球状态
else if (想打球了)
{
_transitionEvent(StateEnum.BASKETBALL);
}
else
{
Debug.Log("我在写作业");
}
} public override void OnExit()
{
Debug.Log("停止写作业");
Debug.Log("作业本收起来");
}
}

打篮球状态

public class StateBasketball : StateBase
{
public StateBasketball()
{
_state = StateEnum.BASKETBALL;
} public override void OnEnter()
{
Debug.Log("开始打篮球啦,好高兴啊");
} public override void OnExecute()
{
// 如果饿了
if (饿了)
{
_transitionEvent(StateEnum.EAT);
}
else if (累了) //如果累了,切换到休息状态
{
_transitionEvent(StateEnum.RESET);
}
else if (该写作业了) //如果该写作业了,切换到写作业状态
{
_transitionEvent(StateEnum.HOMEWORK);
}
else
{
Debug.Log("打篮球");
}
} public override void OnExit()
{
Debug.Log("停止打篮球");
}
}

通过上面代码我们已经能够看到有限状态机的好处了,它将我们的各个状态分散到了不同的类中,这样我们在每个状态中只需要关心自己的逻辑(高内聚),减少了不同状态之间的耦合(低耦合),以及达到某一条件时需要切换到的状态,减少了很多 if else 的判断,提高的代码的可读性和扩展性。

上边代码是有限状态机的大概框架,部分地方使用伪代码,意在说明状态机的形式和大概样子,并不能完整运行,读者应该能发现代码中少了一个重要组成部分 Player,即我们判断的字段中 想打篮球、累了、该写作业了、吃饱了 这些都应该是我们的 Player 拥有的字段,并且在各个状态的 OnExecute 需要补齐逻辑,比如 吃饭状态的 OnExecute 需要每帧不断的去改变吃饭的量,来确定 Player 是否吃饱了。

本篇中还是有一部分使用了 if else

if (饿了)
{
_transitionEvent(StateEnum.EAT);
}

这样的硬编码条件,灵活性不高,我们可以通过一些修改,让我们的有限状态机变得灵活可配置,本片就不再讲解,后续将补上,如有讲解不对的地方,请 留言,谢谢

转自https://blog.csdn.net/LIQIANGEASTSUN/article/details/118932263?spm=1001.2101.3001.6650.20&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-20-118932263-blog-78070440.pc_relevant_3mothn_strategy_recovery&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-20-118932263-blog-78070440.pc_relevant_3mothn_strategy_recovery&utm_relevant_index=27

[转]有限状态机FSM(finite state machine) 一的更多相关文章

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

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

  2. Finite State Machine 是什么?

    状态机(Finite State Machine):状态机由状态寄存器和组合逻辑电路构成,能够根据控制信号按照预先设定的状态进行状态转移,是协调相关信号动       作.完成特定操作的控制中心. 类 ...

  3. Finite State Machine

    Contents [hide]  1 Description 2 Components 3 C# - FSMSystem.cs 4 Example Description This is a Dete ...

  4. FPGA学习笔记(七)——FSM(Finite State Machine,有限状态机)设计

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

  5. paper:synthesizable finite state machine design techniques using the new systemverilog 3.0 enhancements 之 FSM Coding Goals

    1.the fsm coding style should be easily modifiable to change state encoding and FSM styles. FSM 的的 状 ...

  6. paper:synthesizable finite state machine design techniques using the new systemverilog 3.0 enhancements 之 standard verilog FSM conding styles(二段式)

    1.Two always block style with combinational outputs(Good Style) 对应的代码如下: 2段式总结: (1)the combinational ...

  7. paper:synthesizable finite state machine design techniques using the new systemverilog 3.0 enhancements 之 standard verilog FSM conding styles(三段式)

    Three always block style with registered outputs(Good style)

  8. TCP Operational Overview and the TCP Finite State Machine (FSM) http://tcpipguide.com/free/t_TCPOperationalOverviewandtheTCPFiniteStateMachineF.htm

    http://tcpipguide.com/free/t_TCPOperationalOverviewandtheTCPFiniteStateMachineF.htm   http://tcpipgu ...

  9. Linux编程之有限状态机FSM的理解与实现

    有限状态机(finite state machine)简称FSM,表示有限个状态及在这些状态之间的转移和动作等行为的数学模型,在计算机领域有着广泛的应用.FSM是一种逻辑单元内部的一种高效编程方法,在 ...

  10. 有限状态机FSM详解及其实现

    有限状态机,也称为FSM(Finite State Machine),其在任意时刻都处于有限状态集合中的某一状态.当其获得一个输入字符时,将从当前状态转换到另一个状态,或者仍然保持在当前状态.任何一个 ...

随机推荐

  1. Python自动合并Word文件同时添加分页符的方法

      本文介绍基于Python,实现对多个Word文档加以自动合并,并在每次合并时按要求增添一个分页符的方法.   现有多个Word文档文件,需将其按名称顺序合并为一个新的Word文件,且需保证每一次合 ...

  2. Postman操作指南

    基本使用 基本使用在这里不做记录,大多数人下载完就会用.这里记一下重点. 抓包浏览器 浏览器安装插件postman interceptor:插件在postman-interceptor界面最下面提示的 ...

  3. 从NLP视角看电视剧《狂飙》,会有什么发现?

    目录 1.背景 2.数据获取 3.文本分析与可视化 3.1 短评数据预处理 3.2 词云图可视化 3.3 top关键词共现矩阵网络 3.4 <狂飙>演职员图谱构建 4.短评相关数据分析与可 ...

  4. Visual Studio Code C / C++ 语言环境配置的历程

    前言 从大一开始学习c++用的dev-c++,后来看到老师用的是vs  code,实在是馋它的颜值便去下了vs  2017.至于为什么下载vs 2017呢?是因为下载的时候我以为他们是一样的,便下了v ...

  5. 关于hbulider开发工具微信小程序请求跨域

    问题描述: 1.thinkphp设置了跨域请求设置 2.接口在浏览器模式正常请求 3.微信小程序请求显示跨域 解决方案:

  6. k8s之pod的生命周期

    pod生命周期 和一个个独立的应用容器一样,Pod 也被认为是相对临时性(而不是长期存在)的实体. Pod 会被创建.赋予一个唯一的 ID(UID),并被调度到节点,并在终止(根据重启策略)或删除之前 ...

  7. Oracle 详细-创建用户并导入sql文件

       0.基本信息查询SQL   select * from dba_users; 查看数据库里面所有用户,前提是你是有dba权限的帐号,如sys,system select * from all_u ...

  8. Unity Vuforia 动态替换识别图

    1.在Unity里 Vuforia 用来做识别信息的是 StreamingAssets 下 Vuforia文件夹内的 Dat和XML 文件. 2.想要替换识别图需要在Vuforia官网里替换识别图 ( ...

  9. iOS开发之权限申请说明key

    我们申请不同的权限需要对应的key,在info.plist内加上对用户的提示语 麦克风权限 NSMicrophoneUsageDescription 相机权限 NSCameraUsageDescrip ...

  10. iOS开发之桌面快捷方式Quick Actions

    长按桌面APPIcon图标快捷操作添加功能开发 在支持 3D Touch 的设备上,Quick Actions 可以让用户更快,更少的操作步骤去完成他们最常做的事情,其中这么多操作可以通过主屏幕直接完 ...