[转]模糊状态机

FSM  (有限状态机):涉及到不同状态之间的转换,且系统一次只处于一个当前状态。
   
FuSM(模糊状态机):有限状态机的一个变种,建立在模糊逻辑的概念之上,一般定义为“被扩展来处理部分真相概念的传统逻辑(bool 逻辑)的超集”。应该注意,虽然FuSM建立在模糊逻辑概念之上,但不代表是实实在在的模糊系统。
      部分真值是 一个非常强大的概念。与常规的FSM不同,FuSM在范围上不具有一般性。与FSM一样,FuSM跟踪一系列可能的游戏状态。但不同的是,FSM具有一个 单一的当前状态,然后通过转换到一个不同的状态来响应输入事件,而FuSM 可能同时具有多个状态,因此不存在转换。模糊系统中的每个状态都计算一个"激活水平",该激活水平决定了系统处于任意给定状态的程度,因此,系统的整体行 为由当前被激活状态的贡献组合来决定。
      FuSM仅仅对那些能够同时处于多个状态并且具有超越简单数字值(如开或关,关闭或打开,生存或死亡)的系统有用,模糊数值用于描述部分开,几乎关闭和未完全死亡等。另一种对此类数值类型进行量化的方法是使用一个归一系数(0.0 与 1.0之间的数)来表示条件对各个端状态的隶属度(例如:0.0表示完全关闭,1.0表示完全开启),尽管对于FuSM来说归一化并不是必须的。这是不必记住集合隶属度的所有权限制的一种简单方式,同时也确保了集合隶属度值之间比较的简单性。
      关于什么是真正的FuSM,在游戏AI领域还存在一些混淆的看法,因为在同一类别中存在好几个FSM变种被当作是FuSM。这些变种包括:
    1::具有转换优先级的FSM. 在该模型中,必须对每个可用状态的激活水平进行计算(该模型仍然是一个FSM,因此每个状态都有一系列可能的转换);然后那个具有最高激活水平的状态获 胜,成为新的当前状态。这是许多程序员使用模糊度概念来增加其决策状态机的方式,但该系统仍然是一个FSM,并且类似系统输出行的可预测性仅仅比常规 FSM稍微小一点。
   2 :概率FSM,在 该形式的FSM中,从状态出发的各个转换都被赋予了一定的概率,因此FSM遍历更加不确定,从而具有较小的可预测性。这些概率能够随时间发生变化,或者可 以在FSM内部进行设置,使用多个FSM来对不同的概率集进行分组。概率FSM有时用于当某些转换具有许多等价输出状态的情形,例如,靠近一个敌人将使他 转换到3个状态(具有等效值)之一:Punch,Kick,HeadButt。如果某一给定转换只有一个输出状态,FSM则正常工作。但如果有多个状态, 则对这多个状态进行概率分配(可以同等选择的平均分配,或侧重于某些状态,或根据分支最近是否被采用或人类阻止使用某一走步进行更复杂的判断)。
   3 :马尔可夫模型 。(什 么时候用俺的名字也命名一个模型)与概率FSM很像似,但其转换逻辑是完全基于概率的,因此马尔可夫模型可用于耦合状态中推断变化。举一个非常简单的例 子,比如有两个状态 :AIM(瞄准)和FireWeapen(开火)。在该游戏中,这两个状态通常是完全连接的,因为当瞄准后就会发射,但是,假如要模仿一个更真实的枪炮模 型,并有2%的机会AIM状态将转换到WeaponJam状态。此类转换有时称为“可靠性建模”。在该示例中,武器具有 98%的可靠性,巴尔可夫模型主要用于此类统计的建模,因为系统的假设之一就是下一个状态通过概率与当前状态发生联系。一个反应性的视频游戏可能具有某些 属于该类的元素,但由于AI对手可能改变状态的主要原因是为了响应人类玩家的愚蠢行动,此类状态预测很少成为主流。此类系统的一个有趣用法是去实际模仿人 类偶尔表现出来的“意外事件”-----模糊状态机 - 生活 - 无敌 AI 对手偶尔摔倒,丢球或打到自己的脚。所有这些这此意外事件都可以在基本的跑步,持球或射门动作层级中处理,并能够在这些活动中的高度耦合动画中通过采用极其不太可能的分支来使其发生。这类真实行为是否符合游戏模拟,或对玩家是否具有娱乐性,完全取决于用户。
  4:实际的模糊逻辑系统。与 流行的观念相反,FuSM并不是真正的模糊逻辑系统。模糊逻辑事实上是一个过程,部分真值表示的规则通过通过它来进行组合和推理,从而得到决策。之所以设 计模糊逻辑是因为许多真实世界问题并非总能表示为限定事件,而且真实世界的解决方案也并非总能表示为限定行动。模糊逻辑仅仅是是常规逻辑的一种扩展,使得 他们能够处理此类的规则集。游戏中实际模糊逻辑的最简单形式是描述行为变化的简单“If  ... else” 语句(或其等价物,通过一个数据表格或某种组合矩阵)。例如,语句“如果我的健康值低,而我的敌人的健康值高,那我应该逃跑”  就是一个简单的模糊规则。它以一种模糊的方式(低 高)对两个感知(我的健康值,敌人的健康值)进行比较,并赋予它一个行动。在过去几年中,该语句作为一个IF 语句在大量游戏中使用。这是一个实际模糊系统最简单,最小的形式。一个真正的模糊逻辑系统将包括许多一般的模糊指导方针,它们可处理我的健康值,我的敌人 的健康值 “ 以及所有其它规则需要考虑的变量的任意组合,其中这些规则将通过算法组合来提供一个响应动作。这往往是从模糊系统中获取结果的一种强大方式,但当存在过多 模糊变量时会出现问题。这可以通过一咱叫做 Comb's Method的统计方法进行处理,它能够简化所需要的规则集合,但同时也降低了精度。
     FuSM以及前面提到的相似变种 , 很快成了游戏AI 使用中的一种普遍方法。FSM的可预测性正越来越不能满足人们的需要,并且许多游戏的整体内容正变得足够丰富从而确保了FuSM额外设计和实现复杂性的必要性。
     FuSM需要具有比FSM更多的前向思考。游戏问题必须真正地分解为问题允许的极其独立的元素。FSM能够在FuSM系统范围内进行设计,通过计算数字激 活水平并设计系统使得状态执行中不存在交叠。有人在建立模糊系统时偶然做到了这些。对许多问题情形来说以一种限定方式进行思考是很自然的事情,因此如果在 游戏中很难想出那样能够适用于一般范围的问题。FuSM是一类允许多个状态激活为当前状态并能够具有与游戏局势有得每个状态的程度相当的一定激活水平的 FSM。
     事实上,许多人都认为FuSM根本就不是真的状态机(因为系统并不处于孤立状态),而更像是模糊知识库,其中有多个断言能够同时部分为真。但是,通过对独立状态进行编码以利用这些断言,我们能够使用FuSM来实现此类机制的AI目标。
    对Robottron游戏中AI控制敌人的决策系统进行编码是使用此类系统的一个简单例子,如图是一个简单Robottron玩家的FSM状态图
模糊状态机(FuSM) - 生活 - 无敌
它 有三个主状态:Approach,Evade,Attack.在一个严格的基于FSM的系统中,为了能够同时移动和射击,必须进行编码以使得 Approach和Evade状态以特定方向开始运动,但当状态改变时也不停止运动。这样做能奏效,但不够简洁。Attack状态必须要保持对其他状态的 转换进行检测,因此玩家在向另一个方向射击时不会撞上敌人,也不会陷入远离所有敌人的角落。更好的方式是为游戏设计一个FuSM。这样,玩家能够同时 Approch,Evade,Attack
      与FSM一样,FuSM也可以以一种自由形式的方式进行编写。
      关于Robotron示例需要注意的另一件事是Attacks状态不能完全模糊化。玩家要么射击,要么不射击,因为我们不可能部分启动激光器。其它状态则 不同,比如运动就可以表示成在不移动和全速移动之间的一个平滑梯度。然而,Attack状态的不可模糊化并不影响系统的其余部分,而且并不会使该方法失 效。FuSM能够很容易地通过以数字方式计算激活水平来加入更多的数字状态,系统将像其它状态一样响应该数字状态。
      16.2:
       与FSM一样,FuSM的代码也将由3个主类来实现
       1:FuSMState类。它是基本的模糊状态
       2:FuSMMachine类。它是模糊状态机
       3:FuSMAIControl类。它是AIControl类,负责机器的工作,它存储游戏相关的信息和代码
