Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/279.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 切换/加载场景时如何使用单例?_C#_Unity3d - Fatal编程技术网

C# 切换/加载场景时如何使用单例?

C# 切换/加载场景时如何使用单例?,c#,unity3d,C#,Unity3d,在层次结构中,我有3个对象,我希望在开始新游戏时保留它们,而不是销毁它们。 但我想在切换回主菜单时销毁它们 对象包括:玩家、游戏管理员、场景加载器 在3个对象(玩家、游戏管理员、场景加载器)上,我为每个对象添加了一个脚本名PersistentManager: using System.Collections; using System.Collections.Generic; using UnityEngine; public class PersistentManager : MonoBe

在层次结构中,我有3个对象,我希望在开始新游戏时保留它们,而不是销毁它们。 但我想在切换回主菜单时销毁它们

对象包括:玩家、游戏管理员、场景加载器

在3个对象(玩家、游戏管理员、场景加载器)上,我为每个对象添加了一个脚本名PersistentManager:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PersistentManager : MonoBehaviour
{
    public static PersistentManager Instance { get; private set; }

    private void Awake()
    {
        if(Instance == null)
        {
            Instance = this;
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            Destroy(gameObject);
        }
    }
}
附加到场景加载器的场景加载器脚本:

using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.SceneManagement;

public class SceneLoader : MonoBehaviour
{
    private bool loadScene = false;

    [SerializeField]
    private int scene;
    [SerializeField]
    private Text loadingText;

    // Updates once per frame
    void Update()
    {
        // If the player has pressed the space bar and a new scene is not loading yet...
        if (Input.GetKeyUp(KeyCode.Space) && loadScene == false)
        {
            LoadScene(scene);
        }

        // If the new scene has started loading...
        if (loadScene == true)
        {
            // ...then pulse the transparency of the loading text to let the player know that the computer is still working.
            loadingText.color = new Color(loadingText.color.r, loadingText.color.g, loadingText.color.b, Mathf.PingPong(Time.time, 1));
        }
    }

    // The coroutine runs on its own at the same time as Update() and takes an integer indicating which scene to load.
    IEnumerator LoadNewScene()
    {
        // This line waits for 3 seconds before executing the next line in the coroutine.
        // This line is only necessary for this demo. The scenes are so simple that they load too fast to read the "Loading..." text.
        //yield return new WaitForSeconds(3);

        // Start an asynchronous operation to load the scene that was passed to the LoadNewScene coroutine.
        AsyncOperation async = SceneManager.LoadSceneAsync(scene);

        // While the asynchronous operation to load the new scene is not yet complete, continue waiting until it's done.
        while (!async.isDone)
        {
            yield return null;
        }
    }

    public void LoadScene(int scene)
    {
        // ...set the loadScene boolean to true to prevent loading a new scene more than once...
        loadScene = true;

        // ...change the instruction text to read "Loading..."
        loadingText.text = "Loading...";

        this.scene = scene;

        // ...and start a coroutine that will load the desired scene.
        StartCoroutine(LoadNewScene());
    }
}
以及附加到游戏管理器的游戏管理器脚本:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameManager : MonoBehaviour
{
    public SceneLoader sceneLoader;
    public PlayerController playerController;
    public CamMouseLook camMouseLook;
    public static bool backToMainMenu = false;
    public static bool togglePauseGame;

    // Start is called before the first frame update
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.P))
        {
            PauseGame();
        }

        if (Input.GetKeyDown(KeyCode.Escape))
        {
            BackToMainMenu();
        }
    }

    public void PauseGame()
    {
        togglePauseGame = !togglePauseGame;

        if (togglePauseGame == true)
        {
            playerController.enabled = false;
            camMouseLook.enabled = false;
            Time.timeScale = 0f;
        }
        else
        {
            playerController.enabled = true;
            camMouseLook.enabled = true;
            Time.timeScale = 1f;
        }
    }

    private void BackToMainMenu()
    {
        sceneLoader.LoadScene(0);
        playerController.enabled = false;
        camMouseLook.enabled = false;
        Cursor.lockState = CursorLockMode.None;
        Time.timeScale = 0f;
        backToMainMenu = true;
    }
}
现在,当我运行游戏,然后按下空格键开始一个新游戏时,主菜单场景被删除,不知道为什么

在使用我在3个对象中的每个对象上使用的PersistentManager脚本之前,请使用另一个脚本:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class DontDestroy : MonoBehaviour
{
    private void Awake()
    {
        if (GameManager.backToMainMenu == false)
        {
            DontDestroyOnLoad(transform);
        }
    }
}
当开始新游戏时,这3个对象保持活动状态,如果切换回主菜单,问题是切换回主菜单时,这3个对象也在主菜单中

所以我有6个对象的副本。3在主菜单场景中,3在DontDestroyOnLoad场景中。 这就是为什么我尝试使用singleton


