60

I'm making simple game manager. I have a script, which will be accessible from all scenes in the game. And I need to check values of its variables after loading new scene. But my code runs only once after starting the simulation while an object with this script exists in all scenes. What is wrong? Why doesn't it work after loading a new scene?

9
  • 1
    The start is only meant to be called once and since you use DontDestroyOnLoad, this one is not happening again. The object stays in all scene because of DontDestroyOnLoad. Not sure why OnLevelWasLoaded would not trigger though.
    – Everts
    Mar 9, 2016 at 12:00
  • 1
    since The Awake function is called on all objects in the scene before any object's Start function is called. Probably Start function is not triggered because of this situation. Have you ever tried to call OnLevelWasLoaded function? Mar 9, 2016 at 12:02
  • 1
    Pls try OnEnable. And look at this. Mar 9, 2016 at 12:16
  • @BarışÇırıka unfortunately id don't work too..and on the first scene it works just because after each load it create a new instance of the object with the script, I need to fix it. So it don't work for Start and Awake too
    – Dima
    Mar 9, 2016 at 12:26
  • No he can use. But in this code block it seems useless. If you want to apply singleton pattern you can use like this. Check this link. Mar 9, 2016 at 12:38

5 Answers 5

119

In every Unity project you must have A PRELOAD SCENE.

It is quite confusing that Unity does not have a preload scene "built-in".

They will add this concept in the future.

Fort now you have to click to add a preload scene yourself.

This is the SINGLE GREATEST MISUNDERSTANDING for new programmers trying Unity!


Fortunately, it is extremely easy to have a preload scene.

Step 1.

Make a scene named "preload". It must be scene 0 in Build Manager.

enter image description here

Step 2.

In the "preload" scene make an empty GameObject called, say, "__app".

Simply, put DontDestroyOnLoad on '__app'.

Note:

This is the only place in the whole project you use DontDestroyOnLoad.

It's that simple.

enter image description here

In the example: the developers have made a one-line DDOL script.

Put that script on the "__app" object.

You never have to think about DDOL again.

Step 3

Your app will have (many) "general behaviors". So, things like database connectivity, sound effects, scoring, and so on.

You must, and can only, put your general behaviors on "_app".

It's really that simple.

The general behaviors are then - of course - available everywhere in the project, at all times, and in all scenes.

How else could you do it?

In the image example above, notice "Iap" ("in-app purchase") and the others.

All of your "generally-needed behaviors" - sound effects, scoring, and so on - are right there on that object.

Important...

This means that - of course, naturally -

...your general behaviors will have ordinary Inspectors, just like everything else in Unity.

You can use all the usual features of Unity, which you use on every other game object. Inspector variables, drag to connect, settings, and so on.

