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

Unity使用C#开发时事件监听器未正常触发问题排查

事件监听不触发的核心原因

你对Unity生命周期的时序认知存在偏差:Unity只保证同一个GameObject上挂载的单个脚本的Awake执行早于自身的Start,完全不保证跨GameObject、跨加载时机的脚本执行顺序,你遇到的问题基本都是时序错位导致的:

  • 最高概率诱因:如果挂载BirdPigeon的对象是场景加载完成后通过Instantiate动态生成的,场景内默认存在的GameController/BattleRandomController会在场景加载后的第一帧跑完Start、触发对应事件;此时动态生成的鸽子对象还没完成实例化,根本没走到Awake的监听注册步骤,事件触发时没有任何订阅者,回调自然不会执行。
  • 次常见诱因:如果BirdPigeon挂载的GameObject初始状态是SetActive(false),禁用状态下的脚本不会执行Awake,等你后续激活对象完成监听注册时,事件早就触发完了。
  • 易忽略诱因:静态Action不会随场景卸载自动清空订阅列表,如果你之前写过残留的取消订阅、重置事件的逻辑,很可能导致当前场景的订阅关系错乱。
可直接落地的修复方案

方案1(最推荐,彻底规避时序问题)

不要用一次性事件传递初始化状态,直接做全局静态状态标记,不存在错过时机的问题:

// 全局状态类不需要挂载到任何GameObject,直接全局访问
public static class GlobalGameState
{
    public static bool IsSinglePlayer;
}

// 单人模式控制器修改
public class GameController : MonoBehaviour
{
    void Start()
    {
        GlobalGameState.IsSinglePlayer = true;
    }
}

// 多人模式控制器修改
public class BattleRandomController : MonoBehaviour
{
    void Start()
    {
        GlobalGameState.IsSinglePlayer = false;
    }
}

BirdPigeon里直接在Start阶段读取状态执行对应逻辑即可,完全不需要注册事件:

void Start()
{
    if (GlobalGameState.IsSinglePlayer)
    {
        SinglePlayer();
    }
    else
    {
        MultiPlayer();
        // 原有多人模式下的PhotonView判断逻辑保留
        if (PV.IsMine)
            IndicatorForMe.SetActive(true);
        else
            IndicatorForMe.SetActive(false);
    }
}

方案2(保留事件模式,兼容现有逻辑)

把普通静态Action改成带状态缓存的事件,新对象注册监听时如果事件已经触发过,直接立刻执行回调,从机制上避免错过时机:

public class GameController : MonoBehaviour
{
    private static bool _singleModeEventFired = false;
    private static Action _singlePlayerEvent;
    public static Action isSinglePlayer
    {
        add
        {
            _singlePlayerEvent += value;
            // 注册时如果事件已经触发过,直接执行回调
            if (_singleModeEventFired) value?.Invoke();
        }
        remove
        {
            _singlePlayerEvent -= value;
        }
    }

    void Start()
    {
        _singleModeEventFired = true;
        _singlePlayerEvent?.Invoke();
    }

    // 场景卸载时重置状态,避免跨场景污染
    void OnDestroy()
    {
        _singleModeEventFired = false;
        _singlePlayerEvent = null;
    }
}

多人模式的isMultiPlayer事件按照完全相同的逻辑改写即可。

方案3(临时调试用,治标不治本)

打开Project Settings -> Script Execution Order,手动设置脚本执行优先级:把BirdPigeon的执行顺序数值调得比GameController、BattleRandomController更小(Unity中执行顺序数值越小,脚本生命周期跑的越早),保证场景初始存在的BirdPigeon的Awake一定在另外两个脚本的Start之前执行完。注意这个方案解决不了动态生成对象、初始禁用对象的时序问题。

额外排查点
  • 检查BirdPigeon挂载的GameObject初始是否为禁用状态,禁用对象上的脚本不会执行Awake,无法注册监听。
  • 全局搜索有没有代码在事件触发前意外给GameController.isSinglePlayer/BattleRandomController.isMultiPlayer赋值null,清空了已注册的订阅列表。
  • 检查BirdPigeon脚本上有没有写OnDisable/OnDestroy逻辑,在事件触发前就取消了订阅。

内容的提问来源于stack exchange,提问作者chethanv77777

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.01 04:12:30