Unity 摧毀 DontDestroyOnLoad 遊戲物件


建立時間: 2023年5月12日 10:53
更新時間: 2023年5月12日 13:42

說明

在 Unity 只要調用 DontDestroyOnLoad(gameObject)gameObject 就不會因為切換場景而被摧毀,但問題是如果又想摧毀 gameObject 該怎麼辦?本篇將告訴大家我是如何摧毀的。

DontDestroyOnLoad 簡述

如果調用 DontDestroyOnLoad(gameObject),你會在場景中看到新增一個 DontDestroyOnLoad 場景,在這個場景中的遊戲物件不會因為載入新場景 SceneManager.LoadScene("NewScene"); 而被摧毀。

找不到內建的解除 DontDestroyOnLoad

上網查了如何解除 DontDestroyOnLoad,但很可惜沒有找到相關的方法可用。

GetSceneByName 方法無效

我嘗試取得 DontDestroyOnLoad 場景的遊戲物件

Scene scene = SceneManager.GetSceneByName("DontDestroyOnLoad");

if (scene.isLoaded)
{
    // 進不來這裡
}

如上,就算我不判斷 scene.isLoaded,硬要取得裡面的遊戲物件也會發生錯誤,具體為什麼會錯誤我也不太清楚,我只能先放棄這個方向了。

摧毀 DontDestroyOnLoad 遊戲物件的方法

摧毀 DontDestroyOnLoad 場景的遊戲物件有兩種方法。

  1. 直接取得遊戲物件使用 Destroy 方法摧毀。
  2. 將 DontDestroyOnLoad 場景的遊戲物件,搬移到 DontDestroyOnLoad 場景之外。

上述兩個方法都需要先取得遊戲物件才行。

實作 Destroyer

根據上面的資訊,最後我實作 Destroyer 專門用來摧毀 DontDestroyOnLoad 遊戲物件,原理很簡單將要 DontDestroyOnLoad 遊戲物件存取在 Destroyer 的 indestructibleGameObjects 變數中,統一管理不會被摧毀的遊戲物件,之後想要怎麼摧毀都好處理。

如果只有極少個遊戲物件要摧毀,你可以直接取得遊戲物件使用 Destroy 方法摧毀會比較省事。

Destroyer.cs

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

/// <summary>
/// 用來摧毀 DontDestroyOnLoad 遊戲物件
/// </summary>
public class Destroyer : MonoBehaviour
{
    /// <summary>
    /// 單例實體
    /// </summary>
    private static Destroyer instance;

    /// <summary>
    /// 被加入在 DontDestroyOnLoad 的遊戲物件
    /// </summary>
    private static List<GameObject> indestructibleGameObjects;

    /// <summary>
    /// 單例實體
    /// </summary>
    public static Destroyer Instance
    {
        get => instance;
    }

    private void Awake()
    {
        if (instance != null)
        {
            Destroy(gameObject);

            return;
        }
        instance = this;
        DontDestroyOnLoad(instance);
        indestructibleGameObjects = new();
    }

    /// <summary>
    /// 同 DontDestroyOnLoad 但會紀錄物件用來管理
    /// </summary>
    /// <param name="_gameObject"></param>
    public void DontDestroyOnLoad(GameObject _gameObject)
    {
        indestructibleGameObjects.Add(_gameObject);

        Object.DontDestroyOnLoad(_gameObject);
    }

    /// <summary>
    /// 將 DontDestroyOnLoad 場景的遊戲物件移到當前場景,讓遊戲物件可以被清除。
    /// </summary>
    public void MoveGameObjects()
    {
        while (indestructibleGameObjects.Count > 0)
        {
            GameObject _gameObject = indestructibleGameObjects[0];
            indestructibleGameObjects.RemoveAt(0);

            SceneManager.MoveGameObjectToScene(_gameObject, SceneManager.GetActiveScene());
        }
    }
}

這裡有用到單例模式,Destroyer 本身也是被設計為 DontDestroyOnLoad,裡面的方法都有註解說明,我就不再贅述。

使用方式

新增一個空遊戲物件取名為 Destroyer,然後將添加 Destroyer 組件。

接著將 DontDestroyOnLoad 方法取代為 Destroyer.Instance.DontDestroyOnLoad(),需注意如果在 Awake 方法呼叫 Destroyer.Instance.DontDestroyOnLoad() 可能會還沒找到 Destroyer 遊戲物件,因為這裡只用了最簡單的單例模式,Instance 沒有判斷是不是 null,只有單純回傳單例的實體而已。

private void Start()
{
    Destroyer.Instance.DontDestroyOnLoad(gameObject);
}

結果

這是一個很陽春的組件,如果有其他功能需求,再自己新增上去就好,這裡主要想分享我在摧毀 DontDestroyOnLoad 遊戲物件時遇到的問題,和解決辦法。

觀看次數: 1501
destroydontdestroyonloadunity
按讚追蹤 Enjoy 軟體 Facebook 粉絲專頁
每週分享資訊技術

一杯咖啡的力量,勝過千言萬語的感謝。

支持我一杯咖啡,讓我繼續創作優質內容,與您分享更多知識與樂趣!