I need to accomplish something specific but I'm not sure how to do it exactly:

My game has 2 scenes:

  • The main menu that has a couple of buttons...
  • ... and the game scene that displays a different mesh (which is imported from a folder on the HDD at runtime) depending on which button you pressed and that also lets you walk around.

My approach for this: Clicking on a button makes a script A call a static class B (used for sharing data between scripts), which then in turn creates an instance of another class C, which inherits from ScriptableObject and takes care of the whole "import and display the mesh" part.

Importing a mesh takes a while, so I'd like to display some kind of loading screen (a panel that overlays everything else in the 2nd scene) and this is where I'm stuck.

Importing a mesh accesses the Unity API, so I can't do it in an extra thread (or Task, which would be perfect) but I also can't use a coroutine because class C derives from ScriptableObject, not MonoBehaviour (which I can't use because I can't create a new instance then).

Another problem is that the game is stuck on the main scene with the button pressed and doesn't display the second scene until the mesh is loaded, so even if I set the loading panel active (loadingPanel.setActive(true)) before I start importing the mesh, it doesn't display it until after the model's already imported.

What can I do here? There's no need to display a loading screen while switching scenes, so I'm guessing that there's no point to load the second scene asynchronously, is there?

  • \$\begingroup\$ "MonoBehaviour (which I can't use because I can't create a new instance then)" Can you explain this? Unity code tends to create new instances of MonoBehaviours quite frequently. \$\endgroup\$ – DMGregory Aug 29 '18 at 15:45
  • \$\begingroup\$ @DMGregory If a class derives from MonoBehaviour you can't do (okay, you could but it shows a warning) MyMonoBehaviourClass mmbc = new MyMonoBehaviourClass();. This script isn't attached to any Object but I need to get access to it from my static class and since I'm creating GameObjects in it, I can't make it a second static class. ScriptableObject was what I found to be working. \$\endgroup\$ – Neph Aug 30 '18 at 8:24
  • \$\begingroup\$ What you mean is "I can't create a new instance with new" but that's hardly the only way to make an instance of something. AddComponent<>() makes a new instance of a MonoBehaviour just fine, if you find need for it. \$\endgroup\$ – DMGregory Aug 30 '18 at 10:07
  • \$\begingroup\$ @DMGregory AddComponent<>() assumes that you have a GameObject to add the script as a component to but that's not the case for me. \$\endgroup\$ – Neph Aug 30 '18 at 10:38
  • \$\begingroup\$ Your script already creates GameObjects, so you can have a GameObject to add a component to anytime you want. If that's a route you choose not to take, that's fine, it's just clearer to write "I'd prefer not to create a new GameObject/MonoBehaviour instance for this (for reasons XYZ)" as opposed to "I can't." \$\endgroup\$ – DMGregory Aug 30 '18 at 10:44

The reason why the button gets stuck until the scene loads is, you are not loading the mesh in a parallel thread or a coroutine that allows the game loop to keep ticking, hence, you are loading it in a concurrent environment, which means that the next frame will tick once that mesh (and whatever more resources) finish loading.

What you can do is having an IEnumerator (what is used for coroutine'a pattern in C#) on your ScriptableObject, but call it on whatever MonoBehaviour Instance you have. Since you didn't provide example code, I'll just leave the kinda IEnumerator's template:

public class DataContainer : ScriptableObject
    private IEnumerator AsynchronousStuff()
            yield return null;

So you could do something like that, which is perfectly valid, since StartCoroutine takes an IEnumerator, it does not matter where it comes from, the MonoBehaviour is needed to make the call, since it runs while that instance is active:

public class SceneController : MonoBehaviour
    public DataContainer data;
    private Coroutine loadingOperation;

    public void LoadResources()
        /// By storing a reference in a coroutine, you could check if there is an operation running, so you don't execute it by accident again
        loadingOperation = StartCoroutine(data.AsynchronousStuff());

You could do it on threading, just take values that are allowed, in the case of UnityEngine's namespace, structs (Vectors, Quaternions, Colors, Bounds, Rect, Rays, etc.) are allowed for multithreading, so you could also modify structs, and once the thread is done, interpret that data to create meshes / resources with the given data.

Hope it helps.

  • \$\begingroup\$ I'm aware that the game would get stuck if I instantly tried to load the mesh but I don't. I call SceneManager.LoadScene("Game"); in the 1st scene, then a script that is attached to the camera in the 2nd scene calls a method within the static class that in turn creates a new instance of class C. This class does some stuff (e.g. create a LineRenderer that's displayed too) and only then I'm importing the mesh. What I'm confused about is that it does all of this and also imports the mesh while it's frozen showing the buttons. It doesn't load the 2nd scene and only then freezes while importing. \$\endgroup\$ – Neph Aug 30 '18 at 8:33
  • \$\begingroup\$ I see - I'll try starting the coroutine in the Awake method of the script that's attached to the camera (let's just call it "script 0"), thanks. I'd love to just use a normal thread but I'm using an asset for importing that's sadly not split up between "thread-safe" and "not thread-safe" but just returns a finished GameObject. \$\endgroup\$ – Neph Aug 30 '18 at 8:45
  • \$\begingroup\$ Starting the coroutine from the other MonoScript worked - thanks! \$\endgroup\$ – Neph Aug 31 '18 at 8:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.