Home Fsm
Post
Cancel

Fsm

好用的FSM插件

UnityHFSM

NodeCCanvas


FSM

  1. FSM本质上只是机械化的程序逻辑,只需要最基本的分支语句就可以实现。
  2. 设计FSM的难点在于针对需求设计状态转移图,并设计精确的从一个状态转移到另一个状态的条件。只有有了清晰的状态转移图,才可能用明确的代码描述规则。

状态和条件

image-20230205201541077

当前处于某个状态,如果发生某件事,就切换到另一个状态。

  1. 现态:当前所处的状态
  2. 条件:条件满足时会触发一个动作,或迁移到次态。
  3. 动作:条件满足后执行的动作,非必须。
  4. 次态:条件满足后要迁往的新状态,次态被激活后就变成“现态”。

状态机的应用

所有能够构成独立状态的系统或功能,都可以使用状态机来表示。

  1. 游戏框架设计。
  2. 场景切换。
  3. 游戏AI - 空闲,巡逻,平静,激怒等
  4. 宝箱,机关等多动画的元素。例如:把宝箱或机关的每个动画看成一个状态 - 打开,关闭等。
  5. [[C#迭代器]]
  6. [[动画状态机]]

AI状态机

AI状态机框架伪代码

框架伪代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
// 状态基类
class AIStateBase
{
    private int STATE;
    public abstract OnEnter();
    public abstract OnExit();
    public abstract Update();
}

// 继承基类实现状态类
class AIRunState : AIStateBase
{
    public AIRunState() {
        STATE = STATE.AIRunState;
    }
    public override OnEnter() {
        PlayAnimation("run",loop);
    }
    public override OnExit() {
        StopAnimation("run");
    }
    public override Update() {
        if(target != null) {
            MoveTo(target);    // 如果有目标,则向目标移动
        }
        else {
            Move(dir);    // 如果没有目标,则向指定方向移动
        }
    }
}

// 状态机控制类
// 控制类管理各个状态类,控制各个状态之间的切换,相当于状态机的大脑。
class StateControl
{
    AIStateBase[] mStates;
    AIStateBase mCurrentState;

    // 初始化
    public void Init() {
        mStates = new AIStateBase[STATE.Max];
        mStates[STATE.AIIdleState] = new AIIdleState(StateControl);
        mStates[STATE.AIRunState] = new AIRunState(StateControl);
        mStates[STATE.AIMoveAttackState] = new AIMoveAttackState(StateControl);
        mStates[STATE.AIPatrolState] = new AIPatrolState(StateControl);
        mCurrentState = null;
    }
    // 切换状态
    public void ChangeState(STATE state) {
        if(mCurrentState != null) {
            mCurrentState.OnExit();
        }
        mCurrentState = mStates[state];
        mCurrentState.OnEnter();
    }
    // 实时更新
    void Update(){
        if(mCurrentState != null)
        {
            mCurrentState.Update();
        }
    }
}

AI状态机状态示例

跑步 Run
  1. 用OnEnter函数编写机器人跑步的动画,并让动画不断循环播放,然后在更新函数Update中开启不断向前移动的位置变化动画。当机器人不再需要移动时,也就是退出跑步状态事件,OnExit既可以调用停止播放动画的方法,也可以不停止播放,因为下一个状态肯定会播放其他动画,不如让它们通过插值过渡一下,让动画看起来更顺滑。
追击 MoveAttackState
  1. 进入OnEnter函数时先锁定目标,把目标保存下来,并且寻找追击目标的路径(Path Find)。接下来就是“追击状态”的更新函数,每帧都会调用更新函数Update。在Update函数中,检查目标是否已在攻击范围内,如果在范围内,则立刻播放攻击动画,并且调用目标的攻击接口来攻击目标,如果不在范围内,则检查锁定目标是否超出检测范围,如果确定已超出范围,则重新寻找路径,并且根据路径来完成位移,否则不再追击。整个追击状态都是在Update函数中不断地进行判断和位移。
巡逻 Patrol
  1. 进入OnEnter函数,在OnEnter里找到一个最近的巡逻点,然后在更新函数Update中持续循环,走向最近的巡逻点,在走到第一个最近的巡逻点后,继续按照巡逻点的布置顺序前往下一个巡逻点,如此不断循环往复。同时每次更新移动时,都要检查敌人是否在范围内,如果有敌人出现在检查范围内,则结束当前巡逻状态,调用OnExit,转而进入追击状态。

状态机的优缺点

优点
  1. 可维护强。
  2. 可扩展性强。
  3. 逻辑耦合清晰。
  4. 符合人类思维逻辑,代码容易理解。
  5. 理论上来说 FSM 可以实现任何 AI 行为。
缺点
  1. 每个状态都必须由设计人员亲自设定,因此编写每个状态时要考虑所有情况,每种情况要有相应的处理方式。
  2. 根据图论,增加顶点时,连线数会以平方的量级增加。所以AI过于复杂时,编写的逻辑复杂度呈指数级增长。最后很可能无法承受太复杂的AI行为逻辑。
  3. 复杂到一定程度,人脑就无法承受,所以只能实现一些低能或者低阶的 AI。
  4. 状态间的连线太多很容易让人陷入困惑。
This post is licensed under CC BY 4.0 by the author.
Contents