EventBus设计模式
EventBus是一种解耦的通信机制,它的核心思想是:
发布者(Publisher)只把事件发送到总线,不需要知道谁会接收;订阅者(Subscriber)向总线注册感兴趣的事件回调,总线负责把事件分发给所有订阅者。
优点:
- 强烈解耦:发布者不依赖订阅者,便于模块化开发。
- 易扩展:增加新订阅者无需改发布者代码。
- 适合事件驱动/松耦合架构:UI、系统事件(音效、数据统计、AI信号)等很方便。
缺点:
- 可追踪性差:调用链被隐藏,调试困难(谁在什么时候发布/接收到?)。
- 内存泄漏:静态事件/未取消订阅会导致对象无法回收(特别容易在 Unity 中出现)。
- 隐式控制流:业务逻辑可能散落各地,读代码很难理解程序流程(容易变成“事件地狱”)。
有限状态机(FSM)架构详解
有限状态机是一个抽象模型,它有:
- 状态集合(比如 Idle、Patrol、Attack)
- 状态切换条件(什么时候从一个状态切换到另一个状态)
- 行为逻辑(每个状态具体的行为)
在游戏AI里,FSM就是根据当前状态和条件执行对应行为,并根据条件切换状态。
有限状态机的工作流程:
- 初始化状态机,指定初始状态(比如
IdleState)。 - 在游戏循环里(比如
Update函数中)调用当前状态的Update方法,执行状态逻辑。 - 状态逻辑内部根据条件判断,调用
stateMachine.ChangeState()切换状态。 - 切换时自动调用
Exit和Enter,完成切换准备。
实现有限状态机的步骤:
- 定义状态基类(EnemyState),抽象公共行为和接口
- 实现状态机类(EnemyStateMachine),管理当前状态,处理切换
- 实现具体状态子类,覆盖Enter、Update、Exit方法写行为逻辑
- 敌人主体(Enemy)初始化状态机,并每帧调用当前状态更新
- 状态逻辑里根据条件调用ChangeState切换状态
EnemyStateMachine类
职责:
- 管理当前状态currentState,负责切换状态。
Initilaize():设置初始状态,并调用Enter()执行进入逻辑。ChangeState():切换状态时,先调用旧状态的Exit(),再赋新状态并调用新状态的Enter()。
public class EnemyStateMachine
{
public EnemyState currentState { get; private set; }
public void Initialize(EnemyState startState)
{
currentState = startState;
currentState.Enter(); // 进入初始状态
}
public void ChangeState(EnemyState newState)
{
currentState.Exit(); // 退出当前状态
currentState = newState;
currentState.Enter(); // 进入新状态
}
}
EnemyState类
职责:
- 抽象了一个状态的行为,包括三个状态:Enter / Update / Exit。
- 保存了引用的敌人对象和状态机,方便访问数据和切换状态。
- 绑定动画参数名,方便状态对应动画切换。
- 设计了一个动画触发器标志(
triggerCalled),方便动画事件通信。
public class EnemyState
{
protected Enemy enemyBase; // 持有敌人对象,方便访问敌人数据和行为
protected EnemyStateMachine stateMachine; // 状态机对象,方便切换状态
protected string animBoolName; // 动画参数名称,用于控制动画切换
protected float stateTimer; // 状态计时器,用于状态时间控制
protected bool triggerCalled; // 用于动画触发事件标记
public EnemyState(Enemy enemyBase, EnemyStateMachine stateMachine, string animBoolName)
{
this.enemyBase = enemyBase;
this.stateMachine = stateMachine;
this.animBoolName = animBoolName;
}
public virtual void Enter()
{
enemyBase.animator.SetBool(animBoolName, true); // 进入状态时,开启对应动画参数
triggerCalled = false;
}
public virtual void Update()
{
stateTimer -= Time.deltaTime; // 状态持续时间递减
}
public virtual void Exit()
{
enemyBase.animator.SetBool(animBoolName, false); // 退出状态时关闭动画参数
}
public void AnimationTrigger() => triggerCalled = true; // 动画事件触发
}
具体状态类的实现:IdleState
public class IdleState_Range : EnemyState
{
private Enemy_Range enemy; // 持有具体敌人类型引用,方便调用远程敌人特有方法和属性
// 构造函数:传入敌人基类、状态机和动画参数名称
public IdleState_Range(Enemy enemyBase, EnemyStateMachine stateMachine, string animBoolName)
: base(enemyBase, stateMachine, animBoolName)
{
enemy = enemyBase as Enemy_Range; // 转型为远程敌人类型
}
public override void Enter()
{
base.Enter();
// 启用IK,具体控制是否启用手部或头部IK,这里启用左手IK,禁用右手IK
enemy.visuals.EnableIK(true, false);
// 随机播放一个Idle动画变体(0或1)
enemy.animator.SetFloat("IdleAnimIndex", Random.Range(0, 2));
// 如果武器是手枪,则禁用IK(可能因为手枪动作不需要IK)
if (enemy.weaponType == Enemy_RangeWeaponType.Pistol)
{
enemy.visuals.EnableIK(false, false);
}
// 设置计时器,从Enemy_Range的idleTime获取,用来控制多久后切换状态
stateTimer = enemy.idleTime;
}
public override void Update()
{
base.Update();
// 计时器倒计时结束,切换到移动状态
if (stateTimer < 0)
{
stateMachine.ChangeState(enemy.moveState);
}
}
}
行为树
行为树是一种组织AI行为的树形结构,每个节点代表一个行为单元或逻辑控制点,树从根节点开始,遍历执行子节点,从而实现复杂行为决策。
相比有限状态机(FSM),行为树能更灵活地组织复杂行为,易扩展,调试清晰。

Leave a comment