(Indeed: say you've been hired to work on an existing project. The first thing you will do, is glance at the preload scene. You will see all the "general behaviors" in the preload scene - sound effects, scoring, AI, etc etc. You will instantly see all the settings for those things as Inspector variables ... speech volume, playstore ID, etc etc.)

Here's an example "Sound effects" general behavior:

enter image description here

Looks like there's also a "voice over" general behavior, and a "music" general behavior".

To repeat. Regarding your "general behaviors". (Sound effects, scoring, social, etc etc.) These CAN ONLY GO on a game object in the preload scene.

This is not optional: there's no alternative!

It's that easy.

Sometimes engineers coming from other environments get caught up on this, because it seems like "it can't be that easy".

To repeat, Unity just plain forgot to "build-in" a preload scene. So, you simply click to add your preload scene. Don't forget to add the DDOL.

So, during development:

Always start your game from Preload scene.

It's that simple.

Important: Your app will certainly have "early" scenes. Examples:

  • "splash screen"
  • "menu"

Note. Tou CAN NOT use splash or menu as the preload scene. You have to literally have a separate preload scene.

The preload scene will then load your splash or menu or other early scene.


The central issue: "finding" those from other scripts:

So you have a preload scene.

All of your "general behaviors" are simply on the preload scene.

You next have the problem of, quite simply, finding say "SoundEffects".

You have to be able to find them easily, from, any script, on any game object, in any of your scenes.

Fortunately it is dead easy, it is one line of code.

Sound sound = Object.FindObjectOfType<Sound>();
Game game = Object.FindObjectOfType<Game>();

Do that in Awake, for any script that needs it.

It's honestly that simple. That's all there is to it.

Sound sound = Object.FindObjectOfType<Sound>();

Tremendous confusion arises because of the 100s of absolutely wrong code examples seen online.

It really is that easy - honest!

It's bizarre that Unity forgot to add a built-in "preload scene" - somewhere to attach your systems like SoundEffects, GameManager, etc. It's just one of those weird thing about Unity. So, the first thing you do in any Unity project is just click once to make a preload scene.

That's it!


A Detail...

Note that, if you really want to type even less (!) lines of code, it's remarkably easy - you can just use a global for each of these things!

This is explained in detail here , many folks now use something like this, a Grid.cs script ...

 using Assets.scripts.network;
 using UnityEngine;
 
 static class Grid
 {
     public static Comms comms;
     public static State state;
     public static Launch launch;
     public static INetworkCommunicator iNetworkCommunicator;
     public static Sfx sfx;
 
     static Grid()
     {
         GameObject g = GameObject.Find("_app");
 
         comms = g.GetComponent<Comms>();
         state = g.GetComponent<State>();
         launch = g.GetComponent<Launch>();
         iNetworkCommunicator = g.GetComponent<INetworkCommunicator>();
         sfx = g.GetComponent<Sfx>();
     }
 }

Then, anywhere in the project you can say

Grid.sfx.Explosions();

It's just that easy, that's the whole thing.

Don't forget that each of those "general systems" is on, and can only be on, the DDOL game object in the preload scene.


DylanB asks: "During development it's quite annoying that you have to click to the preload scene every time before you click "Play". Can this be automated?"

Sure, every team has a different way to do this. Here's a trivial example:

// this should run absolutely first; use script-execution-order to do so.
// (of course, normally never use the script-execution-order feature,
// this is an unusual case, just for development.)
...
public class DevPreload:MonoBehaviour
 {
 void Awake()
  {
  GameObject check = GameObject.Find("__app");
  if (check==null)
   { UnityEngine.SceneManagement.SceneManager.LoadScene("_preload"); }
  }
 }

But don't forget: what else can you do? Games have to start from a preload scene. What else can you do, other than click to go to the preload scene, to start the game? One may as well ask "it's annoying launching Unity to run Unity - how to avoid launching Unity?!" Games simply, of course, absolutely have to start from a preload scene - how else could it be? So sure, you have to "click to the preload scene before you click Play" when working in Unity - how else could it be?

46
  • 8
    So in terms of workflow, how does this work? If you want to test the scene you're currently editing, do you then have to go load the preload scene in the editor and hit Play on that, since the preload scene is what has the initial objects that propagate to the subsequent scenes? If so, that seems incredibly obtuse as a workflow. I feel like I'm missing something here. Jul 15, 2016 at 20:41
  • 3
    It's a GREAT QUESTION. Yes, it is ABSOLUTELY NORMAL in Unity that you have to annoyingly click back to the preload scene, to test the game. You are missing nothing. You've completely hit the nail on the head.
    – Fattie
    Jul 15, 2016 at 20:43
  • 20
    "every Unity project you must have A PRELOAD SCENE" Why? Please explain why. Lazily initialising objects and accessing them from a static class/singleton seems to be easier - especially when it comes to testing scenes, as you can easily start each scene individually. Nov 5, 2016 at 15:26
  • 19
    @Fattie I will have to disagree with you on this one. True, a preload scene is sometimes (very rarely) necessary, but for the most part everything you describe is just a lazy way of doing it. The first downside you already mentioned... you always have to start from scene 0. I've made the mistake of developing a game this way, and have lost hours upon hours because of it. ALWAYS develop your game so it can be started from any scene. The benefit of this should be obvious.
    – Squeazer
    Jul 6, 2017 at 19:11
  • 10
    @Fattie Second, you suggest using FindObjectOfType to get your instances. Yeah, it's dead simple, but yet again, a lazy way of doing it. The call is super slow and even Unity suggests not to use it: docs.unity3d.com/ScriptReference/Object.FindObjectOfType.html When developing with Unity, you're offered way too much freedom, so you have to be really careful how you design your architecture. If you're not, you'll run into endless problems later on... trust me, I've been there. There are a lot of good practices for Unity out there, this, however, is not one of them.
    – Squeazer
    Jul 6, 2017 at 19:12
31
+100

@Fattie: Thanks for elaborating all this, it's great! There is a point though that people are trying to get through to you, and I'll just give it a go as well:

We do not want every instantiation of everything in our mobile games to do a "FindObjectOfType" for each and every every "global class"!

Instead you can just have it use an Instantiation of a static / a Singleton right away, without looking for it!

And it's as simple as this: Write this in what class you want to access from anywhere, where XXXXX is the name of the class, for example "Sound"

public static XXXXX Instance { get; private set; }
void Awake()
{
if (Instance == null) { Instance = this; } else { Debug.Log("Warning: multiple " + this + " in scene!"); }
}

Now instead of your example

Sound sound = Object.FindObjectOfType<Sound>();

Just simply use it, without looking, and no extra variables, simply like this, right off from anywhere:

Sound.Instance.someWickedFunction();

Alternately (technically identical), just use one global class, usually called Grid, to "hold" each of those. Howto. So,

Grid.sound.someWickedFunction();
Grid.networking.blah();
Grid.ai.blah();
3
  • 3
    Hey Frits! You are 1000% correct :) I guess I invented the Grid.cs idea, which you can see here answers.unity.com/answers/663681/view.html It is widespread now. It is precisely and exactly what you describe. It used to be that I would describe to new Unity users the "grid" system... exactly what you describe. But I realized over time it is confusing - it's better to simply explain the fact that you have a preload scene. That is the "core" of the issue. Sure, you can add some syntactic sugar later once you're familiar (and if you want to). Your code is 10000% lovely.
    – Fattie
    Dec 29, 2017 at 2:42
  • 1
    Since this QA is so popular, I sent a 100 point bounty here since it is an excellent piece of information, building on this really important QA. Do not spend it all in one place, Frits! :)
    – Fattie
    Jan 4, 2018 at 16:41
  • Say @fritslyneborg, if you're still reading ..... funnily enough, I do almost exactly the same thing in iOS! Here ... stackoverflow.com/q/32660471/294884
    – Fattie
    Jan 31, 2018 at 14:32
