尝试自主打造一个有限状态机(一)

news2024/11/6 7:07:57

前言

        我们都知道Unity有自带的有限状态机Animator,它的功能非常强大,为了探索它背后的原理,我开启了这个系列的文章,尝试通过自主打造一个有限状态机来理解Animator的工作原理,同时我会将这个状态机应用于实际,进而测试它是否能够正常运转以及它的性能如何,最后针对测试结果去探索优化的方案。

声明

        本系列文章要求读者具备一定的C#编程基础,同时对接口和抽象类、继承关系、设计模式以及面向对象等知识有所了解,在文章中我会对这些知识进行简要的阐述,对于描述有误的地方敬请指正。

词表

名称

阐述

状态转换路径

以某个状态作为源状态,另一个状态作为目标状态,从源状态到目标状态的过渡关系称之为状态转换路径。

理论上的状态机

        我们从理论上的状态机去分析一个通用状态机的组成部分,进而探索状态机的设计原理和技巧。所以我们需要明确以下五个问题的答案:

       1.状态机是什么?

       2.状态机有什么作用?

       3.状态机的组成部分有哪些?

       4.状态机的工作原理是什么?

       5.应该怎么去设计一个状态机?

        1.状态机的定义

        有限状态机(英语:finite-state machine,缩写:FSM)又称有限状态自动机(英语:finite-state automaton,缩写:FSA),简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学计算模型。状态机的应用非常广泛,在编程中的应用仅作为其中一部分,本文不作详细阐述。具体请参考有限状态机和自动机编程。

       在本系列文章中,我们可以将状态机理解为一个管理状态和状态过渡的机器,状态机需要结合当前状态和输入来检测是否能够过渡到下一个状态,当前状态可能对应多个目标状态,但是在一次检测周期中有且仅有一个目标状态可能成为下一个状态,当前状态到目标状态之间的过渡条件是事先确定好的,那么在一次周期内会对当前状态所有的过渡进行一次检测,一旦满足过渡条件则进入下一个状态。

        2.状态机的作用

        从状态机的定义我们就可以明确状态机是用来管理状态和状态过渡的系统,尤其是对于状态数量多或状态过渡关系复杂的情况,状态机的优势就更加突显。在开发过程中我们难免会遇到一种情况——使用大量的if-else系列语句来完成一些业务逻辑的开发,当然这往往只是简单的情况,在游戏开发中如果我们对角色控制的开发不采用状态机,那么就需要大量的变量作为锁并且使用大量的if-else语句以防止角色从某个状态跳转到其它某些状态,随着状态数量的增多,代码的可读性下降,而出错率也会随之提升,同时后续的扩展或者维护将变得艰难。(在我第一次游戏开发中就出现过这个问题)

        3.状态机的组成

        一个状态机至少应该包括状态、状态过渡关系、状态过渡参数和状态机系统四个部分,除此之外还可以根据具体的应用为状态机添加组成部分,状态和状态过渡关系通常作为非静态实体类分别记录状态信息和状态过渡信息,而状态机往往需要复用以及适应更加广泛的应用所以也需要作为一个非静态实体类,如果状态过渡参数的种类较多并且要求统一也可以单独为一个非静态实体类记录状态过渡参数的信息。静态类与非静态类的区别在于,静态类无法为之创建实例对象,它是密封类且无法被继承,具体请参考Static Class。

        4.状态机的工作原理

        状态机理应具备一个起始状态,当启动状态机时,对所有以起始状态为源状态的状态转换路径进行检测,若满足过渡条件则从源状态过渡到目标状态,状态机完成相关交接任务后更新当前状态为目标状态,重复上述过程。对于有退出状态的状态机,当状态机的当前状态为退出状态时则根据退出状态中的执行逻辑完成对应的任务,而对于没有明确的退出状态的状态机而言将会不断运转直至人为调用状态机的停止方法才会结束运转。

        5.状态机的设计

        从状态机的组成我们可以明确状态类、状态转换路径类和状态机类是必不可少的。那么是否还存在其它类呢?这个就需要看我们如何划分这三个类的工作了,实际上在程序设计中类应该尽可能遵循单一职责原则(SRP),在这个基础上我们先尝试对这三个类的工作进行探索。状态类用于记录状态信息作为状态实体类,状态类应该包括四个基本方法,分别是进入该状态时执行的方法,处于该状态时执行的方法,退出该状态时执行的方法以及对状态进行重置的方法,除此之外状态之间可能还需要进行比较,所以还可以有状态的比较方法。状态转换路径类用于记录两个状态之间的过渡关系,应该包括获取源状态和目标状态的方法,状态转换路径重置的方法,状态转换路径的比较方法,同时对于状态转换路径是存在一个转换条件的,所以还应该包括一个用于检测该状态转换路径能否转换的方法。状态机类则作为整个状态机的系统类,负责管理所有的状态和状态转换路径以及协调状态和状态转换路径之间的对接工作,我们可以对状态和状态转换路径进行添加和删除,可以判断当前状态能否转换到指定的状态,判断该状态机类中是否存在指定的状态或状态转换路径,对当前状态的方法调用,状态机类的重置以及当前状态改变前后执行的方法。状态机应该能够独立运行,所以不应向外暴露不必要的接口避免其它调用者干预其正常运转。例如一个电梯,使用者需要按下对应方向键来告知电梯自己需要使用它以及需要向上还是向下,那么电梯就会根据使用者的要求来运转,但是其中具体的运转逻辑不是使用者所关心的,对于状态机而言也是如此,我们只需要暴露状态机的一些基本功能即可。

        状态类、状态转换路径类、状态机类具体的定义可能还需要结合具体的应用场景和开发环境,例如在Unity3D中我们通常需要将状态的刷新方法放在MonoBehaviour的Update或FixedUpdate中执行,此时我们就需要暴露状态机类中的刷新方法的接口,但是对于其它某些应用场景或开发环境,刷新方法的接口也可以不暴露在外,而是通过一些内在的机制,在状态机类中自行调用,例如在状态机类中创建一个不终止的计时器,然后设置对应的时间间隔,使得刷新方法能够以时间间隔为周期反复执行,那么此时调用者只需要启动状态机,状态机则会自动启动计时器开始执行刷新方法。对于状态类和状态转换路径类,也并非有统一的定义标准,对于有些应用场景,状态中仅需要完成一次指定逻辑的执行则退出,不存在重复执行指定逻辑的需求,那么状态类中就并非一定需要刷新的方法。

        所以对于状态机的设计,我们一方面要贴合实际应用,另一方面还应该遵循一些设计原则,避免让类变得臃肿,违反单一职责原则,可读性变差,扩展性变差和维护成本变高等问题。

