901 words
5 minutes
Untity学习记录-实现一个完整的2D游戏Demo-16-有限状态机和抽象多态类
有限状态机(FSM)的实现,以及如何使用抽象类来构建灵活可扩展的敌人AI系统。
什么是有限状态机?
有限状态机是一种数学模型,用于表示对象在不同状态下的行为。在游戏开发中,它特别适合用来管理角色的各种行为状态,比如:
- 巡逻状态
- 追击状态
- 攻击状态
- 受伤状态
- 死亡状态
状态机的基本组成
- 状态(State) :角色当前的行为模式
- 转换(Transition) :状态之间的切换条件
- 动作(Action) :在特定状态下执行的行为
抽象类基础
抽象类 vs 普通类
// 普通类
public class Pizza
{
public virtual void MakeCrust()
{
// 基础饼皮制作
}
}
// 抽象类
public abstract class PizzaBase
{
public abstract void MakeCrust(); // 只有声明没有实现
}
抽象类的特点:
- 不能被实例化
- 可以包含抽象方法(只有声明没有实现)
- 派生类必须实现所有抽象方法
- 可以包含已实现的方法
为什么要用抽象类实现状态机?
- 强制规范:确保所有状态都有统一接口
- 灵活扩展:方便添加新状态
- 代码复用:共享通用逻辑
- 清晰结构:状态逻辑分离,易于维护
实现步骤
1. 创建抽象基类
// BaseState.cs
public abstract class BaseState
{
protected Enemy currentEnemy;
public virtual void OnEnter(Enemy enemy)
{
currentEnemy = enemy;
}
public abstract void LogicUpdate();
public abstract void PhysicsUpdate();
public abstract void OnExit();
}
2. 实现具体状态
以巡逻状态为例:
// BoarPatrolState.cs
public class BoarPatrolState : BaseState
{
public override void LogicUpdate()
{
// 巡逻逻辑
if (!currentEnemy.physicsCheck.isGround ||
currentEnemy.physicsCheck.touchLeftWall ||
currentEnemy.physicsCheck.touchRightWall)
{
currentEnemy.isWaiting = true;
currentEnemy.anim.SetBool("walk", false);
}
// 计时器逻辑
if (currentEnemy.isWaiting)
{
currentEnemy.waitTimeCounter -= Time.deltaTime;
if (currentEnemy.waitTimeCounter <= 0)
{
currentEnemy.isWaiting = false;
currentEnemy.TurnAround();
currentEnemy.anim.SetBool("walk", true);
}
}
// TODO: 检测玩家
}
public override void PhysicsUpdate()
{
if (!currentEnemy.isHurt && !currentEnemy.isDead && !currentEnemy.isWaiting)
{
currentEnemy.Move();
}
}
public override void OnExit()
{
currentEnemy.anim.SetBool("walk", false);
}
}
3. 在Enemy类中管理状态
// Enemy.cs
public class Enemy : MonoBehaviour
{
public BaseState patrolState;
public BaseState chaseState;
protected BaseState currentState;
protected virtual void Awake()
{
patrolState = new BoarPatrolState();
// 初始化其他状态...
}
protected virtual void OnEnable()
{
currentState = patrolState;
currentState.OnEnter(this);
}
void Update()
{
currentState.LogicUpdate();
}
void FixedUpdate()
{
currentState.PhysicsUpdate();
}
protected virtual void OnDisable()
{
currentState.OnExit();
}
public void SwitchState(BaseState newState)
{
currentState.OnExit();
currentState = newState;
currentState.OnEnter(this);
}
}
设计优势
- 单一职责原则:每个状态只关注自己的逻辑
- 开闭原则:添加新状态不影响现有代码
- 低耦合:状态之间通过明确条件转换
- 易调试:可以轻松追踪当前状态
状态转换示例
// 在巡逻状态中检测到玩家
if (DetectPlayer())
{
currentEnemy.SwitchState(currentEnemy.chaseState);
}
// 在追击状态中丢失玩家
if (!DetectPlayer() && chaseTime > maxChaseTime)
{
currentEnemy.SwitchState(currentEnemy.patrolState);
}
性能优化技巧
- 对象池:重用状态实例而非频繁创建销毁
- 状态缓存:常用状态预先实例化
- 分层状态机:复杂AI使用多层状态机
- 条件优化:使用位掩码优化多条件判断
常见问题解决
Q: 为什么我的状态切换不生效? A: 检查:
- 确保调用了
SwitchState
方法 - 新状态的
OnEnter
方法正确执行 - 转换条件逻辑正确
Q: 如何共享数据 between states? A: 通过Enemy基类中的protected变量共享
Q: 状态太多导致代码混乱怎么办? A: 考虑:
- 使用子状态机
- 将相关状态分组
- 使用脚本化对象管理状态配置
扩展思考
- 状态模式 vs 策略模式:两者相似但意图不同
- 行为树:更复杂的AI可以考虑行为树
- Utility AI:基于得分的决策系统
- 机器学习:高级AI可以使用ML算法
Untity学习记录-实现一个完整的2D游戏Demo-16-有限状态机和抽象多态类
https://fuwari.vercel.app/posts/unity/2dadvanture/16statemachineabstarct/