Dim*_*zyr 46 c# unity-game-engine
我正在制作简单的游戏经理.我有一个脚本,可以从游戏中的所有场景访问.我需要在加载新场景后检查其变量的值.但是我的代码在开始模拟后只运行一次,而具有此脚本的对象存在于所有场景中.怎么了?装载新场景后为什么不起作用?
Fat*_*tie 91
Unity没有"内置"的预加载场景,这让人很困惑.
他们将来会添加这个概念.
重复一遍,你必须有一个预加载场景.
就这么简单.
幸运的是,拥有预加载场景非常容易.
步骤1.
制作一个名为"preload"的场景.它必须是构建管理器中的场景0.
第2步.
在"预加载"场景中,创建一个名为"__app"的空GameObject.
简单地说,穿上DontDestroyOnLoad'__app'.
注意:
这是您使用的整个项目中唯一的位置DontDestroyOnLoad.
就这么简单.
在示例中:开发人员制作了一行DDOL脚本.
将该脚本放在"__app"对象上.
你再也不用考虑DDOL了.
第3步
您的应用将具有(许多)"一般行为".所以,比如数据库连接,声音效果,评分等等.
您必须,并且只能将您的一般行为放在"_app"上.
这真的很简单.
当然,一般行为 - 在项目的任何地方,任何时候和所有场景中都可用.
你怎么能这样做?
在上面的图像示例中,请注意"Iap"("应用程序内购买")和其他内容.
所有"一般需要的行为" - 声音效果,得分等等 - 就在那个对象上.
这意味着 - 当然,自然 -
您可以使用Unity的所有常用功能,您可以在其他所有游戏对象上使用它.
当然,您可以使用常用的Inspector变量(拖动连接),设置等.
(的确如此:假设你已经被聘请参与一个现有的项目.你要做的第一件事就是看一下预载现场.你会在一个地方看到项目的所有"一般行为".声音效果您将立即看到这些内容的所有设置,如Inspector变量...语音量,Playstore ID等.)
这是一个例子"声音效果"的一般行为:
看起来还有一种"配音"的一般行为,以及"音乐"的一般行为".
重复.关于你的"一般"行为.(声音效果,得分,社交等等)这些只能在预加载场景中的游戏对象上进行.
你完成了.
说实话,这很简单.几乎没有什么可以做到的!
许多来自其他类型的工程师都会永远意识到这很简单,因为它简单易行,"不可能是真的".
重复一遍:整个问题是(奇怪)Unity只是简单地忘记了"内置"一个预加载场景.因此,您只需单击添加一个(不要忘记在预加载场景中添加DDOL).
现在在开发期间:
始终从Preload场景开始游戏.
就这么简单.
(重要的一点:你的应用程序肯定会有"早期"场景.例如,"启动画面"或"菜单".请注意,你不能使用"启动"或"菜单"作为预加载场景.你必须字面上有一个预加载场景.预加载场景将加载你的飞溅场景,菜单场景,或任何你想要的.)
然后,您可以非常简单地从任何场景中的任何脚本中找到"SoundEffects"或"GameManager".
Sound sound = Object.FindObjectOfType<Sound>();
Game game = Object.FindObjectOfType<Game>();
Run Code Online (Sandbox Code Playgroud)
做到这一点的Awake,对于任何需要它的脚本.
说实话,这很简单.这里的所有都是它的.
Sound sound = Object.FindObjectOfType<Sound>();由于在线看到的100个绝对错误的代码示例,出现了巨大的混乱.此外,新的Unity工程师"不敢相信"这很容易!
真的很容易 - 诚实!
Unity忘记添加一个内置的"预加载场景" - 在某个地方附加你的系统,如SoundEffects,GameManager等,这是完全奇怪的.这只是Unity的一个奇怪的事情.因此,您在任何Unity项目中所做的第一件事就是单击一次以制作预加载场景.
而已!
请注意,如果您真的想要输入更少(!)的代码行,那么它非常简单 - 您可以为这些内容使用全局代码!
这在详细解释这里(你可以用我的流行Grid.cs脚本)和@Frits回答以下.
(无论是使用全局变量,还是只需要在每个需要特定变量的组件中都有局部变量,只需要代码风格.在非常不寻常的情况下,"全局"方法可能具有高性能(与任何全局变量一样).)
DylanB问:" 在开发过程中,每次点击"播放"之前必须点击预加载场景是非常烦人的.这可以自动化吗?"
当然,每个团队都有不同的方式来做到这一点.这是一个简单的例子:
// 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"); }
}
}
Run Code Online (Sandbox Code Playgroud)
但不要忘记:"你还能做什么?" 游戏必须从预加载场景开始. 除了去预加载场景,你还可以做些什么来开始游戏?您也可以问"启动Unity以运行Unity是多么烦人 - 如何避免启动Unity?" 当然,游戏绝对必须从预装场景开始 - 它怎么可能呢?当然,在Unity中工作时,你必须"在点击播放之前点击预加载场景" - 它还有什么用呢?
小智 21
@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!"); }
}
Run Code Online (Sandbox Code Playgroud)
而不是你的例子
Sound sound = Object.FindObjectOfType<Sound>();
Run Code Online (Sandbox Code Playgroud)
只需简单地使用它,无需查看,也无需额外的变量,就像这样,从任何地方开始:
Sound.Instance.someWickedFunction();
Run Code Online (Sandbox Code Playgroud)
或者(技术上相同),只需使用一个全局类(通常称为Grid)来"保持"每个类.Howto.所以,
Grid.sound.someWickedFunction();
Grid.networking.blah();
Grid.ai.blah();
Run Code Online (Sandbox Code Playgroud)
这是您可以开始喜欢的任何场景的方法,并确保每次在Unity编辑器中单击“播放”按钮时都重新整合_preload场景。自Unity 2017起有新的属性可用RuntimeInitializeOnLoadMethod,此处有更多信息。
基本上,您有一个简单的平面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
}
Run Code Online (Sandbox Code Playgroud)
然后,在_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
...
Run Code Online (Sandbox Code Playgroud)
2019 年 5 月的替代解决方案不包含_preload:
https://low-scope.com/unity-tips-1-dont-use-your-first-scene-for-global-script-initialization/
我将上面的博客转述为下面的操作方法:
在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
}
Run Code Online (Sandbox Code Playgroud)
添加Main.cs到文件夹Main中的预制件Resources。
请注意它如何RuntimeInitializeOnLoadMethod与Resources.Load("Main")和一起使用DontDestroyOnLoad。
将需要跨场景全局的任何其他脚本附加到此预制件。
Start请注意,如果您将其他场景游戏对象链接到这些脚本,您可能希望在这些脚本的函数中使用类似的内容:
if(score == null)
score = FindObjectOfType<Score>();
if(playerDamage == null)
playerDamage = GameObject.Find("Player").GetComponent<HitDamage>();
Run Code Online (Sandbox Code Playgroud)
或者更好的是,使用资产管理系统,例如Addressable Assets或Asset Bundles。
| 归档时间: |
|
| 查看次数: |
36061 次 |
| 最近记录: |