Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question A bit confused on script execution order... OnEnable seem to be called before (or with) Awake

Discussion in 'Scripting' started by danielesuppo, Jan 23, 2023.

  1. danielesuppo

    danielesuppo

    Joined:
    Oct 20, 2015
    Posts:
    331
    Hello! As far as I know the Awake method should be called on every script before OnEnable.
    So, why when I create a Singleton in MyClassA like this
    Code (CSharp):
    1. public class MyClassA : MonoBehaviour
    2. {
    3.     public static MyClassA instance;
    4.  
    5.     void Awake()
    6.     {
    7.         instance = this;
    8.     }
    9. }
    and I next call it in some other MyClassB like this
    Code (CSharp):
    1. public class MyClassB : MonoBehaviour
    2. {
    3.     void OnEnable()
    4.     {
    5.         MyClassB.instance.... /// Do Something with it
    6.     }
    7. }
    I often get a "null reference exception"?
    Should'nt be FIRST created the instance on Awake, and THEN readed OnEnable ?
    I'm a bit confused...
    Many thanks for any help to better understand what I'm missing!
     
  2. halley

    halley

    Joined:
    Aug 26, 2013
    Posts:
    1,869
    Your singleton script is not complete as shown. There is no instance of MyClassA created.

    The public static "instance" should be a property which, when accessed, actually creates an instance of MyClassA. Your Awake() method is only called when there really is an instance.

    Maybe you already have an instance that you created manually in your scene. The only guarantee about Awake coming before OnEnable is within the same game object. All of the instances of components in an object will get their Awake called, then all of the instances of components in the same object will get their OnEnable called. If you're trying to reference this singleton from some other object, they may have been lucky and gotten their Awake/OnEnable before the object with MyClassA got theirs. That ordering is not guaranteed.

    I won't give you a singleton script here, there are a hundred tutorials on it. Most of them work.
     
    Last edited: Jan 23, 2023
  3. mgear

    mgear

    Joined:
    Aug 3, 2010
    Posts:
    8,991
  4. chemicalcrux

    chemicalcrux

    Joined:
    Mar 16, 2017
    Posts:
    717
    I always set up singletons in Awake() and refer to singletons in Start(). That'll deal with your problem.

    Alternatively, it would be very reasonable to something like what halley suggested: make a property that checks if the instance is assigned and, if it isn't, finds an object of the appropriate type in the scene.

    That way, it doesn't matter what order stuff gets set up in: the assignments happen on-demand.
     
    spiney199 likes this.
  5. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    There's a chat about this over here like 2-3 weeks ago where someone created a more detailed event execution order:
    https://forum.unity.com/threads/a-c...ution-order-of-unity-event-functions.1381647/

    And yes... OnEnable is called WITH Awake, but after Awake.

    You'd expect it to be:
    Everything Awake
    Everything OnEnable
    Everything Start

    But really it's:
    Everything Awake/OnEnable (if enabled)
    Everything Start (if enabled)

    I ran into this years and years ago... I just googled my username and StartOrEnable and found a thread from 2014 where a discussion was on it:
    https://forum.unity.com/threads/awake-start-and-onenable-walked-into-a-bar.276712/

    And my solution has and always has been to have a "OnStartOrEnable".

    This can be as simple as:
    Code (csharp):
    1. public class MyScript : MonoBehaviour
    2. {
    3.     void OnEnable()
    4.     {
    5.         if (started) OnStartOrEnable();
    6.     }
    7.     void Start()
    8.     {
    9.         started = true;
    10.         OnStartOrEnable();
    11.     }
    12.     void OnStartOrEnable()
    13.     {
    14.         //do the thing you want to do during enable
    15.     }
    16. }
    This is basically my generalized approach to it because I find myself wanting to do stuff onenable a lot. But I want it to happen after everything has initialized (awake).
     
  6. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,749
    You really really really don't want to do this sort of drop-in-scene singleton if you can get away from it.

    Then your app is sensitive to what scene you load from, etc. Madness.

    Simple Singleton (UnitySingleton):

    Some super-simple Singleton examples to take and modify:

    Simple Unity3D Singleton (no predefined data):

    https://gist.github.com/kurtdekker/775bb97614047072f7004d6fb9ccce30

    Unity3D Singleton with a Prefab (or a ScriptableObject) used for predefined data:

    https://gist.github.com/kurtdekker/2f07be6f6a844cf82110fc42a774a625

    These are pure-code solutions, DO NOT put anything into any scene, just access it via .Instance!

    If it is a GameManager, when the game is over, make a function in that singleton that Destroys itself so the next time you access it you get a fresh one, something like:

    Code (csharp):
    1. public void DestroyThyself()
    2. {
    3.    Destroy(gameObject);
    4.    Instance = null;    // because destroy doesn't happen until end of frame
    5. }
    There are also lots of Youtube tutorials on the concepts involved in making a suitable GameManager, which obviously depends a lot on what your game might need.

    OR if you just can't handle NOT dropping stuff into a scene (some people are like this!), then just make a single instance of a custom ScriptableObject that has the shared fields you want for the duration of many scenes, and drag references to that one ScriptableObject instance into everything that needs it. It scales up to a certain point.
     
    danielesuppo likes this.
  7. danielesuppo

    danielesuppo

    Joined:
    Oct 20, 2015
    Posts:
    331
    Many thanks to you all for your kind reply! That helped me a lot!!!
    I had wrong informations on how to create a Singleton (I've read a long time ago in some "trusted" places that "instance=this" was enough for the job, in a very simple way...).
    Usually I don't use any Singleton, but I just pass the GameManager (and other Classes) to each game state, a bit tricky but it help me to keep track on what's going on.
    This time I was a bit in a hurry so I've tried to to use my GameManager as a Singleton.
    Yes, indeed, now I've understood!
    Yes, sorry
    Yes, that's exactly what I was doing, but sometimes you need to refer to the Singleton when OnEnable (I use it mostly for new Gameobjects that show-up, and should get variables, actions, etc from the GameManager)
    I really meant to figure out what was going on, so to create my own workflow
    Your solution seem to me a bit too tricky to implement on each class that refer to the singleton

    Sure, mine was just a very simple example, just to show my doubts on code execution.
    I've omitted Properties and DontDestroyOnLoad

    I would have ended up with this code, as a Singleton pattern:
    Code (CSharp):
    1. public abstract class Singleton<T> : MonoBehaviour where T : Singleton<T>
    2.     {
    3.         private static T _instance;
    4.         public static T Instance
    5.         {
    6.             get
    7.             {
    8.                 if (_instance == null)
    9.                 {
    10.                     _instance = FindObjectOfType<T>();
    11.                     DontDestroyOnLoad(_instance.gameObject);
    12.                 }
    13.                 return _instance;
    14.             }
    15.         }
    16.  
    17.         public virtual void Awake()
    18.         {
    19.             if (Instance != this)
    20.             {
    21.                 Destroy(gameObject);
    22.             }
    23.         }
    24.     }
    Next, I would implement it like this:

    Code (CSharp):
    1. public class GameManager : Singleton<GameManager>
    2. {
    3. ....
    4. }
     
    Last edited: Jan 25, 2023
  8. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,749
    It relies on dropping things in the scene so it is ripe for future misuse.

    Example misuse case: you use this Singleton<T> in some future manager. That manager adds something in OnEnable() and now it gets run every time this scene loads, even if it just instantly gets Destroyed because it already exists.

    Welcome to bugs.

    Another thing is that 99% of Unity developers simply DO NOT EXPECT the normally-private Unity MonoBehaviour methods to be overridden / virtual.

    Can they be virtual/override? Yes of course they can!

    Are you setting yourself up for future misuse? Absolutely.

    Someone (maybe even you) will write a Manager without Awake(), then later a year later go back and add an Awake() and forget to call base.Awake()

    Have some more bugs!

    But hey, it's your project. The pattern I listed above works 100% of the time and I should know because essentially 100% of all of my games use that for everything from life-of-the-app to life-of-the-game to life-of-a-scene singletons.
     
  9. danielesuppo

    danielesuppo

    Joined:
    Oct 20, 2015
    Posts:
    331
    First of all, I should pay you for your time... :)
    Well, let's say that in scene there's a GameManger, UiManager, AssetManager, etc...
    I don't like this approach, but let's say that each "manager" is a Singleton.
    I think that there should be just one instance of each "manager" in scene, so why do you think that's a bad idea this approach? maybe I'm missing something (again)?

    You're right. I've overwritten my own virtual methods sometimes...
    Surely this pattern is not the best one!
     
    Last edited: Jan 25, 2023
  10. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    5,881
    As Kurt mentioned, you forget to place one in a scene and everything breaks. Often better to design things to lazily initialise whenever they're needed.

    And in a lot of cases, 'managers', don't need to be a singleton when they're only responsible for a few things within a small scope. They can just be another component with references to other parts of this small picture.

    Most of my 'global' managers are static classes these days, as they often don't need to exist in any scene.
     
  11. danielesuppo

    danielesuppo

    Joined:
    Oct 20, 2015
    Posts:
    331
    Hello all,
    has been a long time from this post, but I'd like to share my solution, that I'm actually using from a long time, that use the suggestions from Kurt-Dekker, so to have a (hopefully safe) singleton with exposed parameters, that can be used without the need to specify the name of the class itself.

    The only sad thing, in my opinion, is that the prefab should have the same name of the script.

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class Singleton<T> : MonoBehaviour where T : Singleton<T>
    4. {
    5.     private static T _Instance;
    6.     public static T Instance
    7.     {
    8.         get
    9.         {
    10.             if (!_Instance)
    11.             {
    12.                 var prefab = Resources.Load<GameObject>(typeof(T).ToString());
    13.                 var inScene = Instantiate<GameObject>(prefab);
    14.                 _Instance = inScene.GetComponentInChildren<T>();
    15.                 if (!_Instance) _Instance = inScene.AddComponent<T>();
    16.                 DontDestroyOnLoad(_Instance.transform.root.gameObject);
    17.             }
    18.             return _Instance;
    19.         }
    20.     }
    21. }
     
    Last edited: Aug 11, 2023
  12. SisusCo

    SisusCo

    Joined:
    Jan 29, 2019
    Posts:
    1,114
    You could create an asset postprocessor to validate that all singleton prefabs follow the expected conventions. Something like this for example:
    Code (CSharp):
    1. internal sealed class ResourcePathMustEqualTypeNameValidator : AssetPostprocessor
    2. {
    3.     private void OnPostprocessPrefab(GameObject prefab)
    4.     {
    5.         if(!prefab.TryGetComponent(out IResourcePathMustEqualTypeName component))
    6.         {
    7.             return;
    8.         }
    9.      
    10.         string directory = Path.GetDirectoryName(assetPath);
    11.         string typeName = component.GetType().Name;
    12.         if(!directory.EndsWith("Resources"))
    13.         {
    14.             Debug.LogWarning($"Prefab '{assetPath}' should be moved inside a Resources folder, because it contains the component '{typeName}' which implements {nameof(IResourcePathMustEqualTypeName)}.", prefab);
    15.         }
    16.  
    17.         if(!string.Equals(prefab.name, typeName))
    18.         {
    19.             Debug.Log($"Prefab '{assetPath}' name must match '{typeName}', because it contains the component '{typeName}' which implements {nameof(IResourcePathMustEqualTypeName)}. Renaming automatically...", prefab);
    20.             AssetDatabase.RenameAsset(assetPath, Path.Combine(directory, typeName + ".prefab"));
    21.         }
    22.     }
    23. }
    24.  
    This wouldn't detect a singleton being placed into a scene though... But you should be able to detect that as well using ObjectFactory.componentWasAdded and PrefabUtility.IsPartOfPrefabAsset + PrefabStageUtility.GetPrefabStage.