Unity游戏重启时PersistentScene丢失问题求助
问题描述
我正在开发一款名为Gravity Shifter的游戏,包含四个场景:主菜单(Main Menu)、PersistentScene(承载按钮、生命值文本等UI元素,用于跨关卡UI持久化)、Level1和Level2。需求是:除主菜单外,所有关卡场景都保留PersistentScene,但点击重启按钮后该场景被删除,导致UI元素无法使用。
以下是相关代码:
GameManager.cs
using UnityEngine; using UnityEngine.SceneManagement; // bu kod persistentSceneyi gerekli levellere eklemek icin var. public class GameManager : MonoBehaviour { public static GameManager instance; void Awake() { // Eğer GameManager'ın bir örneği henüz yoksa, bu örneği kalıcı yap if (instance == null) { instance = this; DontDestroyOnLoad(gameObject); // Bu GameObject ve bileşenleri sahne geçişlerinde kalır. } else { // Zaten bir örnek varsa, bu fazladan olanı yok et. Destroy(gameObject); return; } } void Start() { // PersistentScene zaten yüklenmemişse ve main menu sahnesinde degil isek o zaman yukleriz if (!SceneManager.GetSceneByName("PersistentScene").isLoaded && SceneManager.GetActiveScene().name != "MainMenu") { SceneManager.LoadScene("PersistentScene", LoadSceneMode.Additive); } Time.timeScale = 1f; // Level1'e girerken zaman ölçeğini sıfırlıyoruz } // Oyunu sıfırlama fonksiyonu Oyun restart oldugunda bu funcu restart game button scriptin de kullanicaz public void RestartGame() { // Zamanı normal hızda başlat oyun yeniden basladiginda bu deger 0 olursa oyun donar bunu engellemek icin de bunu 1 yapariz Time.timeScale = 1f; PlayerManager.Instance.ResetPlayerPosition(); } }
PlayerManager.cs
using UnityEngine; public class PlayerManager : MonoBehaviour { private static PlayerManager instance; public static PlayerManager Instance { get { return instance; } } public GameObject player; public Vector3 startingPosition; // starting position when game restarts or starts private void Awake() { // Singleton yapısı if (instance == null) { instance = this; DontDestroyOnLoad(gameObject); // PlayerManager, sahne geçişlerinde yok edilmez } else { Destroy(gameObject); // Aynı PlayerManager birden fazla sahnede varsa, yalnızca biri kalır. } } public void ResetPlayerPosition() { if (player != null) { player.transform.position = startingPosition; // Oyuncuyu sıfırla } else { Debug.LogWarning("Player object is not assigned."); } } // Oyuncuyu Don't Destroy'dan çıkar ve sil public void RemovePlayerFromDontDestroy() { Destroy(gameObject); } }
RestartGameButton.cs
using UnityEngine; using UnityEngine.UI; public class RestartGameButton : MonoBehaviour { public GameObject backupCamera; // Yedek kamera referansı (Inspector'dan atanacak) public void RestartGameFromBeginning() { // Yedek kamerayı SceneLoader'a aktar SceneLoader.backupCamera = backupCamera; // Singleton üzerinden GameManager'a erişip RestartGame fonksiyonunu çağır if (GameManager.instance != null) { GameManager.instance.RestartGame(); // GameManager üzerinden oyunu sıfırla } else { Debug.LogError("GameManager instance is not found!"); } SceneLoader.LoadScene(2); // level1'i yuklemek icin } }
SceneLoader.cs
using UnityEngine; using UnityEngine.SceneManagement; public class SceneLoader : MonoBehaviour { public static GameObject backupCamera; // Yedek kamera referansı private static string uiSceneName = "PersistentScene"; // UI'nin sahne adı public static void LoadScene(int sceneIndex) { // Eğer ana menüye dönülüyorsa, tüm sahneleri temizle if (sceneIndex == 1) { DestroyPersistentObjects(); SceneManager.LoadScene(sceneIndex); } else { // Yeni sahneyi yükle (UI kaybolmaz) SceneManager.LoadScene(sceneIndex, LoadSceneMode.Single); // Eğer UI sahnesi yüklü değilse yükle if (!SceneManager.GetSceneByName(uiSceneName).isLoaded) { SceneManager.LoadScene(uiSceneName, LoadSceneMode.Additive); } // Yedek kamerayı aç if (backupCamera != null) { backupCamera.SetActive(true); } } } // Bu sadece ana menüye dönüş için kullanılır private static void DestroyPersistentObjects() { GameObject[] dontDestroyObjects = GameObject.FindObjectsOfType<GameObject>(); foreach (GameObject obj in dontDestroyObjects) { if (obj.scene.buildIndex == -1) // Eğer sahneye bağlı değilse (DontDestroyOnLoad) { Destroy(obj); } } } }
问题原因
点击重启按钮调用SceneLoader.LoadScene(2)加载Level1时,使用了LoadSceneMode.Single模式——该模式会卸载当前所有已加载的场景(包括PersistentScene)。后续代码尝试重新加载PersistentScene,但由于LoadSceneMode.Single是异步操作,检查场景是否加载的代码会在关卡切换完成前执行,导致判断错误,最终没有重新加载PersistentScene。
修复方案
1. 修改SceneLoader.cs的关卡加载逻辑
改用异步加载+手动卸载旧关卡的方式,避免Single模式清空所有场景,同时确保PersistentScene始终被保留:
using UnityEngine; using UnityEngine.SceneManagement; public class SceneLoader : MonoBehaviour { public static GameObject backupCamera; // 备用相机引用 private static string uiSceneName = "PersistentScene"; // UI场景名称 public static void LoadScene(int sceneIndex) { // 返回主菜单时清理持久化对象 if (sceneIndex == 1) { DestroyPersistentObjects(); SceneManager.LoadScene(sceneIndex); } else { Scene currentLevelScene = SceneManager.GetActiveScene(); // 异步加载新关卡,加载完成后处理后续逻辑 SceneManager.LoadSceneAsync(sceneIndex, LoadSceneMode.Additive).completed += (operation) => { // 设置新关卡为激活场景 Scene newScene = SceneManager.GetSceneByBuildIndex(sceneIndex); SceneManager.SetActiveScene(newScene); // 卸载旧关卡(排除PersistentScene) if (currentLevelScene.name != uiSceneName && currentLevelScene.isLoaded) { SceneManager.UnloadSceneAsync(currentLevelScene); } // 确保PersistentScene已加载 if (!SceneManager.GetSceneByName(uiSceneName).isLoaded) { SceneManager.LoadScene(uiSceneName, LoadSceneMode.Additive); } // 启用备用相机 if (backupCamera != null) { backupCamera.SetActive(true); } }; } } // 仅在返回主菜单时调用 private static void DestroyPersistentObjects() { GameObject[] dontDestroyObjects = GameObject.FindObjectsOfType<GameObject>(); foreach (GameObject obj in dontDestroyObjects) { if (obj.scene.buildIndex == -1) // 清理DontDestroyOnLoad标记的对象 { Destroy(obj); } } } }
2. 增强GameManager的场景加载校验
注册场景加载完成事件,确保每次场景切换后都能正确检查并加载PersistentScene:
using UnityEngine; using UnityEngine.SceneManagement; public class GameManager : MonoBehaviour { public static GameManager instance; void Awake() { if (instance == null) { instance = this; DontDestroyOnLoad(gameObject); } else { Destroy(gameObject); return; } } void Start() { SceneManager.sceneLoaded += OnSceneLoaded; Time.timeScale = 1f; } // 场景加载完成后触发 private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { if (scene.name != "MainMenu" && !SceneManager.GetSceneByName("PersistentScene").isLoaded) { SceneManager.LoadScene("PersistentScene", LoadSceneMode.Additive); } } private void OnDestroy() { SceneManager.sceneLoaded -= OnSceneLoaded; } public void RestartGame() { Time.timeScale = 1f; PlayerManager.Instance.ResetPlayerPosition(); } }
3. 优化重启按钮逻辑
调整场景加载与玩家重置的顺序,避免冲突:
using UnityEngine; using UnityEngine.UI; using UnityEngine.SceneManagement; public class RestartGameButton : MonoBehaviour { public GameObject backupCamera; public void RestartGameFromBeginning() { SceneLoader.backupCamera = backupCamera; // 场景加载完成后再重置玩家 SceneManager.sceneLoaded += HandleSceneLoadedForRestart; SceneLoader.LoadScene(2); } private void HandleSceneLoadedForRestart(Scene scene, LoadSceneMode mode) { if (scene.buildIndex == 2 && GameManager.instance != null) { GameManager.instance.RestartGame(); SceneManager.sceneLoaded -= HandleSceneLoadedForRestart; } } }
内容的提问来源于stack exchange,提问作者nuray nasirzade
相关产品推荐
相关产品推荐

