返回
玩法拆解 · 代码 · Demo
战斗状态机热门

用状态机组织战斗单位行为

把单位行为拆成可测试、可组合的状态,减少 if-else 爆炸。

0
用状态机组织战斗单位行为

玩法拆解

为什么需要状态机
  • 复杂行为可读性差
  • 需求变化导致分支膨胀
拆分建议
  • Idle/Move/Attack/Hitstun/Dead
  • 状态间用事件驱动

关键代码

事件驱动(简化)
ts· 13

Demo

最小可交互:事件驱动的战斗 FSM(用于演示状态切换与可测试转移)。

文章

文章以 Markdown/MDX 文本子集渲染(不支持自定义组件)。

用状态机组织战斗单位行为

战斗单位的行为一旦开始“加需求”,最容易变成这种形态:

  • 追击时被打断怎么办?
  • 攻击前摇能不能被打断?
  • 受击硬直结束后回到哪里?
  • 仇恨切换、丢失目标、回到巡逻怎么拼?

如果用 if / else 把这些拼在一起,代码会快速变成“谁都不敢改”的分支地狱。

这篇文章给一个可落地、可测试、可扩展的做法:用有限状态机(FSM)组织战斗单位行为,并把“状态切换”统一成事件驱动。

---

1. 核心结论:把行为拆成“状态”,把切换拆成“事件”

推荐把单位的行为分成两层:

  1. 状态(State):单位当前处于哪种行为模式(Idle / Move / Attack / Hitstun / Dead …)
  2. 事件(Event):触发状态变化的事实(SEE_TARGET / IN_RANGE / TOOK_HIT / ANIM_DONE / LOST_TARGET …)

你要追求的是:

  • 状态内部逻辑尽量简单(只处理“在这个状态里要做什么”)
  • 状态切换只发生在一个统一的地方(transition/guard)

---

2. 最小状态集:先把“可玩闭环”跑通

对多数战斗单位(近战 AI),“最小可玩闭环”一般是:

  • idle:站立/巡逻
  • move:朝目标移动
  • attack:攻击(包含前摇/出手/后摇)
  • hitstun:受击硬直(可打断)
  • dead:死亡(不可逆)

其中最关键的是:attack 里要明确“能否被打断”,否则后续再补打断会很痛苦。

---

3. 状态机的接口:把外界依赖收敛到 Context

不要让状态里随便访问全局对象或到处 import,否则测试会很难写。

推荐每个单位有一个 context(或称 blackboard),提供状态需要的最小信息:

  • pos / velocity
  • targetId / distanceToTarget
  • cooldowns
  • hp
  • events(本帧输入/感知事件)

状态函数只读写 context,不直接操作引擎全局。

---

4. 事件来源:输入/感知/动画/伤害,都统一成 Event

很多“分支爆炸”来自于事件来源分散:

  • AI 感知(看到目标、丢失目标)
  • 碰撞与距离(进入攻击范围、离开范围)
  • 动画回调(前摇结束、出手帧、后摇结束)
  • 受击系统(被打、击退、控制)

推荐做法:每帧把这些统一塞进 events[],状态机只消费 events。

这样你甚至可以做“录制/回放”:记录事件序列就能复现行为。

---

5. 迁移策略:从 if-else 到 FSM 不需要一次性重写

一个安全的迁移节奏:

  1. 先把“攻击流程”提炼成 attack 状态(最容易分支爆炸的部分)
  2. 再把“追击/脱战”提炼成 moveidle
  3. 最后补 hitstun 与各种控制(眩晕/击退/沉默…)

每一步都能运行、能回归测试,不会把项目搞崩。

---

6. 可测试性:transition 是最值得写单测的地方

最容易写单测、也最能挡住回归 bug 的部分,是 transition(state, event)

  • 给定当前 state + event,是否切到正确 state?
  • guard 条件(cd 未好不能攻击、HP<=0 必死)是否生效?

推荐把 transition 写成纯函数(或接近纯函数),这样测试成本最低。

你可以用“状态转移表”的方式列出关键场景:

  • idle + SEE_TARGET => move
  • move + IN_RANGE => attack
  • attack + TOOK_HIT => hitstun(如果允许打断)
  • hitstun + STUN_END => move/idle(看是否仍有目标)
  • 任意状态 + HP_ZERO => dead

---

7. 调试与可视化:先把“状态名”打出来就能救命

MVP 阶段不需要高级工具,先做到:

  • 屏幕上显示单位当前 state
  • 显示最近 3 个事件(lastEvents)
  • 显示目标与距离(targetId / dist)

这会让你在调 AI 行为时节省大量时间。

下一步才是:

  • 状态图可视化
  • 事件时间线
  • 断点式暂停/单步推进

---

8. 下一步扩展(可选)

  • 分层状态机(Hierarchical FSM):把 attack 拆成 windup/strike/recover
  • 行为树 + 状态机结合:高层决策用 BT,低层动作用 FSM
  • 数据驱动:把状态/事件/guard 配成表,支持不同怪物复用同一套框架

你可能也喜欢