【问题标题】:Instantiate object on first scene load in Unity在 Unity 中的第一个场景加载时实例化对象
【发布时间】:2015-03-24 18:14:30
【问题描述】:

我有一个包含各种场景的 Unity 项目。我的第一人称控制器有一个脚本,我在其中保留了几个我想在整个游戏中保留的变量。

我会不断更换场景,所以我的剧本有

void Awake() { DontDestroyOnLoad(transform.gameObject); }

这样我的变量值就不会丢失。现在,我将对象放在场景 1 上,当我加载第一个场景时,我的对象就可用了。当我改变场景时,一切都按预期运行:我的变量值是守恒的。但是,当我返回场景 1 时,我的第一人称控制器对象没有被破坏,现在我有两个第一人称控制器(我从另一个场景带来的一个,一个在场景 1 内)。相机没有知道要关注哪一个。

如何仅在第一次加载场景时实例化我的游戏对象?

【问题讨论】:

    标签: unity3d


    【解决方案1】:

    如果您使用该对象重新加载场景,则无法避免重复的 Awake 调用。您可以通过以下两种方式来处理:

    • 有一个专门用于静态初始化的场景。使用 DontDestroyOnLoad 将所有永久加载的脚本放在这里,并始终使用此场景启动应用程序。
    • 存储对第一个初始化对象的静态引用。如果实例已存在,则在 Awake 中销毁自身。

    第二个选项的示例:

    static private MyClass instance = null;
    void Awake() {
        if (instance != null) {
            Destroy(this);
            return;
        }
        instance = this;
    }
    

    【讨论】:

    • 场景前的实例化真是天才!它做了我需要它做的事情。好主意。
    【解决方案2】:

    如果您在加载开始时实例化您的第一人称控制器,则在实例化时,您可以进行条件检查以查看它是否已存在于场景中(然后删除或不实例化)。至于跨场景保存信息,我建议你改用PlayerPrefs

    【讨论】:

      【解决方案3】:

      您可以将您的第一人称控制器(实际上,如果您愿意,可以将您的整个播放器)放入预制件中。

      然后,不要在场景中使用预制件的实例,而是编写一个带有 reference 到预制件的 Spawner 组件。 Spawner 可以检查(在 Awake 中)第一人称控制器是否已存在,并且仅在预制件不存在时才实例化它。

      【讨论】:

        【解决方案4】:

        我知道这是一篇较旧的帖子,但当我在寻找一种方法来统一自动实例化静态 GameObject 时,它在 Google 的前几个结果中。不确定旧版本,但这与 5.6.1 完美结合。我需要一种方法来存储游戏设置并从许多场景中访问它们。我还需要访问某些只能从 MonoBehaviour 访问的统一功能(如屏幕资源等),因此不能选择普通的静态类。我想出的基本上是一个单例类型的统一游戏对象,如果您获得对实例的引用,并且场景中没有这些,它会自动创建它。如果存在,(更改回创建它的场景)复制的自毁,因此游戏中只有一个。

        我想要这个的原因是因为我使用 Unity ScriptableObjects 来存储数据,因为它们可以在编辑和运行时从检查器中编辑,并且在运行时的更改不会恢复(如果您使用旧的 OnGUI 非常方便,因为您可以将矩形位置存储在可编写脚本的对象中,并观察您的控件在运行时四处移动,并且在您停止播放模式时位置不会恢复)

        这是我非常基础的课程(从中删除了我的特定数据)

        using UnityEngine;
        
        public class GameSettings : MonoBehaviour
        {
            private static GameSettings _instance;
        
            public static GameSettings Instance
            {
                get
                {
                    if (_instance != null) return _instance;
                    else
                    {
                        Setup();
                        return _instance;
                    }
                }
            }
        
            public void Start ()
            {
                DontDestroyOnLoad(this);
            }
        
            public void Awake()
            {
                if (_instance != null) Destroy(this);
            }
        
            public static void Setup()
            {
                GameObject settings = GameObject.Find("GameSettings");
                if (settings == null)
                {
                    settings = new GameObject("GameSettings");
                    settings.AddComponent<GameSettings>();
                }
                _instance = settings.GetComponent<GameSettings>();
            }
        }
        

        您可以使用这两种方式:

        1:在您的第一个场景中将其添加到游戏对象(这样您就可以从检查器中修改值)。如果您更改回该场景,由于检查 _instance,它不会创建副本。如果您确实将其手动添加到场景中,请确保将 GameObject.Find 字符串编辑为您的 GameObject 的名称,否则它将创建一个新的。

        2:只需创建对 ClassName.Instance 的引用,如果它不存在,它将自动创建对象。

        场景的示例实现脚本:

        public class TestScene : MonoBehaviour
            {
                private GameSettings _settings;
        
                public void Awake()
                {
                    _settings = GameSettings.Instance;
                }
                public void Start()
                {
                }
        
                public void Update()
                {
        
                }
            }
        

        现在您只需将公共数据(字符串、int 等)添加为 GameSettings 类的公共非静态成员,然后简单地使用 _settings.MemberName 即可访问数据。

        希望这可以帮助那些正在寻找一种动态自动创建游戏对象的方法的人。如果您构建一个 Prefab 并将这个脚本附加到它的任何对象上,这也可以工作。可以方便地在特定场景中动态创建 UI 元素。

        如果您只在一个场景中需要它并且不需要它在关卡加载中持续存在(例如在某些场景中自动实例化某些 UI 元素,例如 npc 对话框等),只需从类的 start 函数中删除 DontDestroyOnLoad那是附加在预制件上的。

        【讨论】:

          【解决方案5】:

          使用属性RuntimeInitializeOnLoadMethodRuntimeInitializeLoadType.BeforeSceneLoad 属性参数创建一个静态void 函数,它将在第一个场景加载之前被调用。通过该功能,您应该能够创建所需的游戏对象等。

          using UnityEngine;
          
          public class GameStartupHelper
          {
              [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
              static void OnBeforeSceneLoad()
              {
                  Debug.Log("OnBeforeSceneLoad(");
                  // Create your stuff from here...
              }
          }
          

          ps。无需将类添加到任何场景中,只需将脚本文件保留在项目中即可调用回调。

          更多解释请参见this post

          【讨论】:

            猜你喜欢
            • 2015-09-07
            • 1970-01-01
            • 1970-01-01
            • 2016-12-04
            • 1970-01-01
            • 2018-09-08
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多