Something like Awake that runs even if disabled?

The documentation for Start says:

Like the Awake function, Start is
called exactly once in the lifetime of
the script. However, Awake is called
when the script object is initialised,
regardless of whether or not the
script is enabled.
Start may not be
called on the same frame as Awake if
the script is not enabled at
initialisation time.
Unity - Scripting API: MonoBehaviour.Start()

It always seemed to me like Awake would be called regardless of whether an object were enabled or not, whereas Start would be called only when the object became active. And that this would apply to app startup. But that does not appear to be the case.

http://forum.unity3d.com/threads/unityevent-disabled-objects-awake-failure.267241/

My question is whether there is anything like Awake that is called even on disabled objects when the app is started. Because that would be terribly handy. (Or am I misunderstanding Awake, maybe it does somehow run on disabled objects at startup?)

So I think I do understand the difference between Awake and Start. If you Instantiate an object then disable it, Awake will run but Start will not. So you should initialize the object itself in Awake and its connections in Start.

What I’m asking is why can’t there be another callback, like Init, which runs only at app startup, for all disabled objects in the scene, if it exists. Unity could make a list of which ones to call when it builds the scene, so it doesn’t have to traverse the scene hierarchy at startup. (This is basically how static initialization functions are compiled in some languages.)

At the very least I think the docs should be clearer that Awake is not called at startup if the object is not enabled, because it really seems like it would be, as the docs currently describe.

1 Like

It might help people to try of think of ways to achieve what you're after, if you explain what use you want to put it to.

Seconded for the question. This is very important if you want for example to check if the mute button should be on or off, and the mute button is part of an options menu. Now the problem is that you don't want that UI menu to clutter the whole game area while editing, but you can't disable it because it won't make that check on scene startup.

Objects are active or inactive. The components like a script, are enabled or disabled

13 Answers

13

The constructors of scripts will run on disabled GameObjects.

For example, the following script will output “Constructor” into the log if it is on a disabled GameObject:

public class TestDisabled {

public TestDisabled() {
     Debug.Log("Constructor");
}

void Awake()
{
    Debug.Log("Awake");
}

void Start()
{
    Debug.Log("Start");
}
 
void OnLevelWasLoaded(int level)
{
    Debug.Log("OnLevelWasLoaded");
}

}

Ah, that could be handy, thanks!

The problem with this is that it will run the content of the function erratically during Editor time too.

@puppeteer Yes, that's true! I tried this, but I was quickly able to solve that problem by giving my constructor function a boolean parameter, and wrapping all functions within it in an if statement. So it will only run in the editor (in full) if that boolean is set to true externally. Voila, no more annoying console errors!

You should NEVER use the constructor of a MonoBehaviour. Unity [might call the constructor multiple times][1]. It's also not executed on the main thread. [1]: http://ilkinulas.github.io/development/unity/2016/05/30/monobehaviour-constructor.html

That is pretty lame documentation. On the Monobehavior description they say this:

Note: The checkbox for disabling a MonoBehavior (on the editor) will only prevent Start(), Awake(), Update(), FixedUpdate(), and OnGUI() from executing. If none of these functions are present, the checkbox is not displayed.
Unity - Scripting API: MonoBehaviour

I just tried it out with Awake, Start, and OnLevelWasLoaded

    protected void Awake()
    {
        Debug.Log("Awake");
    }

    protected void Start()
    {
        Debug.Log("Start");
    }

    protected void OnLevelWasLoaded(int level)
    {
        Debug.Log("OnLevelWasLoaded");
    }

With an enabled object I got both Start and Awake, with a disabled object, neither, and neither called OnLevelWasLoaded.

In answer to the Init question: if you really wanted to, I’m sure you could call a custom Init function using OnLevelWasLoaded and an object with the DontDestroyOnLoad attribute. If you’re more specifically interested in an OnApplicationStart event you could have a scene specifically for initialization scripts.

I believe OnLevelWasLoaded is called in any object that is not created in the scene but was not destroyed in the previous scene. Official docs: http://docs.unity3d.com/ScriptReference/MonoBehaviour.OnLevelWasLoaded.html

However, it’s probably easier to just disable the object in the Awake event.

sorry for the double post - I'm not sure why mods have to approve answers on this site.

Another workaround:

Since the constructor solution kept throwing Errors i found another way to do it (in my case it worked):

I encapsulated my GameObject with another empty GameObject which has a script attached to it doing the initialization stuff. The top GameObject is enabled (and therefore executes Start() and Awake()) while the child is disabled and gets enabled when needed.

Having a Create() method that gets called even if the object is inactive would be awesome, but alas, not (yet) supported, so perhaps the next best thing is to make your own… :wink:

The approach I use uses an interface IInitable that defines a single Init() method:

public interface IInitable {
   void Init();
}

On the scene manager (or any other script for that matter), I gather all the instances that implement IInitable, and call Init() on them:

var scripts = gameObject.GetComponentsInChildren<IInitable>(true);

foreach (var script in scripts)
    script.Init();

Yes this is slow (you should use this only when setting up the scene), and yes it’ll not work when instantiating new objects (but then you can call Init() manually), but it gets the job done.

Final tip, if you don’t have all your game objects under a single root (what I call the scene manager), you’ll have to iterate each root object of each scene when looking for IInitable instances:

var scripts = new List<IInitable>();
var scene = SceneManager.GetActiveScene();

