I Had all my Npc's prefab packed into assetbundle with all dependencies,but when I finished loading all dependencies of one npc and instantiate it into scene, some times the animation clip on it can't be played. I print animator.GetCurrentAnimatorClipInfo(0).Length and I found it is 0.So it seems that I had lost all animation clip of this animator. The funny thing is that it doesn't happed on all npc,and I can't find any differences between them.
I used following codes to attached assetbundle name to all assets.
private void SelectionAndAssetsListChange()
{
EditorUtility.ClearProgressBar();
selection = Selection.objects;
//assets = new AssetImporter[selection.Length];
if (selection.Length > 0)
{
for (int i = 0; i < selection.Length; i )
{
string assetPath = AssetDatabase.GetAssetPath(selection[i]);
string[] dps = AssetDatabase.GetDependencies(assetPath);
int length = dps.Length;
int index = 0;
foreach (var dp in dps)
{
if (EditorUtility.DisplayCancelableProgressBar("Changing Assets's ab name ", "Changing No." index "/" length, (float)index / length))
{
EditorUtility.ClearProgressBar();
return;
}
if (dp.EndsWith(".cs"))
continue;
AssetImporter dpAsset = AssetImporter.GetAtPath(dp);
string assetNameDPs = dp.Substring("Assets".Length 1);
assetNameDPs = assetNameDPs.Replace(Path.GetExtension(assetNameDPs), ".data");
assetNameDPs = Path.Combine("assetbundle", assetNameDPs);
assetNameDPs = assetNameDPs.Replace("\\", "/");
dpAsset.assetBundleName = assetNameDPs;
index ;
}
EditorUtility.ClearProgressBar();
}
}
}
Following Codes are used to load assetbundle ,i used 2 dictionaries TempABGO_Dict and TempDPSAB_Dict to make sure make sure it doesn't double load.
IEnumerator SingleTempGOABLoad(string abName)
{
string path = Application.streamingAssetsPath "/" abName;
if (!File.Exists(path))
{
Debug.Log("Can't find path:" path);
yield break;
}
Debug.Log(" 1 SingleTempGOABLoad :" abName);
if (TempABGO_Dict.ContainsKey(abName))
yield break;
string[] allDps = mainManifest.GetAllDependencies(abName);
for (int i = 0; i < allDps.Length; i )
if (TempDPSAB_Dict.ContainsKey(allDps[i]))
{
TempDPSAB_Dict[allDps[i]].refCount ;
}
else
{
DpsContainer dc = new DpsContainer(null, allDps[i]);
TempDPSAB_Dict[allDps[i]] = dc;
StartCoroutine(SingleDpsABLoad(allDps[i]));
yield return 0;
}
for (int i = 0; i < allDps.Length; i )
yield return new WaitUntil(() => TempDPSAB_Dict[allDps[i]].finishLoad);
yield return 0;
AssetBundleCreateRequest ab = AssetBundle.LoadFromFileAsync(path);
yield return ab;
GameObject go = null;
AssetBundleRequest abReq = ab.assetBundle.LoadAllAssetsAsync<GameObject>();
yield return abReq;
go = (GameObject)abReq.asset;
TempABGO_Dict[abName] = new TempPrefabABPair(ab.assetBundle, go, allDps);
}
IEnumerator SingleDpsABLoad(string abName)
{
DpsContainer dc = TempDPSAB_Dict[abName];
AssetBundleCreateRequest ab = null;
ab = AssetBundle.LoadFromFileAsync(Application.streamingAssetsPath "/" abName);
yield return ab;
dc.AB = ab.assetBundle;
AssetBundleRequest rq = ab.assetBundle.LoadAllAssetsAsync();
yield return rq;
dc.finishLoad = true;
}
This is what I used to build BuildAssetBundles.
BuildPipeline.BuildAssetBundles(Application.streamingAssetsPath, compressMode, EditorUserBuildSettings.activeBuildTarget);
CodePudding user response:
First more a general note, you can use
yield return null;
and you can also probably reduce the amount of those a lot e.g. before and after
AssetBundleCreateRequest ab = AssetBundle.LoadFromFileAsync(path);
within the for
loop etc.
And then within in your SingleDpsABLoad
you do not wait for the
ab = AssetBundle.LoadFromFileAsync(....);
to actually finish! You just continue to immediately set
dc.finihed = true
so you might just be lucky that sometimes the rest of the redundant
yield return 0;
in the for loop etc are just delay enough to actually manage to load it in time ^^
So you probably could rather do something like
IEnumerator SingleDpsABLoad(DpsContainer dpsContainer)
{
var path = Path.Combine(Application.streamingAssetsPath, dpsContainer.abName);
if (!File.Exists(path))
{
Debug.LogError("Can't find path:" path);
yield break;
}
var ab = AssetBundle.LoadFromFileAsync(path);
// actually wait until the async operation is done
yield return ab;
// can also make that one async
yield return ab.assetBundle.LoadAllAssetsAsync();
dpsContainer.AB = ab.assetBundle;
dpsContainer.finishLoad = true;
}
IEnumerator SingleTempGOABLoad(string abName)
{
Debug.Log(" 1 SingleTempGOABLoad :" abName);
// personally I would start with that check here already
var path = Path.Combine(Application.streamingAssetsPath, abName);
if (!File.Exists(path))
{
Debug.Log("Can't find path:" path);
yield break;
}
if (TempABGO_Dict.ContainsKey(abName)) yield break;
var allDps = mainManifest.GetAllDependencies(abName);
foreach (var dps in allDps)
{
if (TempDPSAB_Dict.TryGetValue(dps, out var dpsContainer))
{
dpsContainer.refCount ;
}
else
{
dpsContainer = new DpsContainer(null, dps);
TempDPSAB_Dict[dps] = dpsContainer;
StartCoroutine(SingleDpsABLoad(dps));
}
}
// using System.Linq
yield return new WaitUntil(() => TempDPSAB_Dict.All(kvp => kvp.Value.finishLoad));
var ab = AssetBundle.LoadFromFileAsync(path);
yield return ab;
var abReq = ab.assetBundle.LoadAllAssetsAsync<GameObject>();
yield return abReq;
var go = (GameObject)abReq.asset;
TempABGO_Dict[abName] = new TempPrefabABPair(ab.assetBundle, go, allDps));
}