16.2.1 :FuSMState类
       在大多数纯粹的设计层级上,FuSM系统中的状态都是分离系统。每个状态都将使用感知变量(来自Contrl类,或更复杂和专用的感知系统)来确定激活水 平(我们将一个0到1之间的数字表示),该激活水平胜于测量所需状态怎么完全主动地对感知进行响应。在最简单的方式下,激活水平能够响应游戏中某个值的数 量,如进攻性:激活水平为零意味着根本不具有攻击性,而1.0则表示完全沉浸于进攻之中。FuSM的状态的最小需求与FSM状态非常相似,它们有:
       ●Enter()。只要进入该状态,该函数就会始终运行。它使得状态可以执行数据或变量的初始化
       ●Exit()       该函数在离开状态时运行,并主要作为一项清除任务,或用于需要运行的(或开始运行)任意希   望在特定转换处发生的额外代码的地方(针对Mealy式状态机)。
       ●Update()。如果该状态是FSM(针对Moore式状态机)中的当前状态,那么这是调用AI每个处理循环的主函数。
       ●Init().          该函数对状态进行初始化
       ●CalculateActivation()
. 该函数用于确定状态模糊激活水平。它返回一个数值,并将其作为m_activationLevel数据成员存储在状态之中。在本章后面将会看到,通过返回布尔值而非正常的归一值,我们将可以模仿更多的数字状态
       下面程序清单给出了该类的header。再次,我们尽量全面地设计这个类,从而在游戏实现时能够具有最大的灵活性。从中可知,除了 m_activationLevel数据成员外,它与FSM类非常相似。事实上,该数据成员可以并入FSM类中,并交替使用两种类型的状态来开发一个混合 系统。

//----------------------------------------------------
//   说明:(FuSM.h)类型定义头文件
//   日期:2009-09-29
//----------------------------------------------------
#ifndef __FUSM
#define __FUSM

// 状态枚举
enum
{
    FUSM_STATE_NONE,
    FUSM_STATE_APPROACH,
    FUSM_STATE_ATTACK,
    FUSM_STATE_EVADE,
    FUSM_STATE_GETPOWERUP,
    FUSM_STATE_COUNT
};

//模糊状态机类型
enum
{
    FUSM_MACH_NONE,
    FUSM_MACH_SAUCER,
    FUSM_MACH_COUNT
};

#endif

//------------------------------------------------------------
//  说明:状态类  具体状态继承此类
//  日期:2009-09-29
//  抄写:帝林
//------------------------------------------------------------

#ifndef  _FUSM_STATE_
#define  _FUSM_STATE_

class Control;                // AI控制类 负责状态机的工作  并存储游戏相关的信息和代码

class FuSMState
{
public:

    FuSMState(int type = FUSM_STATE_NONE, Control* vParent = NULL) { m_type = type; m_parent =   vParent; m_activationLevel = 0.0f;}

    virtual void Update(float dt) {}
    virtual void Enter()          {}
    virtual void Exit()           {}
    virtual void Init()           { m_activationLevel = 0.0f;}
    virtual float CalculateActivation() { return m_activationLevel;}

    // 检测边界  确保激活级别的合法性(位于0.0 -- 1.0 之间)
    virtual void CheckLowerBound(float lbound = 0.0f) {if(m_activationLevel < lbound) m_activationLevel = lbound;}
    virtual void CheckUpperBound(float ubound = 1.0f) {if(m_activationLevel > ubound) m_activationLevel = ubound;}
    virtual void CheckBounds(float lb = 0.0f,float ub = 1.0f) {CheckLowerBound(lb);CheckUpperBound(ub);}


    // 数据成员
    Control* m_parent;                       // 属于哪一个状态机
    int      m_type;                         // 状态类型
    float    m_activationLevel;              // 激活级别                   
};

#endif

       该类具有3个边界检测函数,它们仅仅是对激活水平进行上下限检测。可以从状态中调用任意一个,或者如果希望具有一个完全粗略的激活水平,则不需要进行任何调用
      与常规FSM一样,该类也包括两个数据成员:m_type 和 m_parent。整个状态机和状态间的代码都可以使用类型域,以基于正在考虑的特殊状态来进行判断,这些数值的枚举存储在FSM.h文件中并且通常是空 的,只包含默认的FSM_STATE_NONE值。当实际使用某事件的代码时,需要向该枚举中添加所有的状态类型,并从中开始运行。单个状态使用父域,因 此它们能够通过其Control结构访问一个共享数据区域。

 

FROM:

http://qq79402005.blog.163.com/blog/static/5966286420098285542546/

posted on 2011-01-06 21:25  AlexRowe  阅读(3408)  评论(0编辑  收藏  举报

导航