var rootObjects = scene.GetRootGameObjects();

foreach (var go in rootObjects)
	scripts.AddRange(go.GetComponentsInChildren<IInitable>(true));

foreach (var script in scripts)
    script.Init();

Another option, is to give the scenemanager the responsibility of deciding which objects can have this done. You could for example give it a public array of gameobjects which you populate with those objects. This would speed things up (no need to find them) and also give you a bit more flexibility (you'd still have the option of properly turning the objects on and off, by removing/adding them to the array)

How would I go about creating a priority system for this? Before I relied on the script execution order to ensure my scripts initialized in a specific order but calling them manually from one script breaks that. I can't put a field into the IInitiable interface for priority and I don't want to make an abstract class because I'm already inheriting from other abstract class and it would just become a mess.

I might be mistaken, but calling GetComponentsInChildren does not work on inactive gameobjects. So we run back into the same problem.

For this problem, I

  1. created an interface that is implemented by classes that might be disabled when Awake runs, but still need an initialization:
public interface IDisabledOnAwake
{
    public void Initialize();
}
  1. Created a caller script that finds all the GameObjects with scripts that implement this interface, and calls the Initialize function:
public class DisabledScriptInitializator : MonoBehaviour
{
    private void Awake()
    {
        FindObjectsByType<MonoBehaviour>(FindObjectsInactive.Include, FindObjectsSortMode.None).OfType<IDisabledOnAwake>().ToList().ForEach(x => x.Initialize());
    }
}

Hope this helps!

I released this asset for free quite some time before you asked this question.

“We Need Created Message”

Another option, which I use is the following:

  1. Have the gameobject active in the hierarchy
  2. Do all initializations in Awake()
  3. Then deactivate the gameobject in Awake() after the initializations

This might not help your scenario, but I was running into similar problems with wanting Awake to be called on deactivated GameObjects and children.

My situation was: I needed to deactivate my HUD GameObject at the very beginning of the game (because my game starts at the title screen, where the HUD should not be shown). But this caused Awake() to not be called on all the children of the HUD GameObject!

In Awake() in my HUD script (attached to the root GameObject of the HUD), I was using:
gameObject.SetActive(false);

And consequently, all of the children scripts were not having their Awake()s being called at all.
However, I found out that one possible way to help this is to deactivate the GameObject on Start() instead. Start() is still called early enough that, if I deactivate the object then, we won’t see the HUD flash on the screen at all for a frame – but it happens after all the Awake()s are called!

using UnityEngine;

public class HUD : MonoBehaviour {
	public void Start() {
		gameObject.SetActive(false);
	}
}

I hope this helps! :slight_smile:

Oh yeah
Earth to unity :
Anyone there who would think of mentioning, that a disabled scripts Awake function is executed in the actual documentation of Awake ?

Oh no. It makes Soooo much mooore seeense to mention it in the documentation for ‘Start’

Just in case someone bumps into this problem

  1. I made a gameobject called Initializer with this simple script attached,
using UnityEngine;

public class Initializer : MonoBehaviour
{
    public delegate void Initialize();
    public static Initialize initialize;
    void Awake()
    {
        initialize();
    }
}
  1. In your script that you want to initialize, add a Constructor and Initialize Method like this,
using UnityEngine;

public class MyScript: MonoBehaviour
{
    bool isInitialized;
    MyScript()
    {
        Initializer.initialize += Initialize;
    }
    void Initialize()
    {
        if(this == null)
        {
            Initializer.initialize -= Initialize;
            return; 
        }
        if (isInitialized)
        {
            Initializer.initialize -= Initialize;
            return;
        }
        isInitialized = true;
        Debug.Log("I WAS INITILIZE");
    }
}

Doesn't work when the gameobject is disabled.

As an alternative, you can start your object as enabled and write a quick script that automatically disables your component as soon as it starts. That way you can have the code in your Awake function as you expect, and the object will immediately return to its disabled state. Obviously this might not be helpful in every scenario but it’s another potential option.

I join the others saying that Awake (as OnDestroy) not called if object was inactive all the time.

I made a workaround in my very specific case, which I want to share. My case is the need to check some logic on start scene - it’s only for developers, so checking in editor only is enough. This is how I resolved that:

#if UNITY_EDITOR
void OnValidate()
{
    if (!UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode)
        return;
    Debug.Assert(check, $"Validation fail (obj={name} root={root?.name})");
}
#endif

Your profile makes me wanna play sonic adventure 2 again...

https://unity3d.com/learn/tutorials/modules/beginner/scripting/awake-and-start

I think this describes it very well :slight_smile:

Yes we've all seen that video but it does not address my question. I asked if there is something like Awake which will be called at startup even for an object which is not enabled. If you watch that video you'll see it doesn't answer my question.

"Awake called first, even if the script component is not enabled." So if you have your GameObject in the scene with you own script component which is disabled, it's Awake method will be still executed. So as you asked, Awake will be executed on disabled objects at startup.

Not quite. Awake will be called for disabled components on enabled objects; it will not be called for disabled objects. It's important to consider this distinction. I am asking specifically in my question if there is anything like Awake that will be called for disabled objects.

Oh sorry. then I misunderstood your question. my mistake. I found you this. so Awake is really not good for you. http://answers.unity3d.com/questions/372752/does-finction-start-or-awake-run-when-the-object-o.html