Unity状态机模式下多个同类型GameObject的最优通信方式咨询
领地对象进攻阶段通信方案
针对你当前基于状态机实现的领地管理逻辑,可采用以下几种可落地的通信方案:
方案1:状态上下文携带目标领地引用
- 触发进攻动作时(如玩家选中进攻领地后点击防守领地),在创建
AttackState实例时直接将防守方Territory对象作为参数传入构造函数,让状态本身持有交互双方的引用 - 代码示例:
// 自定义AttackState构造函数 public class AttackState : TerritoryBaseState { private Territory _attacker; private Territory _defender; // 传入攻防双方实例 public AttackState(Territory attacker, Territory defender) { _attacker = attacker; _defender = defender; } public override IEnumerator EnterState() { // 直接交互:调用防守方接口、读写双方属性 int loss = _defender.CalculateDefendLoss(_attacker.AttackTroop); _defender.TroopCount -= loss; // 状态流转时可继续携带防守方引用 _attacker.TransitionToState(new ExecutionState(_attacker, _defender)); yield break; } } // 触发进攻时的调用示例 attackerTerritory.TransitionToState(new AttackState(attackerTerritory, defenderTerritory));
- 优势:逻辑直观无额外依赖,调用链路清晰,适合一对一即时交互场景
- 注意:必须做好空引用校验,避免目标领地销毁后触发空指针异常
方案2:全局事件中心(事件驱动)
- 实现单例全局事件中心,定义进攻事件并携带攻防双方、兵力等参数,进攻方发布进攻事件,所有领地订阅事件后判断自身是否为目标防守方,再执行对应逻辑
- 代码示例:
// 进攻事件参数定义 public class AttackEventArgs : EventArgs { public Territory Attacker; public Territory Defender; public int AttackTroopCount; } // 全局事件中心 public static class GameEventCenter { public static event EventHandler<AttackEventArgs> OnAttackLaunched; public static void PublishAttackEvent(AttackEventArgs args) { OnAttackLaunched?.Invoke(null, args); } } // 进攻方AttackState中发布事件 GameEventCenter.PublishAttackEvent(new AttackEventArgs { Attacker = _attacker, Defender = targetDefender, AttackTroopCount = 15 }); // 防守方Territory中订阅事件 private void Start() { GameEventCenter.OnAttackLaunched += HandleBeAttacked; TransitionToState(new TerritoryIdleState(this)); } private void HandleBeAttacked(object sender, AttackEventArgs args) { if (args.Defender == this) { // 切换到被攻击状态处理防守逻辑 TransitionToState(new UnderAttackState(this, args.Attacker, args.AttackTroopCount)); } } // 销毁时取消订阅避免内存泄漏 private void OnDestroy() { GameEventCenter.OnAttackLaunched -= HandleBeAttacked; }
- 优势:完全解耦攻防双方,不需要互相持有引用,后续扩展UI提示、战报记录等监听逻辑不需要修改原有领地代码
- 注意:对象销毁时必须取消事件订阅,避免内存泄漏
方案3:全局GameManager中介
- 实现单例GameManager统一存储所有领地实例,进攻方需要交互时直接从GameManager中查询获取目标防守方实例
- 代码示例:
public class GameManager : MonoBehaviour { public static GameManager Instance; // 存储所有领地,Key可使用领地ID/坐标 public Dictionary<int, Territory> AllTerritoryDict; private void Awake() { Instance = this; } public Territory GetTerritoryById(int territoryId) { AllTerritoryDict.TryGetValue(territoryId, out var territory); return territory; } } // AttackState中调用 var defender = GameManager.Instance.GetTerritoryById(targetDefenderId); if (defender != null) { defender.TakeDamage(_attacker, attackTroop); }
- 优势:适合需要批量操作领地、或多次查询不同目标的场景,统一管理所有领地生命周期
- 注意:控制GameManager的职责范围,不要塞入过多无关逻辑
方案4:Unity内置消息系统
- 使用Unity自带的
SendMessage方法,获取防守方GameObject后直接调用其暴露的被攻击方法 - 代码示例:
// 防守方Territory中暴露被攻击接口 public void OnBeAttacked(Territory attacker, int attackTroop) { TransitionToState(new UnderAttackState(this, attacker, attackTroop)); } // 进攻方调用 defenderObj.SendMessage("OnBeAttacked", new object[] { _attacker, attackTroop }, SendMessageOptions.DontRequireReceiver);
- 优势:无需额外实现中间件,代码量小
- 劣势:方法名用字符串绑定,重构容易出错,性能较差,仅适合小型项目使用
内容的提问来源于stack exchange,提问作者Yeleywale
相关产品推荐
相关产品推荐

