Unity:为啥添加事件侦听器仅在唤醒功能中起作用?

Posted

技术标签:

【中文标题】Unity:为啥添加事件侦听器仅在唤醒功能中起作用?【英文标题】:Unity: Why Adding an Event Listener work only in Awake function?Unity:为什么添加事件侦听器仅在唤醒功能中起作用? 【发布时间】:2017-10-19 01:38:51 【问题描述】:

我想了解更多关于以下场景的信息。我有一个简单的基类,它有一个 Button 类型的公共变量。在我的 Start 函数中,我使用了 Button 的 click 事件并为其添加了一个监听器。但是,代码根本不会执行,它工作的唯一方法是如果事件侦听器被挂在 Awake 函数而不是 Start 函数中。

事件不会在以下情况下执行:

 protected virtual void OnExitedUI()
    
        if(ExitedUI != null)
         ExitedUI(this.gameObject); 
    

    [Tooltip("The Button script/GameObject this UI uses as an Exit button. Used to destroy the UI and fire the exit event.")]
    public Button exitButton;

    // Subscribe to our events at the start.
    private void Start()
    
        exitButton.onClick.AddListener(() =>  OnExitClick(); );
    

    // Event handler for the exit button click event.
    // Fire our ExitedUI event and destroy the object.
    private void OnExitClick()
    
        Debug.Log("Exited UI!");
        OnExitedUI();
        Destroy(this.gameObject);
    

事件按预期执行和工作。请注意,唯一改变的是 Start to Awake。

 protected virtual void OnExitedUI()
    
        if(ExitedUI != null)
         ExitedUI(this.gameObject); 
    

    [Tooltip("The Button script/GameObject this UI uses as an Exit button. Used to destroy the UI and fire the exit event.")]
    public Button exitButton;

    // Subscribe to our events at the start.
    private void Awake()
    
        exitButton.onClick.AddListener(() =>  OnExitClick(); );
    

    // Event handler for the exit button click event.
    // Fire our ExitedUI event and destroy the object.
    private void OnExitClick()
    
        Debug.Log("Exited UI!");
        OnExitedUI();
        Destroy(this.gameObject);
    

我知道 Awake 函数是在 Start 函数之前调用的。但我看不出这对流程有何影响 - 事实上,我认为 Start 方法应该会更好地工作,因为我可以确定 Button 已正确初始化或与此脚本一起实例化。

附加说明:上面的脚本是我用于 UI 游戏对象的 UI 管理器类的基础,它包含一组面板和 UI 元素。其中一个子面板包括上面提到的退出按钮。

【问题讨论】:

我怀疑 Start 函数甚至没有被调用。使用Debug.Log 来验证这一点。 你的派生类是否也实现了 Start()?如果是这样,您也有责任调用基类的 Start() 方法。 Unity 只会调用最派生的 Start() 实现。此外,您可能会考虑根本不使用继承,而是使用组合(使您的共享代码成为自己的脚本组件)。 【参考方案1】:

在我的 Start 函数中,我使用了 Button 的点击事件并为其添加了一个监听器。

这是解决您问题的关键。

如果你想执行监听器的代码,你需要在执行事件之前添加监听器。否则委托不能执行它,因为它不知道它必须执行什么。

当你添加一个监听器并在Start() 中执行时,你不能保证添加会首先被调用。调用所有相同事件(如 AwakeStart)的顺序未确定,并且可能会更改。但是任何Awake 总是在任何Start 之前调用。所以如果你想在Start中执行事件,你必须在Awake中添加监听器。

另外,正如程序员在评论中所说,Start 并不总是被调用。如果在场景开始播放时禁用 GameObject,则在启用该对象之前不会调用 Start,但始终会调用 Awake

另外,正如 PMV 在他的评论中所说,在继承类中使用 Start 将阻止在基类中调用 Start。在这种情况下,您应该收到有关您的方法隐藏继承方法的警告。如果您没有收到该警告,则问题不在这里。顺便说一句,如果需要添加一些功能,最好在基类中将StartAwake 等方法标记为virtual,并在继承类中覆盖它。

【讨论】:

他的 Start()Awake() 方法被标记为 private 这将它们从 IDE 中排除了有关隐藏基类方法的警告,但由于 Unity 使用反射巫术来调用这些方法,基类方法仍然被隐藏。我总是将我的 Unity 关键字方法标记为 public,因为我知道它们将被外部调用并避免此问题。 @Draco18s protected 而不是 public 使您的对象对其他类具有更清晰的 API 服务,并且您仍然可以获得派生类能够看到它的好处。 @ScottChamberlain 确实如此。 知道了!也非常感谢其他提示,尤其是我忽略了继承类中的 Start 方法交互这一事实。

以上是关于Unity:为啥添加事件侦听器仅在唤醒功能中起作用?的主要内容,如果未能解决你的问题,请参考以下文章

Javascript仅在html文档中起作用[重复]

为啥 L1 正则化在机器学习中起作用

android - 加速仅在调试中起作用

为啥这个 JS/jQuery 作用于多个输入?

editText.setText仅在一个活动中起作用

为啥我的动作脚本事件没有触发?