Unity 6中Scriptable Object事件系统重启编辑器后失效求助
基于Scriptable Object的事件系统重启Unity后失效问题排查与解决
问题描述
参照YouTube教程实现的Scriptable Object事件系统,初始运行正常,但重启Unity编辑器后监听器完全失效。复制带监听器的游戏对象可在当前会话恢复功能,但保存重启后问题复发。已尝试删除Libraries文件夹、重置偏好设置、重写脚本等操作,仅能暂时缓解。添加Debug.Log后确认事件已触发,但监听器的OnEventRaised方法未执行。当前使用Unity 6000.0.43f1,此前6000.0.39f1版本也存在相同问题。
相关代码
GameEventListener 脚本
using UnityEngine; using UnityEngine.Events; public class GameEventListener : Activation { [SerializeField] GameEventAsset gameEvent; [SerializeField] UnityEvent onEventTriggered; public GameEventAsset GameEvent { get { return gameEvent; } set { gameEvent = value; } } public UnityEvent OnEventTriggered { get { return onEventTriggered; } set { onEventTriggered = value; } } private void OnEnable() { Debug.Log("OnEnable Event"); gameEvent.AddListener(this); } private void OnDisable() { gameEvent.RemoveListener(this); onEventTriggered = null; } public void OnEventRaised() { Debug.Log("OnEventRaised"); onEventTriggered.Invoke(); } }
GameEventAsset Scriptable Object 脚本
using System.Collections.Generic; using UnityEngine; [CreateAssetMenu(menuName = "ScriptableObjects/GameComponent/Event/This is GameDev SO Event System")] public class GameEventAsset : ScriptableObject { [SerializeField] List<GameEventListener> listeners = new List<GameEventListener>(); public void AddListener(GameEventListener listener) { Debug.Log(listener); if (listener != listeners.Contains(listener)) { Debug.Log(listener + " Is listener"); listeners.Add(listener); } } public void RemoveListener(GameEventListener listener) { listeners.Remove(listener); } public void TriggerEvent() { Debug.Log(listeners.Count + " Trigger Event Start"); for (int i = 0; i < listeners.Count; i++) { Debug.Log(listeners[i] + " Trigger Event Engaged"); listeners[i].OnEventRaised(); } } }
故障原因
- 序列化导致的无效引用:
GameEventAsset中的listeners列表被标记为[SerializeField],Unity会序列化保存该列表的引用。但重启Unity后,场景中的监听器对象是新实例,原序列化的引用指向旧对象,导致调用OnEventRaised时无响应。 - AddListener逻辑错误:
if (listener != listeners.Contains(listener))的判断逻辑完全错误——Contains返回布尔值,与listener对象比较永远为true,会导致重复添加监听器,甚至在引用无效时引发异常。 - OnDisable中的错误操作:
OnDisable中执行onEventTriggered = null;会清空UnityEvent的绑定,重启后序列化的事件绑定丢失,导致即使监听器注册成功也无法触发回调。
解决方法
移除listeners的序列化标记
修改GameEventAsset中的列表定义,避免Unity序列化运行时的监听器列表:List<GameEventListener> listeners = new List<GameEventListener>();修复AddListener判断逻辑
将错误的判断替换为正确的存在性检查:public void AddListener(GameEventListener listener) { Debug.Log(listener); if (!listeners.Contains(listener)) { Debug.Log(listener + " Is listener"); listeners.Add(listener); } }删除OnDisable中的onEventTriggered置空操作
保留监听器移除逻辑即可,防止清空序列化的UnityEvent绑定:private void OnDisable() { if (gameEvent != null) { gameEvent.RemoveListener(this); } // 移除 onEventTriggered = null; 这一行 }添加空引用检查与遍历优化
- 在
GameEventListener.OnEnable中添加空引用判断,避免空指针异常:private void OnEnable() { Debug.Log("OnEnable Event"); if (gameEvent != null) { gameEvent.AddListener(this); } } - 在
GameEventAsset.TriggerEvent中复制列表后遍历,防止遍历中列表变更引发异常:public void TriggerEvent() { Debug.Log(listeners.Count + " Trigger Event Start"); // 复制列表避免遍历过程中列表修改导致的异常 var tempListeners = new List<GameEventListener>(listeners); foreach (var listener in tempListeners) { if (listener != null) { Debug.Log(listener + " Trigger Event Engaged"); listener.OnEventRaised(); } } }
- 在
内容的提问来源于stack exchange,提问作者Samuel Ronan
相关产品推荐
相关产品推荐

