Unity中Scriptable Object事件无法注册监听器(类型记录不匹配)
ScriptableObject事件系统注册监听器后列表元素显示为null的问题
我正在跟随教程使用ScriptableObject实现事件系统,编写了两个与教程一致的脚本(Unity版本2021.3.25)。已经完成脚本挂载、事件创建及引用设置,但在RegisterListener方法中,第一个Debug.Log(listener.GetType())能正确打印出listener的类型为GameEventListener,但将其添加到列表后,第二个Debug.Log(listeners[0])却显示元素为null,最终导致类型不匹配问题。尝试将列表设为public也无法解决,请问问题出在哪里?
相关脚本
Script 1: GameEvent.cs
[CreateAssetMenu(menuName = "GameEvent")] public class GameEvent : ScriptableObject { private List<GameEventListener> listeners = new List<GameEventListener>(); public void Raise() { Raise(null, null); } public void Raise(Component sender, object data) { for (int i = 0; i < listeners.Count; i++) { listeners[i].OnEventRaised(sender, data); } } public void RegisterListener(GameEventListener listener) { Debug.Log(listener.GetType()); if (!listeners.Contains(listener)) { listeners.Add(listener); } Debug.Log(listeners[0]); } public void UnregisterListener(GameEventListener listener) { if (listeners.Contains(listener)) { listeners.Remove(listener); } } }
Script 2: GameEventListener.cs
[Serializable] public class CustomUnityEvent : UnityEvent<Component, object> { } public class GameEventListener : MonoBehaviour { public GameEvent gameEvent; public CustomUnityEvent response; private void OnEnable() { gameEvent.RegisterListener(this); } private void OnDisable() { gameEvent.UnregisterListener(this); } public void OnEventRaised(Component sender, object data) { response.Invoke(sender, data); } }
解决方案
1. 排查ScriptableObject实例重复问题
Unity中ScriptableObject若被不当引用,可能生成多个实例副本,导致监听器被加到非目标实例的列表中。在GameEvent类中添加Awake方法,打印实例ID确认:
private void Awake() { Debug.Log($"GameEvent实例ID: {this.GetInstanceID()}"); }
如果注册时出现不同的InstanceID,说明存在多实例问题,需确保所有GameEventListener引用的是同一个GameEvent资源文件。
2. 修复列表遍历与空值保护
当前Raise方法的正向遍历在监听器被移除时可能引发索引错误,改为反向遍历并添加空值保护:
public void Raise(Component sender, object data) { for (int i = listeners.Count - 1; i >= 0; i--) { listeners[i]?.OnEventRaised(sender, data); } }
3. 避免Unity序列化干扰
给列表添加[NonSerialized]标记,明确告知Unity不要序列化该运行时字段,防止序列化系统干扰列表内容:
[NonSerialized] private List<GameEventListener> listeners = new List<GameEventListener>();
4. 验证引用赋值时机
在GameEventListener的OnEnable方法中添加空值检查,确保gameEvent引用已正确赋值:
private void OnEnable() { if(gameEvent != null) { gameEvent.RegisterListener(this); } else { Debug.LogError("GameEvent引用未赋值!", this); } }
若出现错误日志,需重新检查Inspector面板中的引用设置。
内容的提问来源于stack exchange,提问作者Ryan Hibbs
相关产品推荐
相关产品推荐

