C# 如何防止在Unity中重新加载已加载的游戏对象?

C# 如何防止在Unity中重新加载已加载的游戏对象?,c#,unity3d,game-development,scene-manager,C#,Unity3d,Game Development,Scene Manager,我目前正在Unity中开发一个游戏,遇到了一个小问题。我正在开发一个重启函数,当玩家死亡并再次加载第一个场景时,该函数会自动调用。但是,由于某些原因,在重新加载场景时,游戏对象会被复制,死亡时处于活动状态的游戏对象的版本处于非活动状态,而加载时应加载的版本设置为活动状态,等等,每次玩家死亡时,都会向层次结构添加相同游戏对象的新副本。我试图用多种方法解决这个问题。首先,通过尝试检查每个复制的游戏对象是否已经有一个自身运行的实例,方法是附加一个脚本,该脚本在每次场景发生变化时检查它们是否已经是当前游

我目前正在Unity中开发一个游戏,遇到了一个小问题。我正在开发一个重启函数,当玩家死亡并再次加载第一个场景时,该函数会自动调用。但是,由于某些原因,在重新加载场景时,游戏对象会被复制,死亡时处于活动状态的游戏对象的版本处于非活动状态,而加载时应加载的版本设置为活动状态,等等,每次玩家死亡时,都会向层次结构添加相同游戏对象的新副本。我试图用多种方法解决这个问题。首先,通过尝试检查每个复制的游戏对象是否已经有一个自身运行的实例,方法是附加一个脚本,该脚本在每次场景发生变化时检查它们是否已经是当前游戏对象的实例:

 public static GameObject Instance;

 void Awake()
 {
     if(Instance){
         DestroyImmediate(gameObject);
     }else
     {
         DontDestroyOnLoad(gameObject);
         Instance = this;
     }
 }
起初这似乎解决了问题,但到最后却变得很有教育意义,因为脚本使我的所有其他场景对象表现不好或根本没有表现,所以我选择寻找另一种解决方案

其次,在开始加载第一个场景之前,我试图销毁每个游戏对象。这在一开始似乎也是可行的,但现在我的对象池只是重新创建了新的游戏对象,它也增加了层次结构,从本质上取代了其他游戏对象的相同问题

最后,为了解决这个问题,当需要加载objectpooler的场景被调用时,我尝试让objectpooler只运行一次,但这似乎也不起作用。有人知道我怎样才能解决这个问题吗。这是脚本的一部分,负责在玩家死亡时加载原始场景:

void Restart()
{
    GameObject[] allObjects = UnityEngine.Object.FindObjectsOfType<GameObject>();

    foreach (GameObject gos in allObjects)
    {
        if (gos.activeInHierarchy)
        {
            if (gos != GameObject.Find("GameManager") && gos != GameObject.Find("ScreenBound")) 
            {
                gos.SetActive(false);
            }
        }
    }
    MySceneManager.LoadScene(0, this);
}
这是我的ObjectPooler:

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

public class ObjectPooler : MonoBehaviour
{
    [System.Serializable]
    public class Pool
    {
        public string tag;
        public GameObject prefab;
        public int size;
    }

    #region Singleton 

    public static ObjectPooler Instance;

    private void Awake()
    {

        if (Instance)
        {
            Destroy(this.gameObject);
            return;
        }

        Instance = this;
        DontDestroyOnLoad(this.gameObject);
    }

    #endregion

    public List<Pool> pools;
    public Dictionary<string, Queue<GameObject>> poolDictionary;

    private Dictionary<string, Pool> prefabPools;

    void Start()
    {
        poolDictionary = new Dictionary<string, Queue<GameObject>>();

        foreach (Pool pool in pools)
        {
            Queue<GameObject> objectPool = new Queue<GameObject>();

            for (int i = 0; i < pool.size; i++)
            {
                GameObject obj = Instantiate(pool.prefab);
                DontDestroyOnLoad(obj);
                obj.SetActive(false);
                objectPool.Enqueue(obj);
            }

            poolDictionary.Add(pool.tag, objectPool);


        }
    }

    private List<GameObject> currentlySpawnedObjects = new List<GameObject>();



    public void Release(GameObject obj)
    {
        currentlySpawnedObjects.Remove(obj);

        obj.SetActive(false);


        obj.transform.SetParent(transform);


        poolDictionary[obj.tag].Enqueue(obj);
        DontDestroyOnLoad(obj);
    }

    public void ReleaseAll()
    {
        foreach (var child in currentlySpawnedObjects)
        {
            Release(child);
        }
    }

    public GameObject SpawnFromPool(string tag, Vector3 position, Quaternion rotation)
    {


        if (!poolDictionary.ContainsKey(tag))
        {
            Debug.LogWarning("Pool with tag" + tag + " doesn't exist.");
            return null;
        }
        GameObject objectToSpawn = poolDictionary[tag].Dequeue();


        objectToSpawn.SetActive(true);
        objectToSpawn.transform.position = position;
        objectToSpawn.transform.rotation = rotation;

        IPooledObject pooledObj = objectToSpawn.GetComponent<IPooledObject>();

        if (pooledObj != null)
        {
            pooledObj.OnObjectSpawn();
        }

        poolDictionary[tag].Enqueue(objectToSpawn);

        return objectToSpawn;

        currentlySpawnedObjects.Add(objectToSpawn);

        return objectToSpawn;
    }



}
使用系统集合;
使用System.Collections.Generic;
使用UnityEngine;
公共类ObjectPooler:MonoBehavior
{
[系统可序列化]
公共班级游泳池
{
公共字符串标签;
公共游戏对象预制;
公共整数大小;
}
#区域单态
公共静态ObjectPooler实例;
私人空间
{
如果(实例)
{
摧毁(这个游戏对象);
返回;
}
实例=此;
DontDestroyOnLoad(this.gameObject);
}
#端区
公开名单池;
公共词典;
私人词典库;
void Start()
{
poolDictionary=新字典();
foreach(池中的池)
{
队列对象池=新队列();
对于(int i=0;i
根据您的需要,您可以尝试以下方法:

  • 如果需要保存对象的单个实例,请使用单例模式。此案例与保存管理器(GameplayManager、SceneControl、AssetBundleManager等)相关。在其他情况下,最好使用其他方式。要了解更多有关实现的信息,请参见
  • 加载新场景时销毁所有旧对象。为此,可以使用
    SceneManager.LoadScene
    方法,并将
    LoadSceneMode.Single
    作为参数。它将保留
    DontDestoryOnLoad
    对象,但将删除所有其他对象

  • 我不确定,但对我来说,第一个可能的问题似乎是,它在场景中的一个对象上以协同程序运行,您将要卸载该对象

    这是可行的,这很酷,但请记住,一旦调用方对象/组件被破坏或禁用,协同路由就会停止工作

    为了避免这种情况,我将使用单例模式将脚本移动到
    dontdestroyonloadsecene
    中的对象

    下一个问题可能是您将使用SceneIndex。。。两个场景,您要卸载的场景和您要加载的场景都有索引
    0

    因此,可能在添加加载的场景和要卸载的场景之间存在冲突

    当您打电话时,这种情况也可能再次发生

    var newScene = SceneManager.GetSceneByIndex(lastLoadedScene);
    
    为了避免这种情况,我宁愿按场景参考进行卸载

    public class MySceneManager : MonoBehaviour
    {
        private static MySceneManager instance;
    
        // Lazy initialization
        // With this you wouldn't even need this object in the scene
        public static MySceneManager Instance
        {
            if(instance) return instance;
    
            instance = new GameObject ("MySceneManager").AddComponent<MySceneManager>();
    
            DontDestroyOnLoad(instance);
        }
    
        // Usual instant initialization having this object in the scene
        private void Awake ()
        {
            if(instance && instance != this)
            {
                Destroy(gameObject);
                return;
            }
    
            instance = this;
    
            DontDestroyOnLoad(this);
        }
    
    
    
        public void LoadScene(int index)
        {
            StartCoroutine(loadNextScene(index));
        }
    
        private IEnumerator loadNextScene(int index)
        { 
            // I didn't completely go through your ObjectPooler but I guess you need to do this
            ObjectPooler.Instance.ReleaseAll();
    
            // Instead of the index get the actual current scene instance
            var currentScene = SceneManager.GetActiveScene();
    
            var _async = SceneManager.LoadSceneAsync(index, LoadSceneMode.Additive);
    
    
            _async.allowSceneActivation = false;
            yield return new WaitWhile(() => _async.progress < 0.9f);
    
            _async.allowSceneActivation = true;
    
            yield return new WaitUntil(() => _async.isDone);
    
            // You have to do this before otherwise you might again
            // get by index the previous scene 
            var unloadAsync = SceneManager.UnloadSceneAsync(currentScene);
            yield return new WaitUntil(()=>unloadAsync.isDone);            
    
            var newScene = SceneManager.GetSceneByBuildIndex(index);    
    
            SceneManager.SetActiveScene(newScene);
        }
    }
    
    无需添加,因此只要新场景完全加载,当前场景就会自动删除



    注意:在智能手机上输入,因此不提供保修,但我希望这个想法变得清晰

    首先,您描述的行为听起来非常奇怪。。。我会试图解决这个问题,而不是试图找到更糟糕的解决办法。。。加载场景应重置所有内容
    public class MySceneManager : MonoBehaviour
    {
        private static MySceneManager instance;
    
        // Lazy initialization
        // With this you wouldn't even need this object in the scene
        public static MySceneManager Instance
        {
            if(instance) return instance;
    
            instance = new GameObject ("MySceneManager").AddComponent<MySceneManager>();
    
            DontDestroyOnLoad(instance);
        }
    
        // Usual instant initialization having this object in the scene
        private void Awake ()
        {
            if(instance && instance != this)
            {
                Destroy(gameObject);
                return;
            }
    
            instance = this;
    
            DontDestroyOnLoad(this);
        }
    
    
    
        public void LoadScene(int index)
        {
            StartCoroutine(loadNextScene(index));
        }
    
        private IEnumerator loadNextScene(int index)
        { 
            // I didn't completely go through your ObjectPooler but I guess you need to do this
            ObjectPooler.Instance.ReleaseAll();
    
            // Instead of the index get the actual current scene instance
            var currentScene = SceneManager.GetActiveScene();
    
            var _async = SceneManager.LoadSceneAsync(index, LoadSceneMode.Additive);
    
    
            _async.allowSceneActivation = false;
            yield return new WaitWhile(() => _async.progress < 0.9f);
    
            _async.allowSceneActivation = true;
    
            yield return new WaitUntil(() => _async.isDone);
    
            // You have to do this before otherwise you might again
            // get by index the previous scene 
            var unloadAsync = SceneManager.UnloadSceneAsync(currentScene);
            yield return new WaitUntil(()=>unloadAsync.isDone);            
    
            var newScene = SceneManager.GetSceneByBuildIndex(index);    
    
            SceneManager.SetActiveScene(newScene);
        }
    }
    
    ObjectPooler.Instance.ReleaseAll();
    SceneManager.LoadSceneAsync(index);