为啥我的非静态游戏对象在场景重新加载后会在脚本中丢失其引用?

Posted

技术标签:

【中文标题】为啥我的非静态游戏对象在场景重新加载后会在脚本中丢失其引用?【英文标题】:Why does my non-static GameObject lose its reference in a script after scene reloaded?为什么我的非静态游戏对象在场景重新加载后会在脚本中丢失其引用? 【发布时间】:2020-12-04 22:42:13 【问题描述】:

我的 Canvas 上有一个空的 GameObject,用于显示广告菜单,我已通过检查器将其附加到公共变量中的单独脚本(而不是菜单本身)。

我在使用adMenu.SetActive(false) 的脚本中将其设置为非活动状态,这适用于我的游戏的第一次通关。但是,当我通过场景中的按钮重新启动场景时,菜单失去了对检查器中相同游戏对象的引用,并且我收到此错误:

MissingReferenceException: The object of type 'GameObject' has been destroyed but you are still trying to access it.

在场景重新加载后以类似方式初始化其他游戏对象时,我从未遇到过这种情况。

 

其他细节:

GameObject.Find() 可以在同一脚本中使用其名称检索游戏对象 DontDestroyOnLoad() 不会在脚本或它所附加到的 GameObject 上的任何地方使用

 

代码:

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Advertisements;
using MEC;

public class AdManager : MonoBehaviour, IUnityAdsListener

    internal static AdManager instance;
    private static bool isInitialized = false;

    public GameObject adMenu;
    private string placement = "rewardedVideo";

    void Start()
    
        instance = this;
        if (!isInitialized)
        
            isInitialized = true;
            Advertisement.AddListener(this);
            Advertisement.Initialize(Constants.appleGameId, true);
        
    

    IEnumerator<float> ShowAd()
    
        if (!Advertisement.IsReady())
        
            yield return Timing.WaitForOneFrame;
        
        Advertisement.Show(placement);
    

    public void CloseAdMenu()
    
        Debug.Log("Is adMenu null: " + (adMenu == null));  // Returns false on first playthrough only
        adMenu.SetActive(false);
    


    public void OnUnityAdsDidFinish(string placementId, ShowResult showResult)
    
        if (showResult == ShowResult.Finished)
        
            CloseAdMenu();
        
    

    public void OnUnityAdsReady(string placementId)
    
        // throw new System.NotImplementedException();
    

    public void OnUnityAdsDidError(string message)
    
        // throw new System.NotImplementedException();
    

    public void OnUnityAdsDidStart(string placementId)
    
        // throw new System.NotImplementedException();
    

【问题讨论】:

我会检查由单例模式引起的流量。静态实例可能仍指向上一个场景中的 AdManager 实例。即使更新了静态引用,调用 CloseAdMenu() 的事件(可能是 OnUnityAdsDidFinish)也可能在 AdManager.Start() 之前调用,从而导致在更新为当前场景之前使用的对先前场景的静态引用。一般来说,在 Awake() 中连接所有依赖项以最大限度地降低这种风险,或者,甚至尽量避免单例模式。 【参考方案1】:

发生的事情与您的菜单对象或 static 实例无关。

问题是回调

public void OnUnityAdsDidFinish(string placementId, ShowResult showResult)

因为您通过

注册了实例
Advertisement.AddListener(this);

this 实例在场景改变后将成为毁灭者。

如the examples所示,你应该这样做

private void OnDestroy() 

    Advertisement.RemoveListener(this);

【讨论】:

这行得通,谢谢,但是为什么错误回溯指向我的adMenu.SetActive(false) 行,而不是甚至没有运行OnUnityAdsDidFinish()【参考方案2】:

在你的重启方法上,你应该用adMenu.SetActive(true)重新激活按钮,如果没有,当你再次调用场景时,adMenuGameObject被禁用,所以你不能访问GameObject

也许您可以在 AdManager 中添加一个方法,例如:

public void OpenAdMenu()

    adMenu.SetActive(true);

然后拨打AdManager.Start()

【讨论】:

以上是关于为啥我的非静态游戏对象在场景重新加载后会在脚本中丢失其引用?的主要内容,如果未能解决你的问题,请参考以下文章

加载新场景时如何销毁标记为“DontDestroyOnLoad”的游戏对象?

如何通过脚本在 Unity 中重新定位和调整游戏对象的大小?

unity为啥不能加载脚本

为啥在 Django 中更新我的 HTML 之前必须重新加载两次?

unity中怎样重新开始游戏?

(统一)销毁当前场景中的游戏对象并在加载当前场景后使其永久销毁?