实际示例(UML)

        如图1所示是一个状态机的组成部分,它包括CSFStateMachine、CSFState、CSFStateTransition、CSFTransitionMediator、CSFStateRule和CSFStateRuleChain。

图1

        图2是CSFStateMachine的类设计,它实现了IBaseStateMachine接口,作为状态机的实体类。

图2

        图3是CSFState的类设计,它实现了IState接口,作为状态的实体类。

图3

        图4是CSFStateTransition的类设计,它实现了IStateTransition接口,作为状态转换路径的实体类。

图4

        图5是CSFTransitionMediator的类设计,这个类的作用是作为一个中介者或调停者,负责对以指定状态为源状态的所有状态转换路径进行检测。

图5

        图6是CSFStateRule和CSFStateRuleChain的类设计,CSFStateRule则是作为记录状态执行逻辑的实体类,而CSFStateRuleChain则是负责对CSFStateRule的实例进行统一管理。

图6

        图7这两个类并不作为状态机的组成部分之一,两个类的功能则是作为比较器分别负责状态优先级和状态转换路径优先级的比较。 

图7

 如果这篇文章对你有帮助,请给作者点个赞吧!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/905359.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

unity 之 Input.GetMouseButtonDown 的使用

文章目录 Input.GetMouseButtonDown Input.GetMouseButtonDown 当涉及到处理鼠标输入的时候,Input.GetMouseButtonDown 是一个常用的函数。它可以用来检测鼠标按键是否在特定帧被按下。下面我会详细介绍这个函数,并举两个例子说明如何使用它。 函数签名…

美国陆军希望大数据技术能够帮助保护其云安全

随着陆军采用更大型的云服务,一位高级官员警告说,一些在私营部门有效的快速软件开发技巧和简单解决方案(例如开放代码库)如果没有额外的安全性,将无法为军队工作。 我们知道现代软件开发确实依赖于第三方库&#xff…

基于知识蒸馏的两阶段去雨、雪、雾算法调试记录

前言 该项目的介绍可以参考博主这篇博文:基于知识蒸馏的去雪、去雾、去雨算法 调试过程 该项目中inference.py可以直接使用,只要将student的权重文件放入即可,博主实验过其去噪后的结果,貌似是变清晰了一点。但train时的meta里的…

lnmp(docker)

1. 建立工作目录 [rootdocker ~]# mkdir /opt/nginx [rootdocker ~]# cd /opt/nginx [rootdocker nginx]# rz -E rz waiting to receive. #上传 nginx 安装包 nginx-1.12.0.tar.gz[rootdocker nginx]# rz -E rz waiting to receive. #上传 wordpress 服务包 wordpress-4.9.4-z…

使用shell脚本批量curl调用接口

文章目录 [toc] 1. 批量调用接口的方式1.1)方式一:业务代码 curl1.2)方式二 : shell curl 2.curl和wget的使用2.1)wget2.2) curl2.2.1) curl发送POST请求2.2.2) curl发送GET请求2.2.3) 参数有其他类型 3. shell脚本4. 从windows…

深度学习最强奠基作ResNet《Deep Residual Learning for Image Recognition》论文解读(上篇)

1、摘要 1.1 第一段 作者说深度神经网络是非常难以训练的,我们使用了一个残差学习框架的网络来使得训练非常深的网络比之前容易得很多。 把层作为一个残差学习函数相对于层输入的一个方法,而不是说跟之前一样的学习unreferenced functions 作者提供了…

