【问题标题】:Coroutine wrong Behavior when scene is loaded加载场景时的协程错误行为
【发布时间】:2016-05-17 12:53:21
【问题描述】:

好的,我有这个协程:

 IEnumerator ShowCharTraits()
    {

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

    }

我的GameManager 中的 awake 方法是这样调用它的:

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 来检查协程是否工作
  • 我确实知道没有调用哪个特定行。我已经回答了我自己的问题。问题是让我们换个地方说。
  • 我已经为您提供了一个令人兴奋的答案@uripopov!

标签: c# unity3d


【解决方案1】:

假设您希望在 Unity 项目中拥有一个“像单身”一样的中心位置。例如,

SoundEffects
LapTimer
Scores
SocialMediaConnections

你要做的就是这个。

  1. 制作你的脚本 SoundEffects.cs

  2. 回想一下,每个 Unity 项目必须有“预加载”场景。 (不可能没有)

  3. 在预加载场景中,有一个名为“holder”的空游戏对象。确保它被标记为“DontDestroyOnLoad”

  4. 将 SoundEffects.cs 附加到该持有者

你已经完成了。

没有别的了。

你已经完成了。

“就这么简单”

因此,在任何特定场景中,碰巧附加到任何特定对象的任何特定脚本都可能需要访问“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>();

或其他。

统一很简单。 非常简单。就是这么简单。

  1. 编写您的脚本,LapTimer.cs 或其他任何内容

  2. 将它放在预加载场景中的支架对象上(它还能在哪里?)

没有“3”,仅此而已。就是这么简单。

请注意...

在 Unity 的早期,不幸的是,有人在某处的 QA 板上提到了“单例”。 (在 ECS 系统中不能有“单例”,这是没有意义的。)当时,这引发了关于如何在 Unity 中制作“类似单例的东西”的巨大讨论,这在混乱中造成了混乱。不幸的是,从那天到现在,你让学习 Unity 的人看到网上到处提到的“单例”,这导致了无穷无尽的困惑。

再一次,请注意“经理”的概念在 Unity 中非常简单 - 上面已解释过。这是微不足道的。再简单不过了。


请注意,上面我提到您可能需要 syntactic candy 来避免输入 Laps laps = Object.FindObjectOfType&lt;Laps&gt;(); 的繁琐工作

很遗憾,Unity/c# 中没有宏,所以您需要另一种方式。

您只需使用在 Unity 中流行多年的 "Grid" 脚本 http://answers.unity3d.com/answers/1124859/view.html

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

【讨论】:

  • 感谢您一如既往的出色回答。我已经在这样做了。我的第一个场景中有一个对象是 DontDestroyOnLoad()。问题来自这样一个事实,即附加到它的 Dataholder 看起来是否有它的任何其他实例,并在它进入场景时破坏它们。这导致另一个持有特定场景脚本的对象被销毁,因为遗憾的是它有一个通过测试附加到它的 DataHolder 实例。所以发生的事情是一个脚本的 Awake() 正在为字符调用协程,而它正在运行唤醒来自 DataHodler 正在删除整个对象
  • 我的主菜单场景是这样做的。问题是我忘记了另一个场景中游戏对象上的 DataHolder 组件。该游戏对象被来自主菜单场景的 DataHolder 销毁。场景的相同游戏对象帮助脚本。这就是为什么一切都崩溃了。
  • 从主菜单来回切换不是这里的问题。我已经通过更改 DataHolder 的 Awake() 解决了我的问题我不知道如何解释发生的事情比我已经拥有的更好。
【解决方案2】:

好的,我该如何解释。我有一个作为数据持有者的单身人士。在使用游戏管理器开发场景时,我已将单例附加到包含一堆脚本的游戏管理器对象上。现在,当我制作主菜单时,我将我的单例添加到其中,这样我就可以将信息带到游戏场景中,因此我的整个游戏管理器对象都被删除了。这是 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 中没有单身人士。你真的不能拥有它们。
  • 在 C# 中可以吗?我在我的 MusicController 中使用了一个。 ghettocode.blogspot.com/2016/04/…
  • @JoeBlow 等等,我的脚本保证只有一个 DataHolder 对象的实例,这不是让它成为单例吗?
  • 在 Unity 中,绝对不可能保证场景中只有一个给定组件。在 Unity 这样的 ECS 系统中尝试制作“类似单例的东西”是完全没有意义的。这就像说您希望 Photoshop 场景中的像素或文本编辑器场景中的字母是……“单例”。这是没有意义的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-20
  • 2023-03-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多