在使用異步接口 yield return AssetBundle.ASyncLoad的時候,難免會想到:這個異步處理完之前如何Cancel掉這個任務?
也就是一個AssetBundle加載到一半,現在要放棄加載,應該怎么處理?
Unity的接口里沒有中斷操作,但是可以在自己項目的ABMgr模塊給業務邏輯層提供一個可中斷的接口。也就是Mgr里加載好資源后,如果業務層不需要了,則不返回/不執行回調。至於這個“不需要”的資源是要緩存,還是卸載就得根據自己項目來了。
分享一個小的Unity文檔上的坑吧,其實我也不知道答案,可能希望有源碼的朋友幫忙看看。
在異步請求一個AssetBundle的時候,會返回一個AssetBundleCreateRequest對象,Unity的官方文檔上寫AssetBundleCreateRequest.assetBundle的時候這樣說:
“
Description
Asset object being loaded (Read Only).
Note that accessing asset before isDone is true will stall the loading process.
”
用了Stall這樣一個詞,我們之前理解Stall是停止的意思,以為這個接口的調用可以將異步請求打斷,所以借助這個特性應用在“同一個AssetBundle正在被異步加載的過程中來了一個同步請求”這樣的情況下。
今天看到這個問題,又讀了讀這個文檔,感覺Stall這個詞並不是停止的意思,而更可能是“拖延”的意思。如果是一個異步加載請求,那么是在另外的線程里執行的,這時候如果有主線程的訪問請求的話,應該會有跨線程的加鎖邏輯,從而可能導致加載變得更慢。如果是停止的話,我覺得文檔應該使用Stop這樣更加明確的單詞……
當然這個是我個人的猜測,並不一定真的是這樣。。。如果有其他朋友知道准確的意思也煩請告知~
碰到的問題和我一樣呢, 最后我們是使用promise方式封裝了資源加載的接口。
返回的handle對象狀態一旦凝固就不會再改變.
////code1
op = loadasset()
yield return op
if op.error != nil then
log("fail=" + op.error)
end
////code2
op:reject("next scene is loading")
看到賈偉昊說到的這個問題,很激動地去看了一下API和UnityReferenceSource。由於項目一直在用5.6版本,2017和2018的AB這塊還沒測試過。5.6是肯定不能取消或者block獲取到AB資源的,看新的Doc里面都提到了能block獲取到資源,referenceSource中AB的API也提到了,asset的API中沒有提到。理解應該是在異步沒有完成之前,訪問資源可以變成同步馬上獲取到該資源,有測試過的同學可以回答一下。如果真是這樣,那就太好了,相信很多項目的設計上都是同步與異步並存的,只是繞過了同步加載一個正在異步加載資源的問題。Unity如果這塊修改了,就可以完美解決同步加載一個正在異步加載資源的問題了。
鑒於Unity Doc被坑過幾次,更相信ReferenceSource里的,猜測AB也許實現了同步訪問正在異步加載的資源,asset沒有實現。
同一幀相同資源先調異步再調同步辦法
上面說的比較原理。我來直接上代碼吧。造福大家
abrequest = AssetBundle.LoadFromFileAsync(path)
abrequest .assetBundle.Unload(false)
ab = AssetBundle.LoadFromFile(path)
這樣會直接同步回來ab,但是會破壞原來異步的回調值 里面abreast.assetbundle 會變空不太好。
還有一種辦法是最后一句改為
asset a =abrequest.assetBundle.asset
這樣原來異步的回調可以正常執行,同步也可以直接返回值。是一個比較好的解決方案。原來代碼的邏輯改動小。
在Unity2018.1.1f上測試了,可以通過獲取bundle的操作來打斷異步操作,使其改為同步加載。具體示例代碼:
AssetBundleCreateRequest abRequest = AssetBundle.LoadFromFileAsync(path);
abRequest.assetBundle.Unload(true);
AssetBundle ab = abRequest.assetBundle;
if (ab == null)
{
ab = AssetBundle.LoadFromFile(path);
var assets = ab.LoadAllAssets();
foreach (var item in assets)
{
Debug.Log(item.name);
}
}
else
{
var assets = ab.LoadAllAssets();
foreach(var item in assets)
{
Debug.Log(item.name);
}
}
如果執行Unload(true), 那么這一行代碼的本質是將異步改為同步,然后加載該bundle,再釋放該bundle,下面會執行LoadFromFile操作。如果注釋掉該執行代碼,則直接獲取assetbundle的操作返回的是同步加載的bundle,會執行else語句塊中的代碼。
大家可以測試一下,我測試沒有問題
會有一個資源鎖,體現在Loading.LickPersistentManager,這時要控制依賴關系及同一時刻提取asset的數量。
感謝回復~