【算法刷题之链表篇(1)】

目录 1.leetcode-82. 删除排序链表中的重复元素 II(1)题目描述(2)方法及思路(一次遍历)(3)代码实现 2.leetcode-19. 删除链表的倒数第 N 个结点(1)题目描述&a…

push github

一、生成密钥 打开git bash执行下面指令,Enter下一步Enter下一步..生成ssh key 密钥; ssh-keygen -t rsa 二、 复制公共密钥到git hub 登录github,在选项setting >> SSH and GPG key >> add new ssh添加刚才的公钥地址即可 验证…

大数据-玩转数据-Flink营销对账

一、说明 在电商网站中,订单的支付作为直接与营销收入挂钩的一环,在业务流程中非常重要。对于订单而言,为了正确控制业务流程,也为了增加用户的支付意愿,网站一般会设置一个支付失效时间,超过一段时间不支…

Codeforces Round 890 (Div. 2) E2. PermuTree (hard version) (主席树/树状数组/差分+前缀和)

题目 有一个初始为空的数组&#xff0c;你需要处理q(q<1e6)次操作&#xff0c;操作分四种&#xff1a; ① x&#xff0c;数组后面加一个新的数x ② - k&#xff0c;删掉数组最后面的k个值 ③ !&#xff0c;回滚最后一次变更&#xff08;只有①操作和②操作视为变更&…

Leetcode刷题之快乐数

题⽬描述&#xff1a; 算法原理: 为了⽅便叙述&#xff0c;将「对于⼀个正整数&#xff0c;每⼀次将该数替换为它每个位置上的数字的平⽅和」这⼀个 操作记为 x 操作&#xff1b; 我们做这道题可以参考环形链表:142. 环形链表 II - 力扣&#xff08;LeetCode&#xff09;…

在线HmacSHA224加密工具--在线获取哈希值又称摘要

具体请前往&#xff1a;在线计算HmacSha224工具

Internet Download Manager2023下载器最新中文版本功能

对于idm相信大家都不陌生&#xff0c;全称是Internet Download Manager。idm是一款非常经典、功能强大的Windows文件多线程下载加速软件&#xff0c;在电脑用户中口碑极好&#xff0c;被称为必装的HTTP下载神器。 1、idm既是下载器&#xff0c;也是加速器&#xff0c;可以提升…

next.js 创建 react ant design ts 项目

环境说明&#xff1a;next.js 官方文档要求node版本在16.8以上。笔者使用的 node版本是16.20.1&#xff0c;不要使用16.13.0&#xff0c;笔者在使用 node16.13.0环境时创建的 react 项目点击事件无效 next.js官网截图 next.js 官网&#xff1a;https://nextjs.org/ react 官网…

个人信息保护影响评估(PIA)怎么做?解发条件、实施步骤、操作指南

个人信息保护一直是人们关注的热点话题&#xff0c;互联网、人工智能、大数据等新兴技术的快速发展极大地增强了入侵个人信息的能力&#xff0c;对个人信息的随意收集、违法获取、过度使用、非法买卖、泄露等问题引起了全球各国的普遍关注。同时随着用户的个人信息保护意识的逐…

Flask模型部署教程?

如何使用Flask框架来部署机器学习模型&#xff1f;Flask是一个轻量级的Python Web框架&#xff0c;它非常适合用于将机器学习模型部署成实际应用。 什么是Flask&#xff1f; Flask是一个Python Web应用框架&#xff0c;它允许轻松地构建Web应用程序。它被广泛用于构建各种Web…

基于Java+SpringBoot+Vue的校企合作项目管理系统【源码+论文+演示视频+包运行成功】

博主介绍&#xff1a;✌擅长Java、微信小程序、Python、Android等&#xff0c;专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3fb; 不然下次找不到哟 Java项目精品实战案…

代码随想录(八):贪心算法

文章目录 455.分发饼干376. 摆动序列53. 最大子数组和122. 买卖股票的最佳时机 II55. 跳跃游戏1005. K 次取反后最大化的数组和134. 加油站860. 柠檬水找零135. 分发糖果406. 根据身高重建队列 455.分发饼干 题目链接 C代码&#xff1a; class Solution { public:int findCo…

在Windows下安装PhantomJS和CasperJS及入门介绍(上)

近在使用Python爬取网页内容时&#xff0c;总是遇到JS临时加载、动态获取网页信息的困难。例如爬取CSDN下载资源评论、搜狐图片中的“原图”等&#xff0c;此时尝试学习Phantomjs和CasperJS来解决这个问题。这第一篇文章当然就是安装过程及入门介绍。 一. 安装Phantomjs 下载地…

SWUST派森练习题:P118. 数组接雨

描述 给定一个整形数组​​arr​​**&#xff0c;已知其中所有的值都是非负的&#xff0c;将这个数组看作一个柱子高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。​​(​​数组以外的区域高度视为​0)** 数据范围&#xff1a;数组长度​​** 0≤n≤…