1. 程式人生 > >多場景載入

多場景載入

       我們在使用一些外掛的時候,往往都會同步加載出來另外一個場景,這個場景會和我們現有的一個場景共存,而這個場景裡面一般只會有一些指令碼程式碼,或是一些UI,那麼這個到底有什麼用處呢

        前段時間想要封裝一個外掛,但是卡在了多執行緒處理上面,原因當然就是unity根本就沒辦法使用執行緒,但是我很多東西都需要時間,而遞迴或是其他的一些我能想到的方法都是會阻塞main執行緒的,那麼,可不可以在某個地方掛上一個指令碼,通過系統自帶的invoke也好自己寫的也好(更好,因為自己寫的熟悉度比較高,而且可以支援更多的東西),總之,就是有一個繼承了MonoBehaviour的指令碼來讓我們進行時間上的一些操作呢。。。但是,想一下,我們就會知道,我們建立的場景並不會按照Unity裡面自帶的一樣,有可能一個失誤,場景裡面沒有Main相機,或是沒有某些我們可以直接呼叫的物體,這個時候我們就出現了一些很尷尬的事情,當然你可以直接new一個GameObject,在相應的建構函式或是其他的地方,

        那麼,第二個,假設我們需要製作一個遊戲,遊戲有很多場景,豬腳,UI等,那麼我們的豬腳和UI不可能每個場景裡面都Resources或是直接放置上去,而是應該放在一個整個遊戲執行只會出現一次,而且也不會被解除安裝的位置,這個時候,我們也是必須使用多場景進行載入,或是說進行一個場景的單例。。。

       總結,多場景,其實就是在一定程度上模擬場景的單例和多執行緒的一個模擬方式,當然,還有更多用處,比如在臨時載入,或是臨時場景進行更新資料之類的,這裡暫時只是隨便說幾個。。。

==================================================================================================

       多場景的載入其實很簡單,只是我們必須在載入完成之後將載入的場景進行一個“設定為當前活動狀態”(我們指令碼預設生成物體是會生成在當前活動場景的),

UnityEngine.SceneManagement.SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive);

UnityEngine.SceneManagement.SceneManager.LoadScene(sceneName, LoadSceneMode.Additive);


Scene scene = default(Scene);
for (int i = 0; i < SceneManager.sceneCount; i++)
{
    if (SceneManager.GetSceneAt(i).name == sceneName)
    {
       scene = SceneManager.GetSceneAt(i);
    }
}
SceneManager.SetActiveScene(scene);

後面那一個列舉,意思就是我現在需要載入的場景是要和我現在這個場景並存的

第三行則是前面說的,把加載出來的場景進行設定為活動場景,但是注意,這個接受的是Scene型別,所以我們必須先找到這個場景,在設定其啟用,

注意:如果你就這樣寫,程式碼肯定是會報錯的,他會提示你場景並未被載入,無法設定啟用狀態,這是因為我們雖然進行了載入場景,但是其實場景還沒有被載入完成,Unity會認為這個場景根本沒有被載入,但是我們的面板上面卻已經出現了這個場景,所以SceneManager.GetSceneAt(i)可以找到相應的場景。

       關於這種情況,我們可以可以使用一步載入場景,在進度到達100的時候在設定其啟用狀態

    private AsyncOperation async;
    private int ToJourney, YetJourney;
    IEnumerator Loading(string sceneName)
    {
        async = UnityEngine.SceneManagement.SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive);
        async.allowSceneActivation = false;
        while (async.progress < 0.9f)
        {
            ToJourney = (int)(async.progress * 100);
            while (YetJourney < ToJourney)
            {
                YetJourney++;
                ChangeAsyncLoadSceneUI_Slider(YetJourney);
                yield return new WaitForEndOfFrame();
            }
            yield return new WaitForEndOfFrame();
        }
        while (true)
        {
            ToJourney = 100;
            while (YetJourney < ToJourney)
            {
                YetJourney++;
                ChangeAsyncLoadSceneUI_Slider(YetJourney);
                yield return new WaitForEndOfFrame();
            }
            async.allowSceneActivation = true;
            yield return async;
            
            Scene scene = default(Scene);
            for (int i = 0; i < SceneManager.sceneCount; i++)
            {
                if (SceneManager.GetSceneAt(i).name == sceneName)
                {
                    scene = SceneManager.GetSceneAt(i);
                }
            }
            // 在我們載入完成後,可以在這裡選擇時候解除安裝我們上一個場景,因為我是載入了三個場景,
            // 其中只有一個不需要解除安裝,所以才這樣寫
            SceneManager.UnloadScene(SceneManager.GetActiveScene());
            // 注意 這裡會和Awake相差一幀左右,所以最好寫個bool判斷場景是否載入完成
            // M_Manager.AsyncLoadSceneSuccess = true;
            // 載入完成,設定其啟用
            SceneManager.SetActiveScene(scene);
            break;
        }
    }
     private void ChangeAsyncLoadSceneUI_Slider(int value)
    {
        Slider_value.text = "進度->" + value + "%";
        AsyncSlider.value = value;
    }

這樣,我們就在載入其他場景的時候就不會解除安裝我們需要的場景,注意,上面的UnloadScene是需要根據你們程式碼進行修改的,否則有很大可能把你們不想解除安裝的場景解除安裝

放張執行圖 ->

 

        在這裡說一哈,由於我是把豬腳,豬腳UI,非同步載入場景UI都放在了UIScene場景裡面,所以,在test2 場景載入的時候會出現載入的進度,而在UIScene載入的時候則沒有(畢竟非同步載入UI的那個場景都沒有被載入 ~)

        而UIScene場景裡面我放置了兩個相機,平時時候載入場景的相機和載入場景UI(Slider)是隱藏的,而豬腳相應UI的mode則是 RenderMode.ScreenSpaceOverlay,而一旦載入開始,會出現兩個相機並存的情況,但是渲染豬腳和主要UI的相機由於Depth比進度值小,所以主相機會被無視,而UI則會在這個時候被更新為RenderMode.ScreenSpaceCamera,看向豬腳相機,這樣,相關的UI和相機豬腳等就不會影響到我場景載入UI