Search Unity

How to call IEnumerator in a loop without yielding to the next frame each time??/

Discussion in 'Scripting' started by sgower, May 25, 2017.

  1. sgower

    sgower

    Joined:
    Mar 9, 2015
    Posts:
    316
    The Unity AssetBundleManager seems to only have a "LoadAssetAsync" method, and no "LoadAsset" (non-async) method.

    So to load an asset I use code like this:

    Code (CSharp):
    1.         AssetBundleLoadAssetOperation request = AssetBundleManager.LoadAssetAsync(assetBundleName, assetName, typeof(GameObject));
    2.         yield return StartCoroutine(request);
    My question how to call this method multiple times without yielding (which moves to the next frame) for each call. Say for example I have 500 assets to load, the process takes forever, and I can watch the objects pop onto the screen 1 by one.

    Before using AssetBundles, I was just using Resource.Load and calling this non-async. The game would blank out for a short period during the load, but this was an acceptably short time. It was probably 10 times faster than the Async method.
     
  2. andymads

    andymads

    Joined:
    Jun 16, 2011
    Posts:
    1,614
    I don't have much experience with asset bundles and I see that AssetBundleManager is not in the Script Reference so I assume it's a helper class provided by Unity.

    But, for Unity asynchronous operations in general, e.g. Scene and AssetBundle loading, an object of type AsyncOperation (or a derivative) is returned. You can yield on it or check the isDone property know when it's finished.

    You don't have to yield after every load, and in fact you don't have to yield at all, but you'll probably want some way of knowing that the operation has completed. You should be able to make multiple load calls in one go but I don't know what the ramifications of doing 500 would be.
     
  3. sgower

    sgower

    Joined:
    Mar 9, 2015
    Posts:
    316
    Hi thanks. Yikes. I'm really stuck. I'm not sure what I did, but my code is working even worse than before!


    Code (CSharp):
    1.  
    2.         Debug.Log("in loadDemPrefabs and prefabCount=" + prefabCount);
    3.         for (int i = 0; i < prefabCount; i++) {
    4.             Debug.Log("loading prefab:" + i);
    5.             yield return LoadPrefab(i, savefilePath);
    6.             // LoadPrefab(i, savefilePath);
    7.         }
    8.         Debug.Log("done loading prefabs");
    Can you think of any reason why the above code wouldn't make it through the loop?

    The Debug.Log output just shows:
    in loadDemPrefabs and prefabCount=5
    loading prefab:0

    So it should be looping through 5 times, but now it's getting stuck after the first call. Can you think of any reason why this might be happening?

    But even if I get around this current issue, I'm still back to the fact that I need to call "return yield LoadPrefab", and each yield waits a frame. Because the LoadPrefab is an IEnumerator, it seems to need to be called with "return yield" before it. If I call methods like this without the "return yield", the method doesn't get called at all.

    I'd sure love any advice you might have!
     
  4. sgower

    sgower

    Joined:
    Mar 9, 2015
    Posts:
    316
    This new issue doesn't seem to be related to my LoadPrefab method.

    I created this method:

    Code (CSharp):
    1.     public IEnumerator testEnumerator() {
    2.         Debug.Log("in testEnumerator!");
    3.         yield return null;
    4.     }
    5.  
    And then call it like this:

    Code (CSharp):
    1.  
    2.         Debug.Log("calling TestEnumerator");
    3.         yield return testEnumerator();
    4.         Debug.Log("after calling TestEnumerator");
    And the Debug.Log shows:
    calling TestEnumerator

    So even with this simple example it's getting stuck. There are no errors in the console, the method just stops execution.

    if I change the code to this:

    Code (CSharp):
    1.         Debug.Log("calling TestEnumerator");
    2.         yield return StartCoroutine(testEnumerator());
    3.         Debug.Log("after calling TestEnumerator");
    Then the Debug.Log shows:
    callingTestEnumerator
    in TestEnumerator!

    So it at least enters the testEnumerator() method, but still gets stuck. This seems very strange to me. I can't imagine what might be happening.
     
  5. sgower

    sgower

    Joined:
    Mar 9, 2015
    Posts:
    316
    Ok, got it fixed and I learned something today.

    I moved the prefab load loop into it's own IEumerator method:

    Code (CSharp):
    1.     public IEnumerator loadDemPrefabs(int prefabCount, string savefilePath) {
    2.         Debug.Log("in loadDemPrefabs and prefabCount=" + prefabCount);
    3.         for (int i = 0; i < prefabCount; i++) {
    4.             Debug.Log("loading prefab:" + i);
    5.           //  yield return LoadPrefab(i, savefilePath);
    6.             IEnumerator e4=LoadPrefab(i, savefilePath);
    7.             while (e4.MoveNext()) {
    8.                 yield return i;
    9.             }
    10.             // LoadPrefab(i, savefilePath);
    11.         }
    12.         Debug.Log("done loading prefabs");
    13.         // yield return null;
    14.     }
    And instead of "yield return LoadDemPrefabs", I do this:
    Code (CSharp):
    1.   IEnumerator e3= loadDemPrefabs(prefabCount, savefilePath);
    2.         while (e3.MoveNext()) {
    3.             Debug.Log("Iterator function returned: " + e3.Current);
    4.         }
    I finally have a little better understanding of how coroutines work. Thanks to this article. If anyone else out there is fuzzy about how coroutines, it's definitely a good read:

    http://jacksondunstan.com/articles/3036