跳到主要内容

管理事件生命周期与递归触发

TinyGiants
Unity Games & Tools Developer

检查每个监听器和调度任务由谁负责清理,场景对象禁用时移除自己的回调,回调如果还能触发其他事件,就沿连接检查是否会回到源事件

让订阅跟着组件的生命周期​

如果面板打开两次后回调也执行两次,检查注册和解绑的位置

在 OnEnable 注册,在 OnDisable 解绑,让面板只在启用时监听

给 Int32GameEvent 注册 Lambda 时,把委托存成 UnityAction<int>

using TinyGiants.GES.Runtime;
using UnityEngine;
using UnityEngine.Events;

public sealed class HealthDisplay : MonoBehaviour
{
[GameEventDropdown, SerializeField] private Int32GameEvent onHealthChanged;

private UnityAction<int> _listener;

private void OnEnable()
{
_listener = health => Debug.Log($"Health: {health}");
onHealthChanged.AddListener(_listener);
}

private void OnDisable()
{
onHealthChanged.RemoveListener(_listener);
_listener = null;
}
}

用与注册类型对应的方法解绑自己的回调:RemoveListener、RemovePriorityListener、RemoveConditionalListener 或 RemovePersistentListener

RemoveAllListeners() 会清除该事件上的全部 Basic、Priority 和 Conditional 回调,包括其他组件注册的回调

需要跨场景监听的服务可以调用 AddPersistentListener,并让服务对象跨场景保留

服务停用或销毁时移除它注册的回调

场景卸载时,普通事件会清除 Basic、Priority 和 Conditional 监听器

事件资产的 Persistent 设置让这些注册跨场景保留,AddPersistentListener 则把单个回调注册到 Persistent 阶段

如果项目自定义的静态状态残留到下一次 Play,在 RuntimeInitializeLoadType.SubsystemRegistration 重置它

关闭 Domain Reload 时,这个入口也会执行

GES 在这个入口重置事件缓存

更新状态后再通知​

生命值变化的监听器如果再次触发同一个事件,可能同步调用自己

把扣血请求和血量通知分成两个事件

在处理扣血请求时写入新生命值,再通知界面读取

private void ApplyDamage(int amount)
{
int next = Mathf.Max(0, health - Mathf.Max(0, amount));
if (next == health)
return;

health = next;
onHealthChanged.Raise(health);
}

HUD 订阅 onHealthChanged,收到通知后按新数值刷新

如果某个反应还会再次造成伤害,用玩法条件或另一条事件路径结束反馈循环

复现重复触发时,在 Game Event Monitor 核对事件顺序,再到 Warnings 查看已记录的警告

Monitor 的 Warnings 列表

保存和取消计划任务​

RaiseDelayed 和 RaiseRepeating 会返回 ScheduleHandle

保存启动任务时拿到的句柄

以中毒为例,重新施加前取消旧任务,效果结束或组件禁用时也取消

这里 onPoisonTick 是 Int32GameEvent,repeatCount: -1 会重复到取消为止

private ScheduleHandle _poison;

private void StartPoison()
{
StopPoison();
_poison = onPoisonTick.RaiseRepeating(10, interval: 1f, repeatCount: -1);
}

private void StopPoison()
{
_poison?.Cancel();
_poison = null;
}

private void OnDisable() => StopPoison();

组件有多项计划任务时,保存每个句柄,对自己创建的任务调用 Cancel()

对事件本身调用 Cancel() 管理的是 Inspector 中配置的派发计划

显式调用 RaiseDelayed 或 RaiseRepeating 创建的任务用句柄取消

降低高频条件的开销​

事件进入 Conditional 阶段时会检查条件

位置更新事件频繁触发时,可以按固定间隔更新附近是否有敌人的标记,让条件直接读标记

这里 onPositionUpdate 是 Vector3GameEvent,条件接收一个 Vector3

private bool _hasNearbyEnemies;

private void FixedUpdate()
{
_hasNearbyEnemies = Physics.CheckSphere(transform.position, 10f, enemyLayer);
}

private void OnEnable()
{
onPositionUpdate.AddConditionalListener(
HandlePosition, condition: _ => _hasNearbyEnemies);
}

private void OnDisable()
{
onPositionUpdate.RemoveConditionalListener(HandlePosition);
}

批处理时暂停 Inspector 动作​

如果这个事件平时启用 Inspector 动作,批处理期间可以暂时关闭它们,在 finally 恢复

C# 监听器和已注册的 Trigger、Chain 连接仍会执行

需要刷新一次汇总时,在批处理结束后发出完成事件

onItemsProcessed.SetInspectorListenersActive(false);
try
{
foreach (int itemCount in batches)
onItemsProcessed.Raise(itemCount);
}
finally
{
onItemsProcessed.SetInspectorListenersActive(true);
}

onBatchFinished.Raise();

🚀 全球开发者服务矩阵

🇨🇳 国区开发者社区

🌐 全球开发者社区

📧 支持与合作