You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 12:30:19