好用的FSM插件
FSM
- FSM本质上只是机械化的程序逻辑,只需要最基本的分支语句就可以实现。
- 设计FSM的难点在于针对需求设计状态转移图,并设计精确的从一个状态转移到另一个状态的条件。只有有了清晰的状态转移图,才可能用明确的代码描述规则。
状态和条件
当前处于某个状态,如果发生某件事,就切换到另一个状态。
- 现态:当前所处的状态
- 条件:条件满足时会触发一个动作,或迁移到次态。
- 动作:条件满足后执行的动作,非必须。
- 次态:条件满足后要迁往的新状态,次态被激活后就变成“现态”。
状态机的应用
所有能够构成独立状态的系统或功能,都可以使用状态机来表示。
- 游戏框架设计。
- 场景切换。
- 游戏AI - 空闲,巡逻,平静,激怒等
- 宝箱,机关等多动画的元素。例如:把宝箱或机关的每个动画看成一个状态 - 打开,关闭等。
- [[C#迭代器]]
- [[动画状态机]]
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
- 用OnEnter函数编写机器人跑步的动画,并让动画不断循环播放,然后在更新函数Update中开启不断向前移动的位置变化动画。当机器人不再需要移动时,也就是退出跑步状态事件,OnExit既可以调用停止播放动画的方法,也可以不停止播放,因为下一个状态肯定会播放其他动画,不如让它们通过插值过渡一下,让动画看起来更顺滑。
追击 MoveAttackState
- 进入OnEnter函数时先锁定目标,把目标保存下来,并且寻找追击目标的路径(Path Find)。接下来就是“追击状态”的更新函数,每帧都会调用更新函数Update。在Update函数中,检查目标是否已在攻击范围内,如果在范围内,则立刻播放攻击动画,并且调用目标的攻击接口来攻击目标,如果不在范围内,则检查锁定目标是否超出检测范围,如果确定已超出范围,则重新寻找路径,并且根据路径来完成位移,否则不再追击。整个追击状态都是在Update函数中不断地进行判断和位移。
巡逻 Patrol
- 进入OnEnter函数,在OnEnter里找到一个最近的巡逻点,然后在更新函数Update中持续循环,走向最近的巡逻点,在走到第一个最近的巡逻点后,继续按照巡逻点的布置顺序前往下一个巡逻点,如此不断循环往复。同时每次更新移动时,都要检查敌人是否在范围内,如果有敌人出现在检查范围内,则结束当前巡逻状态,调用OnExit,转而进入追击状态。
状态机的优缺点
优点
- 可维护强。
- 可扩展性强。
- 逻辑耦合清晰。
- 符合人类思维逻辑,代码容易理解。
- 理论上来说 FSM 可以实现任何 AI 行为。
缺点
- 每个状态都必须由设计人员亲自设定,因此编写每个状态时要考虑所有情况,每种情况要有相应的处理方式。
- 根据图论,增加顶点时,连线数会以平方的量级增加。所以AI过于复杂时,编写的逻辑复杂度呈指数级增长。最后很可能无法承受太复杂的AI行为逻辑。
- 复杂到一定程度,人脑就无法承受,所以只能实现一些低能或者低阶的 AI。
- 状态间的连线太多很容易让人陷入困惑。