理解Unity3d中的场景

48

我对Unity3d中的场景有些困惑,但找不到任何相关资源。

  1. 什么时候应该使用场景?例如,在平台游戏中,每个级别都必须是一个不同的场景吗?主菜单是否也是一个场景?
  2. 可以在场景上叠加其他场景吗?
  3. 资产在场景之间如何工作?它们附加到每个独立的场景并且必须每次重新加载。能否指定何时不再需要某个资产?
  4. 如何在场景之间发送数据/界面传递数据?我知道这是一个广泛的话题,但我不想用多个问题垃圾邮件轰炸你。

一个非常重要的信息:https://dev59.com/qVsV5IYBdhLWcg3wsguh#35891919 - Fattie
6
这个“极其重要的信息”充其量是误导性的,可能会给你带来更多的伤害而不是好处。 - blade
4个回答

76
“何时应该使用场景?例如,在平台游戏中,每个级别都必须是不同的场景吗?主菜单会是一个场景吗?”这没有通用规则。理论上,您可以为整个游戏只创建一个场景。如何组织场景完全取决于您,并且通常取决于您正在创建的游戏类型。我认为至少要考虑使用场景的3个特点:
它们是所有预实例化对象的逻辑容器,可能有助于将您的游戏分成多个级别/部分。
您可以序列化场景内的GameObject和Component之间的交叉引用(如果GO A需要对GO B的引用,并且它们属于同一个场景,则可以序列化引用,您不再需要在运行时查找所引用的对象)。
当您加载(不是以可添加方式)另一个场景时,已经加载到内存中的资源会自动释放。
“能否覆盖场景?”
可以使用LoadAdditive来实现。不幸的是,一旦两个场景都加载到内存中,就没有自动区分属于其中一个场景的对象的方法。因此,如果您加载了第二个级别环境,则需要跟踪先前的环境并在需要时显式销毁它。 资产如何在场景之间工作?它们是否附加到每个单独的场景并且必须每次重新加载。可以指定何时不再需要资产吗?
默认情况下,每个场景的GameObject在新场景加载时都会被销毁(除非使用附加场景加载)。使GameObject在场景之间存活的一种方法是使用DontDestroyOnLoad进行标记。
如果您需要共享GameObject的特定“配置”,可以将其存储为预制件,并在场景之间引用它(但请记住,一旦在场景中,它就是预制件实例,因此GO与prefab共享最初序列化的属性而不是被覆盖的属性,但是两个相同预制件的实例是不同的对象)。
如何在场景之间发送数据/接口?
有几种方法,具体取决于您要共享什么类型的持久数据。
  • 对于特定的GameObject实例,可以使用DontDestroyOnLoad让对象保留。
  • 如果您有一些不需要附加到特定GameObject的配置数据,可以考虑在AssetDatabase中存储ScriptableObject并引用它。
  • 如果您有必须跨不同游戏会话持久存在的数据,可以考虑将它们存储到PlayerPrefs中。

还有两种我不喜欢的方法,但只是列举:

  • 使用静态字段有时可以帮助您做到这一点,但从我的角度来看,它有几个问题
  • 从磁盘保存和加载(在多种情况下可能很有用,但通常是一种依赖于平台的方式,您可能会遇到一些麻烦,特别是在不同的移动平台上)
这是一个广泛的话题,希望这个答案能够提供一个相当不错的概述。

谢谢你提供这个非常好的答案。我已经使用Unity工作了6个月(所以我还是一个新手)。但是根据我的个人经验,我发现使用ScriptableObjects总是有点麻烦。特别是在开发期间,当其数据结构可能经常更改时。因此,对于我的移动项目,我决定使用JSON.NET for Unity将数据持久化为JSON。到目前为止,它运行得相当不错。希望在阅读了您的答案后,我不会遇到任何严重问题。 - André Gasser

3
什么时候应该使用场景?例如,在平台游戏中,每个级别都必须是不同的场景吗?主菜单会是一个场景吗?
在游戏中,您需要有至少一个场景,但是对于需要在逻辑上将游戏的某些部分与其余部分分开的情况,可以使用场景。没有规定您需要拥有多少场景。
如果您所说的主菜单是带有UI元素的画布,则它将位于场景中,而不是场景本身。Canvas只是另一个GameObject,我们通常用它来显示游戏菜单。我通常创建一个Canvas GameObject,放置一个名为“UIManager”的脚本,并在其上放置DontDestroyOnLoad,以便我可以在所有场景中访问它。将其设置为Singleton,以确保不会重复。
能否覆盖场景?
是的,您可以同时加载任意数量的场景,没有限制。但您计划叠加场景的目的是什么?也许有比叠加加载更好的方法。
资产是您在“项目”层次结构中看到的内容。我认为您指的是场景中的“GameObject”,如果是这样,请将您的游戏对象视为具有组件的实体(Entity-Component System)。当其父场景被销毁时,场景中的所有实体都会被销毁,除非使用DontDestroyOnLoad在某个组件(在Unity中是MonoBehavior)中明确声明不要销毁。被销毁的实体将被垃圾回收。

因此,它们的加载(或重新加载)取决于您的实现方式,取决于您是否一次又一次地实例化/销毁它们,或者是否将其实例化的预制件放在缓存对象中,并稍后从中检索。

如何在场景之间发送数据/在场景之间进行接口通信?

Heisen涵盖了我能想到的那些方法。只是为了添加一点内容,这也取决于您希望如何构建项目的架构。因此,如果您有一个底层数据结构来保存命令,则可以在项目的任何部分自由使用它。


0

大多数游戏都会为每个级别(包括主菜单)设置场景,但这完全取决于您。 如果您将数据保存在文本文件或二进制文件中,则可以在一个场景中使用另一个场景的数据。有很多关于如何做到这一点的教程。我发现文档非常有帮助。 资产在项目中是通用的。 您不能覆盖场景。


0
什么时候应该使用场景?例如,在平台游戏中,每个关卡都必须是不同的场景吗?主菜单会是一个场景吗?
何时使用场景取决于您。如果您刚开始,我建议为游戏的每个部分使用不同的场景。
可以叠加场景吗?
可以,使用LoadSceneMode.Additive()。(LoadAdditive()已过时)
资产在场景之间如何工作?它们是否附加到每个单独的场景并且每次都必须重新加载?可以指定何时不再需要某个资产吗?

默认情况下,使用SceneManager.LoadScene()时会删除资产。但是,如果您使用DontDestroyOnLoad(),则该对象将不会在进入新场景时被销毁。如果您只想在几个场景中保留一个对象而不是全部场景,请使用Destroy()和一些布尔逻辑。

如何在场景之间发送数据/在场景之间进行接口?我知道这是一个广泛的话题,但我不想用多个问题来垃圾邮件。

你可以通过使用前面提到的DontDestroyOnLoad()、在不同的脚本中引用数据、使用ScriptableObjects、使用JSON Serialization、使用StreamWriter()、使用PlayerPrefs(不要用于重要信息)等方式来通过场景发送数据。我个人建议使用ScriptableObjects来实现数据的可访问性,使用StreamWriter()来实现加密功能。


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