Unity中C#泛型类未订阅SC_Axe类事件的问题求助
问题分析与解决方案
问题根源
你的代码中,SC_Coin和SC_Axe的OnCollision都是静态事件,但泛型类CollectableManager<T>在调用AddEventHandler时错误传入了this(当前Manager实例)。静态事件属于类本身而非实例,订阅静态事件时必须传入null作为第一个参数——这就是斧头事件订阅失败的核心原因。硬币看似正常只是巧合,其订阅逻辑本质上同样存在问题。
修复后的代码
修改CollectableManager.cs
using System; using System.Reflection; using UnityEngine; public class CollectableManager<T> : MonoBehaviour where T : MonoBehaviour { protected int count = 0; protected virtual void OnEnable() { EventInfo eventInfo = typeof(T).GetEvent("OnCollision"); if (eventInfo != null) { Delegate handler = Delegate.CreateDelegate(eventInfo.EventHandlerType, this, "OnCollect"); // 根据事件是否为静态,传入正确的目标参数 object eventTarget = eventInfo.IsStatic ? null : this; eventInfo.AddEventHandler(eventTarget, handler); } } protected virtual void OnDisable() { EventInfo eventInfo = typeof(T).GetEvent("OnCollision"); if (eventInfo != null) { Delegate handler = Delegate.CreateDelegate(eventInfo.EventHandlerType, this, "OnCollect"); object eventTarget = eventInfo.IsStatic ? null : this; eventInfo.RemoveEventHandler(eventTarget, handler); } } protected virtual void OnCollect() { count++; UpdateUI(); } protected virtual void UpdateUI() { } }
优化事件触发逻辑(SC_Coin.cs和SC_Axe.cs)
为避免空引用异常,在触发事件前添加空检查:
// SC_Coin.cs修改后的触发逻辑 private void OnTriggerEnter2D(Collider2D other) { if (other.CompareTag("Player")) { OnCollision?.Invoke(); // 使用空条件运算符避免空引用 Destroy(gameObject); } }
// SC_Axe.cs修改后的触发逻辑 private void OnTriggerEnter2D(Collider2D other) { if (other.CompareTag("Player")) { OnCollision?.Invoke(); // 使用空条件运算符避免空引用 Destroy(gameObject); } }
额外说明
- 静态事件的订阅/取消订阅不需要实例,因此必须传入
null作为目标对象;实例事件则需要传入对应的实例。 - 使用
?.Invoke()替代直接调用事件,是Unity开发中处理事件的最佳实践,能有效避免无订阅者时的空引用崩溃。
内容的提问来源于stack exchange,提问作者Gilad Meir
相关产品推荐
相关产品推荐

