【发布时间】:2020-02-05 22:17:42
【问题描述】:
我目前正在使用 Unity 开发游戏,但遇到了一个小问题。我正在开发一个重启功能,当玩家死亡并再次加载第一个场景时会自动调用它。但是由于某种原因,当重新加载场景时,游戏对象会被复制,死亡时处于活动状态的游戏对象版本处于非活动状态,并且每次玩家死亡时加载的版本应该被加载设置为活动等等将相同游戏对象的新副本添加到层次结构中。我试图以多种方式解决这个问题。首先,尝试检查每个被复制的游戏对象是否已经运行了一个实例,方法是附加一个脚本,每次场景发生变化时检查它们是否已经是存在的游戏对象的实例:
public static GameObject Instance;
void Awake()
{
if(Instance){
DestroyImmediate(gameObject);
}else
{
DontDestroyOnLoad(gameObject);
Instance = this;
}
}
一开始这似乎解决了问题,但到了最后却变得乏味,因为脚本使我所有其他场景对象都表现不佳或根本没有表现,所以我选择寻找其他解决方案。
其次,我尝试在开始加载第一个场景之前销毁每个单独的游戏对象。起初这似乎也有效,但现在我的对象池只是重新创建游戏对象的新实例,它也添加了层次结构,基本上将相同的问题转移到其他游戏对象。
最后,为了解决这个问题,我试图让我的 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);
}
如何更改此设置以便能够重新加载原始场景,而不会复制任何先前加载的 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;
}
}
【问题讨论】:
-
你描述的行为首先听起来很奇怪......我会尝试解决这个问题,而不是试图找到看起来更糟糕的解决方法......加载场景应该重置一切,就像如果游戏开始了。你能展示一下你是如何加载场景的吗?
-
我同意 @derHugo 的观点,听起来你正在破解一种解决方法来部分重置游戏。
-
但这正是我想要发生的事情
-
我在最后添加了负责加载和卸载场景的类
-
看起来副本来自 Unloaded 场景。他们呆在那里并被禁用,直到场景完全卸载。由于
lastLoadedScene的默认值始终为>=0,这可能会导致问题,因为您异步加载和卸载相同的场景?
标签: c# unity3d game-development scene-manager