2
\$\begingroup\$

I'm using [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] to initialize a prefab that contains all the components required for the game to work propertly.

public class FrameworkInitializer : MonoBehaviour
{
    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
    public static void InitializeFramework()
    {
       Instantiate(Resources.Load("_GameEngine")) as GameObject;
    }
}

It works fine.

Now I'm creating some scenes to try out things before implementing them in the final game, and I don't really need the default prefab to initialize. I tried adapting the script to this:

public class FrameworkInitializer : MonoBehaviour
{
    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
    public static void InitializeFramework()
    {
        if (GameObject.FindWithTag("Framework Support") != null) return;
        if (GameObject.Find("NoFrameworkSupportRequired") != null)
        {
            Debug.LogWarning("Carla Framework initializer skipped because of NoFrameworkSupportRequired gameobject in scene.");
            return;
        }
        var framework = Instantiate(Resources.Load("_GameEngine")) as GameObject;
    }
}

But it's not working, because the scene is not loaded yet so it can't find any gameobject.

And if I change BeforeSceneLoad to AfterSceneLoad, some of the scripts that require the prefab in the Awake and onEnable cycle fails.

How can I make this script to run only on some of the scenes?

\$\endgroup\$
4
  • \$\begingroup\$ AfterSceneLoad should call this function after Awake, which is still before Start. Are you able to move the behaviour that relies on it from Awake or OnEnable into Start? If not, then you might want to use a lazily initialized singleton for this purpose instead, where it's spawned on demand when something tries to use it. \$\endgroup\$
    – DMGregory
    Commented Apr 28, 2021 at 10:57
  • \$\begingroup\$ You're right, AfterSceneLoad call the function before Start. I'll update the question. Some of the components that require the framework support need it on Awake or Enable as you said, but I shouldn't edit them (because some of them are purchased assets). Will it work if I setup a PlayerPrefs called "develop" or something like that, to decide if the FrameworkInitializer should run? Can I read PlayerPrefs there? \$\endgroup\$
    – Haytam95
    Commented Apr 28, 2021 at 13:31
  • \$\begingroup\$ What do your tests tell you? Honestly though, this seems like a square peg for a round hole. Are you sure you wouldn't prefer to spawn your _GameEngine lazily, so it's there as soon as someone asks for it? \$\endgroup\$
    – DMGregory
    Commented Apr 28, 2021 at 13:36
  • \$\begingroup\$ My _GameEngine prefab is just a wrapper with DontDestroyOnLoad. It contains other child gameobjects that really do the work and acts like singleton. For example, if someone needs to call the DialogueSystem, it'll just do: DialogueSystemManager.Instance . _GameEngine doesn't really know when a script needs something inside it. I don't know how to make _GameEngine aware of this, to make it load lazily \$\endgroup\$
    – Haytam95
    Commented Apr 28, 2021 at 13:53

2 Answers 2

0
\$\begingroup\$

I think you might want to make your _GameEngine/framework the singleton.

public class Framework : MonoBehaviour {
    static Framework _instance;
    public static Framework Instance {
        get {
            if (_instance == null) {
                _instance = Instantiate(Resources.Load<Framework>("_GameEngine"));                    
            }
            return _instance;
        }
    }

    // On the _GameEngine prefab, assign references to each of the child components
    // that other scripts might want to look up.
    public DialogueSystemManager dialogue;
    public OtherManager other;
    // etc.
}

Now this behaves as a service locator. Anything else in your scene that wants to access a singleton manager does so via the Framework. ie..

DialogueSystemManager.Instance

becomes

Framework.Instance.dialogue

Now anytime anything references a manager contained in the Framework, it does so via the Framework singleton's lazy instantiation, ensuring it exists "just in time", but only when needed, so it won't get spawned in scenes where no one asks for it.

\$\endgroup\$
5
  • \$\begingroup\$ That's great and I'm sure that will help out other users. I'm affraid that I'll need to refactor the code that uses those instances (And that means edit various purchased assets, just in the Dialogue system are more than 30 files). I think that in this case the cure will be worse than the sickness, but I don't really see other way to do it. \$\endgroup\$
    – Haytam95
    Commented Apr 28, 2021 at 14:30
  • \$\begingroup\$ If there's one place in your scene where something tries to access the dialogue system directly that you can't edit, you could always throw a RequireFramework component on that object that accesses Framework.instance in Awake, so it ensures the dialogue manager exists by the time the unmodifiable script asks for it. \$\endgroup\$
    – DMGregory
    Commented Apr 28, 2021 at 14:33
  • \$\begingroup\$ That's actually a great idea. One last doubt: How can I ensure that RequireFramework component Awake cycle executes before other components in the object? (Perhaps I'm wrong, but I understand that the components doesn't load in order) \$\endgroup\$
    – Haytam95
    Commented Apr 28, 2021 at 15:07
  • \$\begingroup\$ I think you can control the relative order of Awake calls for components loaded at the same time with Script Execution Order settings \$\endgroup\$
    – DMGregory
    Commented Apr 28, 2021 at 15:13
  • \$\begingroup\$ Great! I think this will work way better than my original RuntimeInitializeOnLoadMethod. Thank you \$\endgroup\$
    – Haytam95
    Commented Apr 28, 2021 at 15:22
0
\$\begingroup\$

I had the same question when setting up some test scenes that I did not want to use my manager GameObject. I am using a similar method, also setting DontDestroyOnLoad after instantiation.

I ended up checking the scene name beforehand. The method using RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad) does appear to know the name of the scene, even though it has not loaded yet.

Include in your namespaces:

using UnityEngine.SceneManagement;  

And then do whatever check you want to do in the function before the instantiation. In my case, I am excluding any Scene that starts with "Test", along with the default scene name created by Unit Tests (e.g. "InitTestScene637801810867719397"):

Scene scene = SceneManager.GetActiveScene();
if (!scene.name.StartsWith("Test") && !scene.name.StartsWith("InitTestScene"))
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .