- Home /
Unexpected behavior when Awake() makes a GameObject inactive
This has come out of questions with Active/Inactive objects and the Awake() function. At the end of my research I think I've encountered a bug - and if not, the documentation (and Unity rep's forum explanation) are wrong...
So the basic question of "when is Awake() called?" has been asked and answered many times in many ways. One point of confusion is this sentence from the docs "If a GameObject is inactive during start up Awake is not called until it is made active,or a function in any script attached to it is called."
A few threads I saw pointed back to this explanation by Unity's Tim C:
This is a bit misleading, it should say: "If a GameObject is inactive during start up Awake is not called until it is made active, or an Unity lifetime function in any script attached to it is called. (OnEnable, OnCollisionEnter)"
Now here's the issue... I have two scripts on a gameObject, both with an Awake() function. When my object is activated for the first time, Awake() should be called on both scripts. However, if the early-executing script contains this code, the second script's Awake() does NOT run:
void Awake() {
gameObject.SetActive(false);
}
In every thread I've read, no one has mentioned this as a possibility, and I had always gotten the impression that if one script's Awake() was called, every script on the same object will also have Awake() called.
I feel like it didn't used to work this way, but I don't have proof of that...
Is this a bug? Especially considering how popular doing object initialization in Awake() seems to be.
Answer by HenryStrattonFW · Nov 05, 2015 at 06:26 PM
I don't think its a bug, I think its just an use case that is not covered in the documentation. It makes sense with how unity processes components top to bottom (as shown in the inspector) as far as I'm aware. It makes sense that Unity would check certain criteria (like if the gameObject is active) prior to processing each successive component. Otherwise you could get conflicting actions from different scripts on a single object.
For example if the first script checked some conditions and determined that this game object should be destroyed right away, you don't really want other scripts on that object carrying on as if nothing is happening after the object has been marked for destruction.
"For example if the first script checked some conditions and deter$$anonymous$$ed that this game object should be destroyed right away, you don't really want other scripts on that object carrying on as if nothing is happening after the object has been marked for destruction."
But this isn't how Unity works is it? I thought it was the case that when you call Destroy() the remaining scripts would still run until the end of the current frame, as opposed to DestroyImmediate, which Unity cautions heavily against using.
The docs make it sound clear, and even moreso when reiterated by Tim C, but simply doesn't work how it's being described. Unless Awake() is not a 'Unity lifetime function'?
Another way to think about it - if Awake() is to be used as a sign that an object was at some time activated, then one object could have one script aware of activation and another script thinking the object is still uninitialized.
That seems dicey, and considering how Destroy is patterned, it feels "bug-ish" to me that Awake()s wouldn't all get called.
Awake is called when the script component is created. Awake is called directly from the creation code. Since Unity creates all components one by one it's quite logical that this happens.
At the beginning you have an empty gameobject that is active. Unity then deserializes the components one by one. If the first one that is created deactivates the gameobject, the gameobject will be deactivated when the second component is created. Awake can be seen like it's called from the constructor of the class. So Awake will run completely before the object creation is completed.
The process can be compared with this:
GameObject GO = new GameObject("obj");
GO.AddComponent<Script1>();
GO.AddComponent<Script2>();
When the first AddComponent is called, Awake of Script1 will be called immediately. So when you call the second AddComponent the Awake of the first has already run. If that Awake has deactivated the GameObject, Script2 won't be initialized since the GameObject is not active.
The active state of a GameObject or the enabled state of a Behaviour are immediate states. They can change during the execution of a frame, even several times. Other states (like key / mouse input, Time.time, ...) are constant for the current frame.
Destroy is delayed to the end of the current frame to avoid null reference exceptions since destroyed object references will become fake null. There is no real problem using DestroyImmediate as long as you really don't touch the object afterwards.
If you want to ensure that all scripts are initialized, deactivate your object in Start. Since Start is called before Update is called or anything got rendered, The object won't be visible in the scene.
Your answer
Follow this Question
Related Questions
How to init disabled canvas properly 1 Answer
Function that Runs Even on Disabled GameObjects 1 Answer
Activating GameObjects one by one 2 Answers
How to set gameobject inactive if random.range decides to choose the same gameobject 2 Answers
Strange Unity state machine behaviour: OnEnable - OnDisable before awake??? 1 Answer