標籤:產生 coroutine res 卡頓 ogre 連續 記錄 img 沒有
背景
通常遊戲的主情境包含的資源較多,這會導致載入情境的時間較長。為了避免這個問題,可以首先載入Loading情境,然後再通過Loading情境來載入主情境。因為Loading情境包含的資源較少,所以載入速度快。在載入主情境的時候一般會在Loading介面中顯示一個進度條來告知玩家當前載入的進度。在Unity中可以通過調用Application.LoadLevelAsync
函數來非同步載入遊戲情境,通過查詢AsyncOperation.progress
的值來得到情境載入的進度。
嘗試——遇到問題
第一步當載入完Loading情境後,調用如下的LoadGame
函數開始載入遊戲情境,使用非同步載入的方式載入情境1(Loading情境為0,主情境為1),通過Unity提供的Coroutine機制,我們可以方便的在每一幀結束後調用SetLoadingPercentage
函數來更新介面中顯示的進度條的數值。
public void LoadGame() { StartCoroutine(StartLoading_1(1));}private IEnumerator StartLoading_1(int scene) { AsyncOperation op = Application.LoadLevelAsync(scene); while(!op.isDone) { SetLoadingPercentage(op.progress * 100); yield return new WaitForEndOfFrame(); } }
最後進度條的效果顯示如下:
進度條並沒有連續的顯示載入的進度,而是停頓一下切換一個數字,再停頓一下切換一個數子,最後在沒有顯示100%就情況下就切換到主情境了。究其原因在於Application.LoadLevelAsync
並不是真正的後台載入,它在每一幀載入一些遊戲資源,並給出一個progress值,所以在載入的時候還是會造成遊戲卡頓,AsyncOperation.progress
的值也不夠精確。當主情境載入完畢後Unity就自動切換情境,所以上述代碼中的while迴圈體內的代碼是不會被調用的,導致進度條不會顯示100%。
修補——100%完成
為了讓進度條能顯示100%,取巧一點的辦法是將AsyncOperation.progress
的值乘上2,這樣當載入到50%的時候介面上就顯示100%了。缺點是當介面上顯示100%的時候,使用者還要等待一段時間才會進入遊戲。其實Unity提供了手動切換情境的方法,把AsyncOperation.allowSceneActivation
設為false
就可以禁止Unity載入完畢後自動切換情境,修改後的StartLoading_2
代碼如下:
// this function is not workprivate IEnumerator StartLoading_2(int scene) { AsyncOperation op = Application.LoadLevelAsync(scene); op.allowSceneActivation = false; while(!op.isDone) { SetLoadingPercentage(op.progress * 100); yield return new WaitForEndOfFrame(); } op.allowSceneActivation = true; }
我們首先將AsyncOperation.allowSceneActivation
設為false
,當載入完成後再設為true
。代碼看上去沒有錯,但是執行的結果是進度條最後會一直停留在90%上,情境不會切換。通過列印log發現AsyncOperation.isDone
一直為false
,AsyncOperation.progress
的值增加到0.9後就保持不變了,也就是說情境永遠不會被載入完畢。
在這個文章中找到了答案,原來把allowSceneActivation
設定為false
後,Unity就只會載入情境到90%,剩下的10%要等到allowSceneActivation
設定為true
後才載入,這不得不說是一個坑。所以代碼改為如下。當AsyncOperation.progress
到達0.9後,就直接把進度條的數值更新為100%,然後設定AsyncOperation.allowSceneActivation
為ture
,讓Unity繼續載入未完成的情境。
private IEnumerator StartLoading_3(int scene) { AsyncOperation op = Application.LoadLevelAsync(scene); op.allowSceneActivation = false; while(op.progress < 0.9f) { SetLoadingPercentage(op.progress * 100); yield return new WaitForEndOfFrame(); } SetLoadingPercentage(100); yield return new WaitForEndOfFrame(); op.allowSceneActivation = true; }
最後的效果如下:
打磨——增加動畫
上述的進度條雖然解決了100%顯示的問題,但由於進度條的數值更新不是連續的,所以看上去不夠自然和美觀。為了看上去像是在連續載入,可以每一次更新進度條的時候插入過渡數值。這裡我採用的策略是當獲得AsyncOperation.progress
的值後,不立即更新進度條的數值,而是每一幀在原有的數值上加1,這樣就會產生數字不停滾動的動畫效果了,迅雷中顯示下載進度就用了這個方法。
private IEnumerator StartLoading_4(int scene) { int displayProgress = 0; int toProgress = 0; AsyncOperation op = Application.LoadLevelAsync(scene); op.allowSceneActivation = false; while(op.progress < 0.9f) { toProgress = (int)op.progress * 100; while(displayProgress < toProgress) { ++displayProgress; SetLoadingPercentage(displayProgress); yield return new WaitForEndOfFrame(); } } toProgress = 100; while(displayProgress < toProgress){ ++displayProgress; SetLoadingPercentage(displayProgress); yield return new WaitForEndOfFrame(); } op.allowSceneActivation = true;}
displayProgress
用來記錄要顯示在進度條上的數值,最後進度條的動畫如下:
對比第一種的進度條
總結
如果在載入遊戲主情境之前還需要解析資料表格,產生對象池,進行網路連接等操作,那麼可以給這些操作賦予一個權值,利用這些權值就可以計算載入的進度了。如果你的情境載入速度非常快,那麼可以使用一個假的進度條,讓玩家看上幾秒鐘的loading動畫,然後再載入情境。總之進度條雖然小,但要做好也是不容易的。
https://docs.unity3d.com/ScriptReference/SceneManagement.SceneManager.LoadSceneAsync.html
https://docs.unity3d.com/ScriptReference/AsyncOperation.html
AsyncOperation Description
Asynchronous operation coroutine.
You can yield until asynchronous operation continues, or manually check whether it‘s done (isDone) or progress (progress).
See Also: SceneManager.LoadSceneAsync, AssetBundle.LoadAsync.
Variables
allowSceneActivation |
Allow scenes to be activated as soon as it is ready. |
isDone |
Has the operation finished? (Read Only) |
priority |
Priority lets you tweak in which order async operation calls will be performed. |
progress |
What‘s the operation‘s progress. (Read Only) |
【轉】Unity3d中製作Loading情境進度條所遇到的問題 LoadLevelAsync,AsyncOperation