我希望,当我切换回主菜单时,3对象不会仅在DontDestroyOnLoad上的层次结构中的主菜单场景中。

问题是,您继承的类在父类中有它的实例

publicstaticpersistentmanager实例{get;private set;}

这意味着第一个对象将消失

  • 实例是否为空?对
  • 以我为例
  • 设置一个不在加载时销毁的
  • 其他两个对象将检查实例,查看它是否为null,因为第一个对象被引用,它们将销毁自己

    一般来说,我建议尽量不要使用单体,因为有些人可能认为它们是反设计模式。

    然而,如果你仍然想使用一个;我在这里附上了一个脚本,我在游戏中使用了这个脚本,它已经在数百万用户中运行了多年,所以它已经针对我们遇到的大多数边缘案例进行了设置

    #region Using
    
    using System.Linq;
    using UnityEngine;
    using UnityEngine.SceneManagement;
    
    #endregion
    
    /// <summary>
    ///     Generic singleton Class. Extend this class to make singleton component.
    ///     Example:
    ///     <code>
    /// public class Foo : GenericSingleton<Foo>
    /// </code>
    ///     . To get the instance of Foo class, use <code>Foo.instance</code>
    ///     Override <code>Init()</code> method instead of using <code>Awake()</code>
    ///     from this class.
    /// </summary>
    public abstract class GenericSingleton<T> : MonoBehaviour where T : GenericSingleton<T>
    {
        private static T _instance;
    
        [SerializeField, Tooltip("If set to true, the gameobject will deactive on Awake")] private bool _deactivateOnLoad;
        [SerializeField, Tooltip("If set to true, the singleton will be marked as \"don't destroy on load\"")] private bool _dontDestroyOnLoad;
    
        private bool _isInitialized;
    
        public static T instance
        {
            get
            {
                // Instance required for the first time, we look for it
                if (_instance != null)
                {
                    return _instance;
                }
    
                var instances = Resources.FindObjectsOfTypeAll<T>();
                if (instances == null || instances.Length == 0)
                {
                    return null;
                }
    
                _instance = instances.FirstOrDefault(i => i.gameObject.scene.buildIndex != -1);
                if (Application.isPlaying)
                {
                    _instance?.Init();
                }
                return _instance;
            }
        }
    
        // If no other monobehaviour request the instance in an awake function
        // executing before this one, no need to search the object.
        protected virtual void Awake()
        {
            if (_instance == null || !_instance || !_instance.gameObject)
            {
                _instance = (T)this;
            }
            else if (_instance != this)
            {
                Debug.LogError($"Another instance of {GetType()} already exist! Destroying self...");
                Destroy(this);
                return;
            }
            _instance.Init();
        }
    
        /// <summary>
        ///     This function is called when the instance is used the first time
        ///     Put all the initializations you need here, as you would do in Awake
        /// </summary>
        public void Init()
        {
            if (_isInitialized)
            {
                return;
            }
    
            if (_dontDestroyOnLoad)
            {
                DontDestroyOnLoad(gameObject);
            }
    
            if (_deactivateOnLoad)
            {
                gameObject.SetActive(false);
            }
    
            SceneManager.activeSceneChanged += SceneManagerOnActiveSceneChanged;
    
            InternalInit();
            _isInitialized = true;
        }
    
        private void SceneManagerOnActiveSceneChanged(Scene arg0, Scene scene)
        {
            // Sanity
            if (!instance || gameObject == null)
            {
                SceneManager.activeSceneChanged -= SceneManagerOnActiveSceneChanged;
                _instance = null;
                return;
            }
    
            if (_dontDestroyOnLoad)
            {
                return;
            }
    
            SceneManager.activeSceneChanged -= SceneManagerOnActiveSceneChanged;
            _instance = null;
        }
    
        protected abstract void InternalInit();
    
        /// Make sure the instance isn't referenced anymore when the user quit, just in case.
        private void OnApplicationQuit()
        {
            _instance = null;
        }
    
        void OnDestroy()
        {
            // Clear static listener OnDestroy
            SceneManager.activeSceneChanged -= SceneManagerOnActiveSceneChanged;
    
            StopAllCoroutines();
            InternalOnDestroy();
            if (_instance != this)
            {
                return;
            }
            _instance = null;
            _isInitialized = false;
        }
    
        protected abstract void InternalOnDestroy();
    }
    

    gameObject.IsNullOrDestroyed()IsNullOrDestroyed()方法不存在。'GameObject'不包含'IsNullOrDestroyed'的定义,并且找不到可访问的扩展方法'IsNullOrDestroyed'接受类型为'GameObject'的第一个参数(是否缺少using指令或程序集引用?)。很抱歉,它使用的扩展方法不包括在内。我已经修改了没有它的代码,请现在再试一次