之前发布的那篇博客可能说的并是不非常清楚,所以整理了一下,也参考了一些文档,于是又写了一篇总结。
一、有限状态机FSM的要点
1、拥有一组状态,并且可以再这组状态之间进行切换。
2、状态机同时只能存在一个状态,英雄不能能同时处于跳跃和站立。而防止这一点就是使用FSM的理由之一。(类似于动画状态机)
3、一连串的输入或事件被发送给机器。(这句话的意思就是,在使用的很多的情况下,要使用FSM状态机)
4、每个状态都有一系列的转换,转换与输入的另一个状态相关。当输入进来时,如果他与当前状态的某个转换匹配,那么机器则转为所指的状态。
举例:在战力状态时,按下 下键转换为俯卧状态。在跳跃时按下 下键转换为跳斩。如果输入在当前状态没有定义转换,则输入就被忽视。也就是说,每个状态机内管理着自己可转换到的状态,如果接收的按键是无法转换到的,那么则无视这个状态。
5、就目前而言,游戏编程中状态机的实现方式,有两种可以选择。
(1)用枚举配合switch case语句。
(2)用多态与虚函数(也就是状态模式)。
二、使用switch case设计的fsm
1、当拥有一些列的标记成员变量,而他们只能有且仅有一个为True时,定义成枚举更加合适。
(这里比如说是你定义了一个跳跃的bool来控制跳跃,同时又定义了一个下蹲的bool来控制下蹲,那么其实这样会导致冲突或者说是浪费,直接定义一个枚举就好了)
2、使用switch case设计的问题:不易于拓展
在你设计好后,如果你要使用更多的功能的话,则会造成一个无法拓展的问题。往往一个小需求就会造成你需要更改很多原有代码,这显然不是我们想看到的。
三、简单描述状态模式与状态机
1、不要无论青红皂白就去使用面向对象思维方式中的动态调度(也就是虚函数和多态)去解决问题。如果每次都这么用,可能简单问题会被复杂化。其实有的时候一个简单的IF语句就够用了。
2、描述状态模式
“Allow an object to alter its behavior whenits internal state changes. The object will appear to change its class.
允许对象在当前内部状态改变时,改变其行为。就像是这个对象改变了自己的类一样。
(1)状态模式主要解决的是当控制一个对象转换的条件表达式过于复杂的情况,它把状态的判断逻辑转移到表示不同的一系列当中,可以把复杂的逻辑判断简单化。
(2)状态模式的实现要点主要有三点:
(2.2)为状态定义一个接口
(2.2)为每个状态定义一个类
(2.3)恰当地进行状态委托
(3)举例:在时序电路里头,一个系统往往由一堆状态组成,从状态A,通过输入,跳转到状态B。这就是状态图。例如下面的图片:
四、什么是FSM有限状态机?
上面这种方法就被称作“有限状态机”(FSM,Finite State Machine)。
那究竟什么是FSM呢?
·FSM是一种数据结构,它由以下几个部分组成:
1、内在的所有状态(必须是有限个) //只有有限个状态,否则也太大了
2、输入条件 //达成某个条件之后,转换成某个状态
3、状态之间起到连接性作用的转换函数 //状态中有切换状态的转换函数(发送一条转换消息,由上层接收)
·为什么要用FSM?
因为他编程快速简单,易于调试,性能高,与人类思维相似从而便于梳理,灵活且容易修改
·FSM的描述性定义:
一个有限状态机是一个设备,或是一个模型,具有有限数量的状态。它可以在任何给定时间根据输入进行操作,使得系统从一个状态转换到另一个状态,或者是使一个输出或者一种行为的发生,一个有限状态机在任何瞬间只能处于一种状态。
·挖掘状态
例如我们有这样一个场景。玩家控制一个Hero打怪练级。这个英雄等级不够没啥经验带上典型的阿Q精神,所以基本上只有这三个动作:
1,平时的状态是巡逻,就是漫无目的的走。。。
2,如果遇到敌人之后大量一下敌人。
3,如果敌人比自己弱小,那就打。
4,如果敌人比自己强大,那就跑。
我对FSM的基本解释:一个智能体,在有规则的时间间隔内询问现在所掌握的环境数据,使得它能够基于从游戏环境中接受到的刺激进行必要的状态转换。每一个状态可以模型化为一个分离的对象,或者存在于智能体外部的函数。
这样,FSM提供给了我们一个清楚灵活的结构。
·FSM一般骨架代码
一般FSM中需要有以下几个类作为一种数据框架:
FSMState类:抽象类,表示基本状态,所有状态都应该继承自这个类
FSMMachine类:一台有限状态机
FSMAIControl类:存放有限状态机,通常就是游戏AI的主循环。并且能存放环境感知数据等内容。
这里有几个类,需要重点解析
这个方法是在StateSystem中,负责转换的方法
转换是由状态来负责,状态触发了条件后,将条件传递过来
这个方法可以通过状态传递过来的条件,通过状态身上的GetPuCutState找到对应的ID
保证这个ID不会出错 这个好处是,就算转换的也是转换为状态身上自己存的东西
(因为存是自定义存,所以可能造成每个状态键和值对应不上。
比如
(攻击)状态 -触发-【看到了】条件,转换到-(技能)状态,
而
(防御)状态-触发-【看到了】条件,转换到-(逃跑)状态
)
总结:市面上关于状态机的介绍很多,这里笔者只是写了自己了解的一部分,当然,更多的逻辑我还没有整理。所以以下是我对状态机的认知与整理。
一、关于
FSM有限状态机是一种为了使得某事或某人在不同情况(不同状态)下进行不同行为的解决方案。类似于Unity中自带的动画状态机的模式。
优点:可以很好的解决有多重状态的角色在不同状态下的切换。(比如在跳跃状态下就无法进入冲刺状态。但是在行走状态中就可以进入冲刺状态。因为在跳跃状态是没有进入冲刺状态的选项的)
(扩展:每个技能的释放都可以算作是一个状态【技能状态】,播放动画的同时不可进入到其他状态,只有达成某种条件后进入攻击或者Idle状态)
缺点:代码量巨大,因为每个状态都对应一个状态脚本,一旦你的状态很多,那么会出现非常多的脚本。
二、实现逻辑
这里还是写一个小型的敌人AI来举例
首先列出必须存在的类和枚举:
enum Transition 【转换条件】
{
LookPlayer, //看到了player
NearPlayer, //接近了Player
LosePlayer //丢失了Player
}
enum StateID 【状态ID】
{
Attack, //攻击
Idle, //Idle
Chase //追逐
}
abstract Class StateBase【抽象状态基类】
{
//当前状态对应的ID,构造的时候传递?只有Get方法,每个状态唯一对应
StateID mStateID; 【当前状态对应ID】
//存放当前状态可以转换到的状态
Dictionary<Transition trans,StateID> map;【状态字典】
//给字典添加转换条件和与之对应的状态
void AddTransition(Transition trans,StateID id); 【添加方法】
//通过状态ID来删除一个状态,不用条件删除的原因是可能某些时候我们只需要删除掉这个状态,而不管是哪个条件影响到他的
void RemoveTran(StateID id);【删除方法】
//这个方法,是配合FSMSystem中切换状态的方法使用的,在切换的时候调用这个方法,可以从状态身上拿到相对应的StateID
StateID GetPuCutState(Transition trans); 【获取当前状态要转换成的状态ID】
//用于每帧监听是否满足切换行为
abstract void Monitor(GameObject Player); //每帧检测是否满足切换状态
//这里只负责行为
abstract void Act(GameObject Player); //每帧执行代码
//进入后触发一次 和 退出后触发一次
virtual void OnEnterState(); 【进入状态】
virtual void OnExitState(); 【退出状态】
}
//用于管理所有的状态
Class FSMSystem 【状态机管理系统】
{
List<StateBase> states; 【存放当前状态机中管理的所有状态】
StateBase CurrentState; 【当前状态】
//添加一个状态进入集合,同时判断是否为第一个加入集合的,如果是,那么就将当前状态设置为他
void AddState(); 【添加一个状态进入】
void Remove(); 【删除一个状态(可能会用上,先写上)】
//转换方法,这个方法可能比较乱,所以我详细的写一下
void ChangeState( Transition trans )
{
//在这里,首先各种判断预防错误我就先不说了
//首先通过当前状态获取StateID
StateID nextStateID = CurrentState.GetPuCutState(trans);
//取得了ID之后,在集合中找到所对应的状态
//foreach遍历管理的所有集合,找到StateID相对应的状态
//调用CurrentState当前状态的退出方法
//交换CurrentState为相对应的状态
//调用CurrentState的进入方法
//(这里就简单的交换并调用了一下)
}
}
本篇整理出自:
https://blog.youkuaiyun.com/poem_qianmo/article/details/52824776