Unity游戏AI实战:用FSM有限状态机打造智能NPC(附完整塔防Demo)
在塔防游戏中,敌人NPC的智能行为往往决定了游戏体验的成败。当敌人机械地沿着固定路径移动时,玩家很快会感到乏味;而当一个敌人能够根据战场形势动态调整策略——发现玩家防御塔时主动攻击,血量不足时撤退,甚至呼叫援军——游戏的策略性和沉浸感将大幅提升。这正是有限状态机(FSM)技术的用武之地。
1. FSM核心架构设计
有限状态机的本质是将复杂行为拆解为离散的状态和转换规则。在Unity中实现FSM时,我们需要建立三个核心组件:
// 状态基类示例
public abstract class FSMState {
public FSMStateID StateID { get; set; }
private Dictionary<FSMTriggerID, FSMStateID> transitionMap;
public virtual void EnterState(FSMBase fsm) {}
public virtual void ActionState(FSMBase fsm) {}
public virtual void ExitState(FSMBase fsm) {}
}
状态机的运作遵循明确的生命周期:
- 状态进入:执行初始化逻辑(如播放进场动画)
- 状态持续:每帧更新行为(如移动或攻击)
- 状态退出:执行清理工作(如停止粒子效果)
关键设计原则:
- 松耦合:状态之间不直接引用,通过状态机中转
- 可配置:使用ScriptableObject存储状态转换表
- 可扩展:新增状态只需继承基类,无需修改现有代码
2. 塔防案例实战配置
我们的塔防Demo包含四种基本状态:
| 状态类型 | 触发条件 | 行为表现 |
|---|---|---|
| 移动状态 | 默认状态 | 沿路径向基地移动 |
| 攻击状态 | 发现目标 | 停止移动并攻击 |
| 死亡状态 | HP≤0 | 播放死亡动画 |
| 闲置状态 | 无目标 | 站立待机 |
配置文件采用键值对格式,便于非程序员修改:
[Moving]
FindTarget>Attacking
NoHealth>Dead
[Attacking]
KilledTarget>Moving
NoHealth>Dead
反射技术实现动态加载:
Type stateType = Type.GetType(stateName + "State");
FSMState state = Activator.CreateInstance(stateType) as FSMState;
3. 高级状态行为实现
攻击状态的完整实现展示了如何融合Unity各系统:
public class AttackingState : FSMState {
private float attackCooldown;
public override void EnterState(FSMBase fsm) {
fsm.motor.StopMove();
Debug.Log($"{fsm.name}进入攻击状态");
}
public override void ActionState(FSMBase fsm) {
if (!fsm.currentTarget) return;
fsm.motor.LookAtTarget(fsm.currentTarget.transform);
attackCooldown -= Time.deltaTime;
if (attackCooldown <= 0) {
fsm.attackSystem.Fire(fsm.currentTarget.transform);
attackCooldown = fsm.attackSystem.AttackData.AtkInterval;
}
}
}
性能优化技巧:
- 使用对象池管理子弹实例
- 通过LayerMask优化碰撞检测
- 在状态Enter时预加载资源
4. 调试与优化策略
开发过程中常见的坑与解决方案:
-
状态振荡问题:
- 现象:状态在短时间内频繁切换
- 解决:添加状态切换冷却时间
-
条件检测开销:
// 优化前:每帧检测所有敌人 Collider[] allEnemies = Physics.OverlapSphere(transform.position, radius); // 优化后:使用触发器或分帧检测 private int checkFrameInterval = 3; void Update() { if (Time.frameCount % checkFrameInterval == 0) { CheckTargets(); } } -
可视化调试工具:
- 在OnGUI中显示当前状态
- 使用Debug.DrawRay绘制检测范围
- 状态切换时打印日志
5. 扩展设计模式
对于更复杂的AI需求,可以考虑组合模式:
// 组合状态示例
public class CompositeState : FSMState {
private List<FSMState> subStates = new List<FSMState>();
public override void ActionState(FSMBase fsm) {
foreach (var state in subStates) {
state.ActionState(fsm);
}
}
}
进阶方案对比:
| 方案 | 适用场景 | 实现复杂度 |
|---|---|---|
| 纯FSM | 简单行为逻辑 | ★☆☆ |
| 分层FSM | 多维度行为 | ★★☆ |
| 行为树 | 复杂决策 | ★★★ |
在塔防Demo中,我们为精英敌人实现了特殊状态:
- 狂暴状态:血量低于30%时攻击速度翻倍
- 召唤状态:定时生成小怪
- 撤退状态:受到重创后暂时撤退
这些扩展只需新增状态类,无需修改原有代码:
public class BerserkState : FSMState {
public override void EnterState(FSMBase fsm) {
fsm.attackSystem.AttackData.AtkInterval *= 0.5f;
}
public override void ExitState(FSMBase fsm) {
fsm.attackSystem.AttackData.AtkInterval *= 2f;
}
}
实际项目中,我会根据NPC类型配置不同的状态机参数。比如Boss敌人可能包含10+种状态,而普通小怪只需3-4种基本状态。通过ScriptableObject实现参数化配置,可以大幅提升设计效率。
313



被折叠的 条评论
为什么被折叠?



