Search Unity

How to add a loading sequence. when importing something at runtime to avoid it going unresponsive

Discussion in 'Scripting' started by XenonSpher, Sep 26, 2016.

  1. XenonSpher

    XenonSpher

    Joined:
    Dec 11, 2015
    Posts:
    29
    Hello. The title speaks for itself. my Unity goes unresponsive when I import an .obj file. The Editor and the built version goes unresponsive but its actually just loading the .obj. What can I do to stop/prevent it from going unresponsive and just create some kind of UI that shows/says its just loading and the reason why its hanging/going unresponsive is because its just loading the .obj file.

    here's a step by step printscreen of what I mean: load1.jpg load2.jpg load3.jpg load4.jpg
     
  2. takatok

    takatok

    Joined:
    Aug 18, 2016
    Posts:
    1,496
    I believe what you are looking for is this:
    https://docs.unity3d.com/ScriptReference/Resources.LoadAsync.html

    Code (CSharp):
    1. ResourceRequest myAsset = Resources.LoadAsync("PathToResource");
    2. // then I'd start a CoRoutine to watch for it:
    3. StartCoroutine(WaitForAsset(myAsset());
    4.  
    5. IEnumerator WaitForAsset(ResourceRequest myAsset)
    6. {
    7.           while (!myAsset.isDone)
    8.                   yield return null;
    9.  
    10.            MethodToCallWhenAssetIsReady();
    11. }
     
  3. XenonSpher

    XenonSpher

    Joined:
    Dec 11, 2015
    Posts:
    29


    sorry but my Importation is a runtime and my .obj file is not inside a Resource Folder inside the Game Folder instead its anywhere in your computer. and uses an url same as like this : (C:/Users/rhylvin2016/Desktop/Build/BackupOBJImporter/Models/Lamborghini/Avent.obj)
     
  4. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,187
    How are you loading the object?
     
  5. XenonSpher

    XenonSpher

    Joined:
    Dec 11, 2015
    Posts:
    29
  6. mgear

    mgear

    Joined:
    Aug 3, 2010
    Posts:
    9,408
  7. takatok

    takatok

    Joined:
    Aug 18, 2016
    Posts:
    1,496
    I assume your calling LoadOBJFile(string fn) ?
    I downloaded the asset and looked at. It looks like when you call that function it runs that one and calls this function:
    public static Material[] LoadMTLFile(string fn) to load materials it finds in your .obj file.

    You could refactor these functions like this:
    Code (CSharp):
    1.  public static GameObject LoadOBJFile(string fn)
    2.     {
    3.              StartCoroutine(LoadOBJFileAsync(fn));
    4. }
    5.  
    6. IEnumerator LoadOBJFileAsync(string fn)
    7. {
    8.          // has all the code LoadOBJFile had in it
    9. }
    Then if you browse the code you'll see LoadObj file has two main foreach loops. You could try putting yield return null at the end of of each iteration through the loops. Or read the actual if / else if / else if blocks and put in yield return nulls where it seems like its doing an operation that might take some time.

    You can make loadMTLFile a Coroutine as well like this:
    Code (CSharp):
    1. public static Material[] LoadMTLFile(string fn)
    2. {
    3.        StartCoroutine(LoadMTLFileAsync(fn);
    4. }
    5. IEnumerator LoadMTLFIleAsync(string fn)
    6. {
    7.       // all the original LoadMTLFile code goes here
    8. }
    just make sure you change the one line that LoadOBJFile calls load MTL:
    Code (CSharp):
    1. materialCache = LoadMTLFile(pth);
    to
    Code (CSharp):
    1. materialCache = yield return LoadMTLFile(pth);
    2.  
    3. // and The final return call in LoadMTLFile to
    4. yield return matlList.ToArray();
     
    Last edited: Sep 26, 2016
  8. XenonSpher

    XenonSpher

    Joined:
    Dec 11, 2015
    Posts:
    29


    do you have any work around with OBJLoader not using Monobehavior? cause StartCorountine needs Monobehavior.
     
  9. takatok

    takatok

    Joined:
    Aug 18, 2016
    Posts:
    1,496
    Remove the static line from every method in ObjLoader.cs , change it to :
    Code (CSharp):
    1. public class OBJLoader : MonoBehavior
    and stick it on an EmptyGame Object in your Scene.
    If you need to use OBJLOader in multiople scenes add DoNotDestroy to its Awake function
     
  10. XenonSpher

    XenonSpher

    Joined:
    Dec 11, 2015
    Posts:
    29
    never mind I think I've found a work around..creating a new monobehavior script and just call OBJLoader.LoadOBJFile from their..by the way.. I've change the script and deleted the static so I can use with other scripts.
     
  11. XenonSpher

    XenonSpher

    Joined:
    Dec 11, 2015
    Posts:
    29
    alright let me try these... hope this works.
     
  12. takatok

    takatok

    Joined:
    Aug 18, 2016
    Posts:
    1,496
    It should.. though the trick will be to sprinkle the right amount of yield returns. If it returns too much it will greatly increase the time it takes to load that object.

    Oh i forgot you'll also have to create a function to call when the LoadOBJFIleAsync Coroutine is done. Otherwise you won't know it finished.

    Or if you using it in multiple places, have LoadOBJFIleAsync have a public boolean variable called isDone. When the Coroutine starts isDone is set to false. Then when the Coroutine ends isDone is set to true.

    Then each script that calls LoadOBJFile can keep checking that boolean in update till it turns true and call their own internal methods

    You can also copy that entire code twice into 2 different methods. (with different names of course). Make one a coroutine with the yield returns added in, and the second one work just like it is. Then you can only call the Async one for big loads.
     
  13. XenonSpher

    XenonSpher

    Joined:
    Dec 11, 2015
    Posts:
    29
    I'm gonna have to make a backup. It seems I need to use your method which is to change all static to nonstatic and add monobehaviour.
     
  14. takatok

    takatok

    Joined:
    Aug 18, 2016
    Posts:
    1,496
    You could copy the entire file and just change all the method names to start with something like MB while your taking out the statics. you'd just have to make sure all the calls you made inside the new function got changed to the new method names. EAsy enough to do, by removing the original asset from your project. Try to compile find all the errors and fix them. Then bring the old asset back in.
     
  15. XenonSpher

    XenonSpher

    Joined:
    Dec 11, 2015
    Posts:
    29
    I ran into problems 2 problems. first of all GameObject LoadOBJFile(string fn) needs a return value since I copied and commented/deleted all the scripts and pasted them to IEnumerator LoadOBJFileAsync(string fn). the second is in the IEnumerator LoadOBJFileAsync the error is in the return value and it says. I cannot implicitly convert GameObject to Inemurator.
     
  16. takatok

    takatok

    Joined:
    Aug 18, 2016
    Posts:
    1,496
    This should get you your object back:
    Code (CSharp):
    1.  public static GameObject LoadOBJFile(string fn)
    2. {
    3.        return StartCoroutine(LoadOBJFileAsync(fn);
    4. }
    5.  
    6. // then the last line of LoadObjFileAsync should be
    7. return parentObject;
    8. // change it to
    9. yield return parentObject;
    I think that might fix the 2nd error as well.
     
  17. takatok

    takatok

    Joined:
    Aug 18, 2016
    Posts:
    1,496
    If the above doesn't work because Coroutines don't work the way I'm expecting them to. You can try this as well:
    Code (CSharp):
    1.  public void LoadOBJFile(string fn,ref GameObject loadedObject)
    2. {
    3.      
    4.        StartCoroutine(LoadOBJFileAsync(fn,ref loadedObject);
    5. }
    Then in the actual LoadObjFileAsync change it to:
    Code (CSharp):
    1. IEnumerator LoadOBJFileAsync(string fn,ref GameObject parentObject)
    2. {
    Then find this line:
    Code (CSharp):
    1. //build objects
    2.         GameObject parentObject = new GameObject(meshName);
    and drop the GameObject declaration since we passed it in
    Code (CSharp):
    1. //build objects
    2.         parentObject = new GameObject(meshName);
    finally get rid of the last return line:
    Code (CSharp):
    1. return parentObject;
    2. //we dont' return anything
    Doing it this way you will need to create an empty GameObject and pass it into the function by reference. Then you have to wait for the Coroutine to be done and access it.
     
  18. XenonSpher

    XenonSpher

    Joined:
    Dec 11, 2015
    Posts:
    29
    Did all you told me. created an empty GameObject and pass in into the fuction by reference
    IEnumerator still needs a return value so I used yield return parentObject.
    and now it gets an error of NullReferenceException.


    Sorry to bother you with this sir.
     
  19. Benj4_

    Benj4_

    Joined:
    Nov 12, 2015
    Posts:
    38
  20. takatok

    takatok

    Joined:
    Aug 18, 2016
    Posts:
    1,496
    Well an IEnumerator function (any coroutine) will complain if it has NO yield returns, so that is why it was probably yelling at you.

    Without seeing all the code its hard to say why you have a null exception. I've thrown around a lot of examples and things to try so I'm not 100% sure the state of the code at the moment. I'd need to see the relevent function declarations, and where you are calling them from.
     
  21. XenonSpher

    XenonSpher

    Joined:
    Dec 11, 2015
    Posts:
    29
    but of course.

    https://www.dropbox.com/s/bt3e4qbzvkcxcex/BackupOBJImporter.rar?dl=0

    alright here's the unedited backup file which means coding that I made from yesterday from your help doesn't exist here. basically this is an OBJ importer which you can also change the position, rotation, scale AND change the material color and material shader of the imported obj at runtime. and you can save all the data. but since runtime importation doesn't really save the obj as a prefab or save it in a resource folder at runtime or leave it at runtime or etc. .. so when you want to load you have to close the app, paste the url again and then press load..

    here's a demo on how to save.



    and then after saving I close the app and play it again or if you're using the editor unplay the editor and then play it again and paste the url again.. and then press load.

    demo of load



    so again the loading of the obj is my problem.. it goes unresponsive.

    by the way the url needs to be all forward slash.. so if the model i added is in here for example.

    C:\Users\rhylvin2016\Desktop\Build\BackupOBJImporter\Models\Lamborghini

    you just need to change all backslash to forward slash and add Avent.obj

    like so

    C:/Users/rhylvin2016/Desktop/Build/BackupOBJImporter/Models/Lamborghini/Avent.obj

    you'll also notice I wrote "Body" as Material Name. it is the name of the materials that are changed and are edited. so just try to use that one as well.

    P.S. I'm using XML to save and load.. the xml file can be found in Assets -> StreamingAssets -> XML -> item_data.xml