Unity事件调用时重复获取历史数据的解决方法咨询
Unity事件重复触发历史数据问题解决
我在Unity中通过事件实现类间数据传递,关联事件的所有GameObject都会接收数据。目前遇到异常:每次调用事件时,订阅者会同时收到历史所有调用的数据。
比如在回合制战斗场景中,单个订阅者会对每一个过往回合执行一次事件逻辑,每次都携带对应回合的EventArgs值。就像回合1行动3时,明明只触发了MultiAttack,却同时收到了当前回合的AlliedTarget、上一回合的MultiTarget、两回合前的OpposingTarget数据,导致曾经成为攻击目标的卡牌后续一直被判定为目标。
调试日志截图:
订阅该事件的函数代码:
void OnAttackable(object sender, EventArgs e) { ActionSO action = new(((GlobalCombatClock.CustomActionEventArg)e).Action); bool turnCharacterAllied = GlobalCombatClock.GetTurnCharacter().GetComponent<ObjectCombatManager>().IsAllied; bool cardAllied = card.GetComponent<ObjectCombatManager>().IsAllied; Debug.Log(GlobalCombatClock.RoundCount + " " + GlobalCombatClock.InitiativeIndex + " " + turnCharacterAllied + cardAllied + action.TargetType.ToString()); if(action.TargetType switch { GlobalResources.Targets.singleOpposingTarget => turnCharacterAllied != cardAllied, GlobalResources.Targets.singleAlliedTarget => turnCharacterAllied == cardAllied, GlobalResources.Targets.multiTarget => turnCharacterAllied != cardAllied, GlobalResources.Targets.allies => turnCharacterAllied == cardAllied, GlobalResources.Targets.enemies => turnCharacterAllied != cardAllied, GlobalResources.Targets.self => card.Equals(GlobalCombatClock.GetTurnCharacter()), _ => false }) AttackableVanity(action); }
可行解决方案
- 杜绝重复订阅:
确保战斗成员的事件订阅只执行一次,比如放在Awake或Start中完成,不要在每回合初始化、Update这类重复调用的方法里订阅。如果对象是复用的(比如对象池),一定要在对象回收时取消订阅,下次激活时再重新订阅。 - 采用弱事件模式:
若订阅者对象已销毁但未取消订阅,会导致事件委托中存在无效引用,引发异常或重复触发。可以用Unity自带的UnityEvent替代普通.NET事件,或者手动实现弱引用的事件订阅逻辑。 - 重置事件委托:
每回合结束或事件触发完成后,清空事件委托引用(比如YourEvent = null;),但要注意先确保所有需要取消订阅的操作都已完成,避免遗漏新的订阅。 - 避免复用EventArgs实例:
检查CustomActionEventArg是否是全局复用的对象,如果每次触发事件都修改同一个实例的数据,会导致旧订阅者拿到历史数据。应该每次触发事件时创建新的EventArgs实例。 - 添加事件唯一标识校验:
在CustomActionEventArg中加入回合数、行动索引等唯一标识,订阅者方法执行前先判断当前事件是否属于当前回合,不符合则直接跳过逻辑。
内容的提问来源于stack exchange,提问作者ShaharKarisi
相关产品推荐
相关产品推荐

