You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 13:04:58