当场景被加载时协程出现异常行为

3

好的,我有这个协程:

 IEnumerator ShowCharTraits()
    {

        while(!hasPlayerChosen)
        {
            yield return null;
            traitPanel.SetActive(true);
        }
        hasPlayerChosen = false;
        traitPanel.SetActive(false);
    //    Debug.Log("Got called! job done");

    }

这是从我的GameManager的唤醒方法中调用的:

players = GameObject.FindGameObjectsWithTag("Player");
                foreach (GameObject g in players)
                {
                    ui_Controller.StartShowCharTraits();
                    g.GetComponent<PlayerToken>().isTurn = false;
                }

StartShowCharTraits() 是一个简单的方法,它可以执行以下操作:

  public void StartShowCharTraits()
    {
        StartCoroutine("ShowCharTraits");
    }

现在,我已经检查了标签,没有空引用异常,实际上没有任何错误或警告被抛出。如果我在编辑器中加载场景,然后播放它,一切都正常。 traitPanel.SetActive(true); 被调用并且我的面板显示出来了。然而,当我使用 SceneManager.LoadScene(1); 从另一个场景加载我的场景时,上述代码行永远不会到达。有什么想法为什么会发生这种情况吗?


你能告诉我哪一行代码没有被调用吗?你至少可以使用debug.log来检查协程是否正常工作。 - LumbusterTick
我可以告诉你哪一行代码没有被调用。而且我已经回答了自己的问题。问题可能出现在不同的地方。 - Uri Popov
我为您@uripopov准备了一个令人兴奋的答案! - Fattie
2个回答

3

假设你想在Unity项目中创建一个“类似于单例”的中心位置。例如:

SoundEffects
LapTimer
Scores
SocialMediaConnections

你所要做的就是:
  1. 新建一个名为 SoundEffects.cs 的脚本文件

  2. 记得每个Unity项目都必须有一个“preload”场景(不可能没有),在这个场景中创建一个名为“holder”的空游戏对象,并确保将其标记为“DontDestroyOnLoad”。

  3. 将 SoundEffects.cs 附加到这个 holder 上。

你已经完成了。

就是这么简单。

没有更多的操作了。

如果某个特定脚本附加在任何特定的对象和场景中,它可能需要访问“SoundEffects”...

要这样做,只需在 Awake 中执行以下操作:

 SoundEffects soundEffects = Object.FindObjectOfType<SoundEffects>();

在那个脚本中只需使用 soundEffects,例如 soundEffects.PlayBoom(13) 等等。

因此,在 Awake() 中:

  Laps laps = Object.FindObjectOfType<Laps>();
  Social social = Object.FindObjectOfType<Social>();
  AI ai = Object.FindObjectOfType<AI>();

或者其他什么。

Unity很简单。 非常简单。 就是那么容易。

  1. 编写你的脚本,LapTimer.cs或其他名称

  2. 将其放置在预加载场景中的一个持有对象上(否则它在哪里?)

没有“3”,就这么简单。 简直太简单了。

请注意...

在Unity的早期,某个人不幸地在QA论坛上提到了“单例”(在ECS系统中无法拥有“单例”,这是毫无意义的)。 当时,这引起了关于如何在Unity中制作“类似单例的东西”的大讨论,这使得混淆更加严重。不幸的是,从那天起,你会看到学习Unity的人在网络上看到“单例”一词,导致无尽的困惑。

再次注意,在Unity中,“管理器”的概念非常简单 - 如上所述。 这很微不足道。 它简直不能再简单了。


请注意,我提到您可能希望使用语法糖来避免输入Laps laps = Object.FindObjectOfType<Laps>();这种难以置信的繁琐操作。

非常不幸地,在Unity / c#中没有宏,因此您需要另一种方法。

你所要做的就是使用一个在Unity中流行多年的“网格”脚本http://answers.unity3d.com/answers/1124859/view.html

但是,老实说,使用Scores scores = Object.FindObjectOfType<Scores>();非常简单,我现在通常只是这样做。


感谢您一如既往的出色回答。我已经在做这个了。我有一个对象在我的第一个场景中是DontDestroyOnLoad()。问题出在附加到它上面的Dataholder会查看是否有其他实例并在进入场景时将其销毁。这导致另一个持有特定场景脚本的对象被销毁,因为不幸的是它有一个来自测试的DataHolder实例附加到它上面。所以发生的事情是一个脚本的Awake()正在调用角色的协程,而当DataHodler的Awake()运行时,整个对象就被删除了。 - Uri Popov
我的主菜单场景会这样做。问题是我在另一个场景的游戏对象上忘记了一个DataHolder组件。那个游戏对象正在被来自主菜单场景的DataHolder销毁。同样的游戏对象帮助脚本为场景服务。这就是为什么一切都崩溃了。 - Uri Popov
来回切换主菜单并不是问题所在。我已经通过更改DataHolder的Awake()方法解决了我的问题。我不知道如何更好地解释发生了什么。 - Uri Popov
让我们在聊天中继续这个讨论 - Uri Popov

-1

好的,我该如何解释呢。我有一个充当数据持有者的单例。在开发游戏管理器场景时,我将单例附加到了包含一堆脚本的游戏管理器对象上。现在,当我制作主菜单时,我当然也将我的单例添加到其中,以便我可以将信息传递到游戏场景中,但由于这个原因,整个游戏管理器对象都被删除了。这是DataHolder的罪魁祸首:

 void Awake()
    {
        if (instance == null)
            instance = this;
        else if (instance != this)
            Destroy(gameObject);//This right here.

        DontDestroyOnLoad(gameObject);
    }

所以我把那行代码改成了Destroy(gameObject.GetComponent(instance.GetType()));


单例模式?Unity里面没有单例模式,伙计。你实际上不能使用它们。 - Fattie
在C#中,你可以做什么?我在我的MusicController中使用了一个。http://ghettocode.blogspot.com/2016/04/creating-musiccontroller-module-in.html - apollosoftware.org
@JoeBlow 等等,我的脚本保证了只有一个DataHolder对象实例的事实,这不是单例模式吗? - Uri Popov
在Unity中,绝对不可能保证场景中只有一个给定组件。在类似Unity的ECS系统中尝试创建“类单例”是毫无意义的。这就像说你希望Photoshop场景中的像素或文本编辑器场景中的字母是“单例”一样,这是没有意义的。 - Fattie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接