10

Here is how you can start whatever scene you like and be sure to reintegrate your _preload scene every time you hit play button in unity editor. There is new attribute available since Unity 2017 RuntimeInitializeOnLoadMethod, more about it here.

Basically you have a simple plane c# class and a static method with RuntimeInitializeOnLoadMethod on it. Now every time you start the game, this method will load the preload scene for you.

using UnityEngine;
using UnityEngine.SceneManagement;

public class LoadingSceneIntegration {

#if UNITY_EDITOR 
    public static int otherScene = -2;

    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
    static void InitLoadingScene()
    {
        Debug.Log("InitLoadingScene()");
        int sceneIndex = SceneManager.GetActiveScene().buildIndex;
        if (sceneIndex == 0) return;

        Debug.Log("Loading _preload scene");
        otherScene = sceneIndex;
        //make sure your _preload scene is the first in scene build list
        SceneManager.LoadScene(0); 
    }
#endif
}

Then in your _preload scene you have another script who will load back desired scene (from where you have started):

...
#if UNITY_EDITOR 
    private void Awake()
    {

        if (LoadingSceneIntegration.otherScene > 0)
        {
            Debug.Log("Returning again to the scene: " + LoadingSceneIntegration.otherScene);
            SceneManager.LoadScene(LoadingSceneIntegration.otherScene);
        }
    }
#endif
...
4

An alternate solution from May 2019 without _preload:

https://low-scope.com/unity-tips-1-dont-use-your-first-scene-for-global-script-initialization/

I've paraphrased from the above blog to a how-to for it below:

Loading a Static Resource Prefab for all Scenes

In Project > Assets create a folder called Resources.

Create a Main Prefab from an empty GameObject and place in the Resources folder.

Create a Main.cs C# script in your Assets > Scripts or wherever.

using UnityEngine;

public class Main : MonoBehaviour
{
    // Runs before a scene gets loaded
    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
    public static void LoadMain()
    {
        GameObject main = GameObject.Instantiate(Resources.Load("Main")) as GameObject;
        GameObject.DontDestroyOnLoad(main);
    }
    // You can choose to add any "Service" component to the Main prefab.
    // Examples are: Input, Saving, Sound, Config, Asset Bundles, Advertisements
}

Add Main.cs to the Main Prefab in your Resources folder.

Note how it uses RuntimeInitializeOnLoadMethod along with Resources.Load("Main") and DontDestroyOnLoad.

Attach any other scripts that need to be global across scenes to this prefab.

Note that if you link to other scene game objects to those scripts you probably want to use something like this in the Start function for those scripts:

if(score == null)
    score = FindObjectOfType<Score>();
if(playerDamage == null)
    playerDamage = GameObject.Find("Player").GetComponent<HitDamage>();

Or better yet, use an Asset management system like Addressable Assets or the Asset Bundles.

0
1

actually as a programmer who comes to unity world I see none of these approaches standard

the most simplest and standard way: create a prefab, according to unity docs:

Unity’s Prefab system allows you to create, configure, and store a GameObject complete with all its components, property values, and child GameObjects as a reusable Asset. The Prefab Asset acts as a template from which you can create new Prefab instances in the Scene.

Details:

  1. Create a prefab within your Resources folder:

  2. create a script with contents like below:

using UnityEngine;

public class Globals : MonoBehaviour // change Globals (it should be the same name of your script)
{
    // loads before any other scene:
    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
    public static void LoadMain()
    {
        Debug.Log("i am before everything else");
    }
}
  1. assign it to your prefab

and you can make it even better:

use prefab and namespaces together:

in your prefab script:

using UnityEngine;

namespace Globals {
    public class UserSettings
    {
        static string language = "per";
        public static string GetLanguage()
        {
            return language;
        }
        public static void SetLanguage (string inputLang)
        {
            language = inputLang;
        }
    }
}

in your other scripts:

using Globals;

public class ManageInGameScene : MonoBehaviour
{
    void Start()
    {
        string language = UserSettings.GetLanguage();
    }

    void Update()
    {
        
    }
}

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.