我目前正在Unity中开发游戏,但遇到了一个小问题。我正在开发一个重启功能,当玩家死亡并再次加载第一个场景时会自动调用该功能。但是由于某种原因,在重新加载场景时,游戏对象会与死亡时处于活动状态的游戏对象版本处于非活动状态,并且要加载的版本应被设置为活动状态,等等。将相同游戏对象的新副本添加到层次结构中。我试图以多种方式解决此问题。首先,通过附加一个脚本来检查每个复制的游戏对象是否已经运行了自己的实例,该脚本将在每次场景变化发生时检查是否已经存在了这些游戏对象的实例:
public static GameObject Instance;
void Awake()
{
if(Instance){
DestroyImmediate(gameObject);
}else
{
DontDestroyOnLoad(gameObject);
Instance = this;
}
}
这似乎一开始就解决了这个问题,但是由于脚本使我所有其他场景对象的表现都差强人意或根本不起作用,所以直到最后变得捉襟见肘,所以我选择了另一种解决方案。
第二,我尝试在开始加载第一个场景之前销毁每个单独的游戏对象。乍看起来这似乎也可行,但现在我的对象池只是重新创建了游戏对象的新实例,它也添加了层次结构,本质上将同一问题转移到了其他游戏对象上。
最后,为了解决此问题,我试图使我的对象池仅在需要加载的场景被调用时运行一次,但这似乎也不起作用。有谁知道我如何解决这个问题。这是脚本的一部分,负责在玩家死亡时加载原始场景:
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);
}
我该如何更改此设置,以便能够重新加载原始场景而无需复制任何先前加载的GameObject
并根据其在原始加载场景中的表现如何运行?
负责加载和卸载场景的类:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public static class MySceneManager
{
private static int lastLoadedScene = 0;
public static void LoadScene(int index, MonoBehaviour caller)
{
ObjectPooler objP = new ObjectPooler();
objP.ReleaseAll();
caller.StartCoroutine(loadNextScene(index));
}
private static IEnumerator loadNextScene(int index)
{
var _async = SceneManager.LoadSceneAsync(index, LoadSceneMode.Additive);
_async.allowSceneActivation = false;
while (_async.progress < 0.9f)
{
yield return null;
}
_async.allowSceneActivation = true;
while (!_async.isDone)
{
yield return null;
}
var newScene = SceneManager.GetSceneByBuildIndex(index);
if (!newScene.IsValid()) yield break;
SceneManager.SetActiveScene(newScene);
if (lastLoadedScene >= 0) SceneManager.UnloadSceneAsync(lastLoadedScene);
lastLoadedScene = index;
}
}
这是我的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;
}
}
答案 0 :(得分:0)
根据您的需求,您可以尝试以下方法:
SceneManager.LoadScene
作为参数使用LoadSceneMode.Single
方法。它将保留DontDestoryOnLoad
个对象,但将删除所有其他对象。答案 1 :(得分:0)
我不确定,但是对我来说,第一个可能的问题似乎是它正在协程中在要卸载的场景中的对象上运行。
这样做很酷,但是请记住,一旦调用者对象/组件被破坏或禁用,协程将立即停止工作。
为避免这种情况,我将使用Singleton模式将脚本移动到DontDestroyOnLoadScene
中的对象。
下一个问题可能是您要按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);
}
}
或者,无论如何,您在加载/卸载场景时没有做任何特别的事情:
如果您还可以简单地调用
,为什么要完全使用Additive
场景加载
ObjectPooler.Instance.ReleaseAll();
SceneManager.LoadSceneAsync(index);
没有添加它,因此只要新场景完全加载,就可以自动将当前场景自动删除。
注意:可以在智能手机上输入内容,因此没有保修,但我希望这个想法会清楚