我正在制作简单的游戏经理。我有一个脚本,可以从游戏中的所有场景访问。我需要在加载新场景后检查其变量的值。但是我的代码在开始模拟后只运行一次,而具有此脚本的对象存在于所有场景中。怎么了?为什么在加载新场景后它不起作用?
答案 0 :(得分:84)
令人困惑的是Unity没有预加载场景"内置"。
他们将来会添加这个概念。
重复一遍,你必须有一个预加载场景。
就这么简单。
幸运的是,拥有一个预加载场景非常容易。
第1步。
制作一个名为" preload"的场景。它必须在构建管理器中场景0 。
第2步。
在" preload"场景制作一个空的GameObject,比方说," __ app"。
只需将DontDestroyOnLoad
放在' __ app'。
注意:
这是 整个项目中唯一的地方 ,您使用DontDestroyOnLoad
。
就这么简单。
在示例中:开发人员制作了一行DDOL脚本。
将该脚本放在" __ app"对象
你再也不用考虑DDOL了。
第3步
您的应用会有(很多)"一般行为"。所以,比如数据库连接,声音效果,评分等等。
您必须且且只能将您的一般行为置于" _app"。
真的那么简单。
当然,一般行为 - 在项目的任何地方,任何时候以及所有场景中都可用。
你还能怎么做?
在上面的图片示例中,请注意" Iap" ("应用内购买")和其他。
您所有的"一般需要的行为" - 声音效果,得分等等 - 就在那个物体上。
这意味着 - 当然,自然 -
您可以使用Unity的所有常用功能,您可以在其他所有游戏对象上使用它。
当然,您可以使用常用的Inspector变量(拖动连接),设置等。
(的确如此:让我们说你已经被聘请参与一个现有的项目。你要做的第一件事就是看一下预载场景。你会看到所有的"一般项目在一个地方的行为和#34;声音效果,评分,人工智能等。您将立即看到这些事物的所有设置,如Inspector变量......语音量,Playstore ID等。)
这是一个例子"音效"一般行为:
看起来还有一个"配音"一般行为和音乐"一般行为"。
重复一遍。关于你的"一般行为"。 (声音效果,得分,社交等)这些 CAN ONLY GO 在预加载场景中的游戏对象上。
你已经完成了。
老实说,这很简单。
根本没什么可说的!
许多来自其他环境的工程师都对此感到满意,因为它看起来似乎很简单"。
重复一遍:整个问题是(奇怪的)Unity只是简单地忘记了#34;内置"一个预加载场景。因此,您只需单击即可添加预加载场景 - 并且不要忘记在预加载场景中添加DDOL。
所以,在开发期间:
始终从Preload场景开始游戏。
就这么简单。
(重点:您的应用肯定会有#34;早期&#34;场景。例如&#34;启动画面&#34;或者&#34;菜单&#34;。注意你< strong> 无法 使用&#34;启动屏幕&#34;或&#34;菜单&#34;作为预加载场景。您必须确实 &#34;预加载场景&#34; 。预加载场景将 然后加载 您的初始场景,菜单场景或其他你希望。)
所以你有一个预加载场景。
您的所有&#34;一般行为&#34;只是在预加载场景。
接下来你会遇到一个问题,很简单,找到说&#34; SoundEffects&#34;或者&#34; GameManager&#34;。
您必须能够轻松地从任何场景中任何游戏对象的任何脚本中找到它们。
Sound sound = Object.FindObjectOfType<Sound>();
Game game = Object.FindObjectOfType<Game>();
在Awake
中为任何需要它的脚本执行此操作。
Sound sound = Object.FindObjectOfType<Sound>();
由于在网上看到了100个绝对错误的代码示例,因此产生了巨大的混乱。此外,新的Unity工程师无法相信&#34;这很容易!
这真的很容易 - 诚实!
Unity完全忘了添加一个内置的预加载场景&#34; - 在某个地方附加你的系统,如SoundEffects,GameManager等。这只是Unity的一个奇怪的事情。因此,您在任何Unity项目中所做的第一件事就是单击一次以制作预加载场景。
那就是它!
请注意,如果你真的想输入甚至更少(!)的代码行,那么它非常容易 - 你可以为这些东西使用全局!
详细解释here(您可以使用我常用的Grid.cs脚本)和@Frits下面的答案。
(无论您是使用全局变量,还是仅在每个需要该变量的组件中都有局部变量,这只是代码风格的问题。在非常不寻常的情况下,&#34;全局&#34;方法可能是高性能的(与任何全球一样)。)
DylanB问道:&#34; 在开发期间 ,每次点击&#34之前,你必须点击预加载场景是非常烦人的;播放&#34 ;.这可以自动化吗?&#34;
当然,每个团队都有不同的方式来做到这一点。这是一个微不足道的例子:
// this should run absolutely first; use script-execution-order to do so.
// (of course, normally never use the script-execution-order feature,
// this is an unusual case, just for development.)
...
public class DevPreload:MonoBehaviour
{
void Awake()
{
GameObject check = GameObject.Find("__app");
if (check==null)
{ UnityEngine.SceneManagement.SceneManager.LoadScene("_preload"); }
}
}
但不要忘记:&#34;你还能做什么?&#34; 游戏必须从预加载场景开始。 除了进入预加载场景,你还能做些什么来开始游戏?你也可以问一下,启动Unity运行Unity是多么烦人 - 如何避免启动Unity?&#34;当然,游戏绝对必须从预装场景开始 - 它怎么可能呢?所以肯定的是,你必须在点击Play&#34;之前点击预加载场景。在Unity工作时 - 它还有什么用呢?
答案 1 :(得分:20)
@Fattie:感谢您详细阐述这一切,这太棒了!有一点虽然人们试图接近你,我也只是试一试:
我们不希望我们手机游戏中的所有实例都为每个“全局类”做一个“FindObjectOfType”!
相反,你可以让它立即使用静态/单例的实例化,而不是寻找它!
它就像这样简单: 将此内容写入您想要从任何地方访问的类,其中XXXXX是该类的名称,例如“Sound”
public static XXXXX Instance { get; private set; }
void Awake()
{
if (Instance == null) { Instance = this; } else { Debug.Log("Warning: multiple " + this + " in scene!"); }
}
现在代替你的例子
Sound sound = Object.FindObjectOfType<Sound>();
只需简单地使用它,不用看,也不需要额外的变量,就像这样,直接从任何地方开始:
Sound.Instance.someWickedFunction();
或者(技术上相同),只需使用一个全局类(通常称为Grid)来“保持”每个类。 Howto。所以,
Grid.sound.someWickedFunction();
Grid.networking.blah();
Grid.ai.blah();
答案 2 :(得分:5)
这是您可以开始喜欢的场景的方式,并确保每次在unity编辑器中按“播放”按钮时都重新整合_preload场景。自Unity 2017 RuntimeInitializeOnLoadMethod
起,有一个可用的新属性,更多关于here的信息。
基本上,您有一个简单的平面c#类和上面带有RuntimeInitializeOnLoadMethod
的静态方法。现在,每次您开始游戏时,此方法都会为您加载预加载场景。
using UnityEngine;
using UnityEngine.SceneManagement;
public class LoadingSceneIntegration {
#if UNITY_EDITOR
public static int otherScene = -2;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
static void InitLoadingScene()
{
Debug.Log("InitLoadingScene()");
int sceneIndex = SceneManager.GetActiveScene().buildIndex;
if (sceneIndex == 0) return;
Debug.Log("Loading _preload scene");
otherScene = sceneIndex;
//make sure your _preload scene is the first in scene build list
SceneManager.LoadScene(0);
}
#endif
}
然后,在_preload场景中,您还有另一个脚本,可以从您开始的场景中加载所需的场景:
...
#if UNITY_EDITOR
private void Awake()
{
if (LoadingSceneIntegration.otherScene > 0)
{
Debug.Log("Returning again to the scene: " + LoadingSceneIntegration.otherScene);
SceneManager.LoadScene(LoadingSceneIntegration.otherScene);
}
}
#endif
...
答案 3 :(得分:3)
从2019年5月开始,没有 mylist.append(child.get())
AttributeError: 'Checkbutton' object has no attribute 'get'
的替代解决方案:
https://low-scope.com/unity-tips-1-dont-use-your-first-scene-for-global-script-initialization/
我从上面的博客改写为下面的操作方法:
在_preload
中创建一个名为Project > Assets
的文件夹。
从空的Resources
创建一个Main
预制件,并将其放置在GameObject
文件夹中。
在Resources
或任何地方创建一个Main.cs
C#脚本。
Assets > Scripts
将using UnityEngine;
public class Main : MonoBehaviour
{
// Runs before a scene gets loaded
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
public static void LoadMain()
{
GameObject main = GameObject.Instantiate(Resources.Load("Main")) as GameObject;
GameObject.DontDestroyOnLoad(main);
}
// You can choose to add any "Service" component to the Main prefab.
// Examples are: Input, Saving, Sound, Config, Asset Bundles, Advertisements
}
添加到Main.cs
文件夹中的Main
预制中。
请注意它如何与Resources
和RuntimeInitializeOnLoadMethod
一起使用Resources.Load("Main")
。
将所有需要跨场景全局使用的其他脚本附加到该预制件。
请注意,如果您将其他场景游戏对象链接到这些脚本,则可能需要在DontDestroyOnLoad
函数中为这些脚本使用以下内容:
Start
或更妙的是,使用资产管理系统,例如Addressable Assets或Asset Bundles。
答案 4 :(得分:1)
实际上,作为一个进入统一世界的程序员,我看不到这些方法 标准
最简单,最标准的方法:根据unity docs创建预制件:
Unity的Prefab系统可让您创建,配置和存储完整的GameObject及其所有组件,属性值和子GameObjects 作为可重复使用资产。预制资产充当模板,您可以从中创建新的预制实例。
详细信息:
在“资源”文件夹中创建预制:
Resources
,因为它是unity Special folder name 创建一个脚本,其内容如下:
using UnityEngine;
public class Globals : MonoBehaviour // change Globals (it should be the same name of your script)
{
// loads before any other scene:
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
public static void LoadMain()
{
Debug.Log("i am before everything else");
}
}
同时使用prefab和名称空间:
在您的预制脚本中:
using UnityEngine;
namespace Globals {
public class UserSettings
{
static string language = "per";
public static string GetLanguage()
{
return language;
}
public static void SetLanguage (string inputLang)
{
language = inputLang;
}
}
}
使用其他脚本:
using Globals;
public class ManageInGameScene : MonoBehaviour
{
void Start()
{
string language = UserSettings.GetLanguage();
}
void Update()
{
}
}