Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
  • Help Room /
avatar image
2
Question by 1337GameDev · Oct 05, 2017 at 04:18 AM · c#startsingletonawakeonenable

Access singleton in OnEnable when it's initialized in Awake

I have a singleton class (let's call it A) that has some initialization code in Awake().

I have a separate class (We'll call it B), that wants to use the singleton A in OnEnable, so every time the B is enabled (B could be toggled throughout the game) I want it to use this Singleton.

The problem is that OnEnable is called alongside Awake for every object, and having code in OnEnable does not guarantee that ALL other objects' Awake methods have been called.

From my understanding, unity only guarantees that (for a particular Monobehaviour) it calls functions in the order of Awake, OnEnable, Start.

If I have Object1, and Object2 with their own functions, they'll be called like the following:

  • Object1.Awake()

  • Object1.OnEnable()

  • Object2.Awake()

  • Object2.OnEnable

  • THEN -- (in any order)

    • Object1.Start()

    • Object2.Start()

Code in OnEnable cannot assume ALL other Awake calls have been made, only that THIS object's Awake call has.

How do I get around this?

I've tried the following:

 public class Object1 {
     private bool HasInitialized = false;
     public void OnEnable() {
         if(HasInitialized) {
             Singleton.Method();
         }
     }
 
     public void Start() {
         if(!HasInitialized) {
             Singleton.Method();
         }
 
         HasInitialized = true;
     }
 
 }

But it seems very clunky and cumbersome to write on EVERY class that want's to use a singleton. Is there a better way to go about this?

Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

3 Replies

· Add your reply
  • Sort: 
avatar image
6
Best Answer

Answer by Bilelmnasser · Oct 05, 2017 at 09:12 AM

hi, the Awake, OnEnable and Update functions of different scripts are called in the order the scripts are loaded (which is arbitrary). However, it is possible to modify this order using the Script Execution Order settings (menu: Edit > Project Settings > Script Execution Order).

otherwise if the execution time is long and you need to wait to all tasks are finished . (sometime depends on other factors (exemple network or any other job)), i can use Couroutine on Start function to wait until my other object was successfully initialized or refreshed (example retrieved database data )

SOLUTION 1 :

 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 
 public class A : MonoBehaviour {
 
     public bool _awakened = false;
     public bool _enabled=false;
     public bool _Started= false;
 
     private void Awake()
     {
         //do jobs
         Debug.Log( "object A is Awakened" );
         _awakened = true;
     }
 
     private void OnEnable()
     {
         //do jobs
         Debug.Log( "object A is Enabled" );
         _enabled = true;
     }
 
     private IEnumerator Start()
     {
         // wait some time for simulated job
         yield return new WaitForSeconds( 10 );
         //do jobs
         _Started = true;
     }
 
    
 }

 




and i have a class B that use class A but need A to be ready before using it :

 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 
 public class B : MonoBehaviour {
 
     public A ReferenceObjectA;
 
     // Use this for initialization
     IEnumerator Start () {
         yield return new WaitUntil( () => ReferenceObjectA._enabled && ReferenceObjectA._awakened && ReferenceObjectA._Started );
         Debug.Log( "ReferenceObjectA is awakened and enabled and started !!" );
         // do my logic game  
     }
 }

Edit (SOLUTION 2): another idea come across my mind, is to use Delegates , or UnityEvent , you can implement the next same logic with both of them [the object him self will call the other object(s) that he is ready ] (OnEnabled can be static member depends on your need )

 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 using UnityEngine.Events;
 
 public class A : MonoBehaviour {
 //   UnityEvent   that  well   invoke   when   we   are   ready,   it   may   be   a   static  if   you   want
     public UnityEvent OnEnabled;
 
 
     private void OnEnable()
     {
         //invoke  all  UnityAction   attached   to  this  UnityEvent 
         OnEnabled.Invoke( );
 
        
     }
 }

and then you have a class B that you want her to execute some code when an object A said he is ready.

 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 
 public class B : MonoBehaviour {
     public A ReferencedAObject;
     // Use this for initialization
     void Awake () {
         Debug.Log( "B Awake  ");
        
         ReferencedAObject.OnEnabled.AddListener( When_A_is_Ready );
     }
 
     //this   will  be   called   by  object   A  him  self
     public void   When_A_is_Ready()
     {
 
         //   job   that   will   be   excuted  when   A   call  OnEnabled
         Debug.Log( "A  is  now   ready "  );
     }
 
 }


Comment
Add comment · Show 9 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image 1337GameDev · Oct 05, 2017 at 01:22 PM 0
Share

Hmm. This seems a bit complicated. I envisioned a simpler answer to allow others to use my singletons easily, but if none exists I might have to do something similar to this.

I wanted to avoid CoRoutines if possible (because they are a bit clunky to set up, and make use of my singleton not very friendly to use for other developers.)

I also wanted to avoid execution order, because that's easily forgotten and can become a complicated mess to deter$$anonymous$$e the correct ordering of scripts if there's a bug. I'd rather not rely on a scene setting to solve this.

Your suggested paradigm would work, and seems correct) for Async tasks like network / db calls, but for singleton use it feels very clunky. Would this idea also work for OnEnable usage? (because it currently demonstrates Start, but start is ALWAYS after Awake/OnEnable of all scripts, so singleton use would be perfectly fine).

avatar image Bilelmnasser 1337GameDev · Oct 05, 2017 at 01:56 PM 1
Share

hi @1337GameDev , i edited the answer, maybe the second one is more easy , it's all depends on your design for the problem you face.

avatar image 1337GameDev Bilelmnasser · Oct 05, 2017 at 03:12 PM 1
Share

I'll test this structure when I get home tonight. It could work to have callbacks, and could be relatively clean code for other developers to understand and use.

I really wish "OnEnable" was always called after start for every gameobject :( I don't know why unity put it before... (in other methods, you can simply check if the current gameobject is enabled before invoking your code).

avatar image Bilelmnasser · Oct 05, 2017 at 07:46 PM 1
Share

Or maybe a static object in your conpoment class can do the trick , look at my answer here: static object to hold point of access to all your methods

avatar image 1337GameDev Bilelmnasser · Oct 06, 2017 at 02:49 AM 1
Share

I'm not a fan of statics in component classes unless it's something explicitly needed for the state of the component (and not just an object reference).

avatar image Bilelmnasser 1337GameDev · Oct 06, 2017 at 08:17 AM 1
Share

here you have a good example to singleton implementation in unity :

Singleton Implementation for Unity $$anonymous$$onobehavior On the UnityWiki Page

Show more comments
avatar image
0

Answer by SirCrazyNugget · Oct 05, 2017 at 05:47 PM

For the singleton pattern you shouldn't be using any of Unity's built-in functions as an initialiser. The getter of the instance should always ensure there is one and only one ever this one returned.

MSDN Singleton in C#

Comment
Add comment · Show 6 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image 1337GameDev · Oct 06, 2017 at 02:46 AM 0
Share

Why shouldn't I use the unity method? It interacts with unity, and can be configured in the inspector like other $$anonymous$$onoBehaviors.

avatar image SirCrazyNugget 1337GameDev · Oct 06, 2017 at 04:53 PM 0
Share

You can use any Unity method though it wouldn't be a singleton.

Object B wants to access Object A during OnEnable is already more information to show it's designed incorrectly. Object A, if truly a singleton, doesn't need to know when it's accessed, only that it can be at any point. Trying to deter$$anonymous$$e the ti$$anonymous$$g of its availability is a huge flaw.

avatar image 1337GameDev SirCrazyNugget · Oct 06, 2017 at 08:13 PM 0
Share

What do you mean "won't be a singleton"

It's simply an object, constrained by constructor / access to only have 1 instance of itself at all times.

And true, AFTER initialization of the singleton, my singleton doesn't care when it's accessed. It's only the first iteration of Awake->OnEnable that my Singleton cares about, because it has it's own initializations to do.

I can't just simply make it not a monobehavior, because I want it able to be modified by the inspector in the scene before game start as well as able to be used by core unity functions (non monobehavior classes can't be).

Show more comments
avatar image
0

Answer by Pepeco159 · Jul 28, 2020 at 12:57 PM

I had a similar issue with the generic singleton class I am using, so I decided to add some more info for those who want to know why this implementation is breaking the codes and how to prevent those problems.

First, take a look at the code below(the one causing problems). Notice that we use unity's Awake call to ensure that only one instance of the class is created.

 using UnityEngine;
 
 public class Singleton<T> : MonoBehaviour where T : Singleton<T>
 {
     private static T _instance;
     public static T Instance
     {
         get{return instance;}
     }
 
     public static bool isInicialized
     {
         get{return _instance != null;}
     }
 
     // Start is called before the first frame update
     protected virtual void Awake()
     {
         if(_instance != null)
         {
             Debug.LogError("[Singleton] Trying to create a second instance of singleton ", this);
         }
         else
         {
             _instance = (T) this;
         }
     }
 
     protected virtual void OnDestroy()
     {
         if(_instance == this)
         {
             _instance = null;
         }
     }
 }

In this implementation, you'll need to attach the script to a gameObject on the editor by yourself in order to use it.

We can see that it becomes a big problem since we cannot control the order that the scripts Awake's will be called, and quite often you'll see that scripts that have onAwake or onEnable references to the singleton will break.

Now let's see a implementation that doesn't use Awake.

 public class Singleton<T> : MonoBehaviour where T : Singleton<T>
 {
     private static T _instance;
     public static T Instance
     {
         get
         {
             if(_instance == null)
             {
                 var objs = FindObjectsOfType(typeof(T)) as T[];
 
                 if (objs.Length > 0)
                     _instance = objs[0];
 
                 if (objs.Length > 1)
                 {
                     Debug.LogError("[Singleton] There is more than one instance of " + typeof(T).Name + " in the scene.");
                 }
 
                 if (_instance == null)
                 {
                     GameObject obj = new GameObject();
                     obj.hideFlags = HideFlags.DontSave;
                     _instance = obj.AddComponent<T>();
                 }
             }
 
             return _instance;
 
         }
     }
 
     public static bool isInicialized
     {
         get{return _instance != null;}
     }
 
     protected virtual void OnDestroy()
     {
         if(_instance == this)
         {
             _instance = null;
         }
     }
 }

Here we used the getter to ensure that anytime a script tries to access the Instance there will be a reference. We could even destroy the other instances if found more than one, though Logging error seems enough to me.

Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

446 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Start is not called after reloading the level 3 Answers

NullReferenceException: Object reference not set to an instance of an object in Singleton class. 1 Answer

Custom Editor need for Awake function called when game starts 0 Answers

String variable declared in editor goes blank at runtime 0 Answers

Class array initialization on Start() gives -> Object reference not set to an instance of an object 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges