Search Unity

OnEnable() is called AFTER object was rendered

Discussion in 'Scripting' started by cdr9042, Jul 3, 2020.

  1. cdr9042

    cdr9042

    Joined:
    Apr 22, 2018
    Posts:
    173
    I'm trying to write a script that copy another SpriteRenderer's color so their alpha will sync without me having to manually setting every single object's alpha through Animation.
    Code (CSharp):
    1. private void OnEnable()
    2.     {
    3.         UpdateColor();
    4.     }
    5.  
    6.     private void Update()
    7.     {
    8.         UpdateColor();
    9.     }
    10.  
    11.     void UpdateColor()
    12.     {
    13.         switch (type)
    14.         {
    15.             case Type.Text:
    16.                 text.color = textSource.color;
    17.                 break;
    18.             case Type.SpriteRender:
    19.                 spriteRender.color = spriteRenderSource.color;
    20.                 break;
    21.         }
    22.     }
    According to this https://docs.unity3d.com/Manual/ExecutionOrder.html#Rendering OnEnable should be called before rendering. However, this wasn't the case with my script. Sometimes it works right, but sometimes it doesn't.
    For example, I want Copier's Sprite to have the same alpha as SourceObj's Sprite.
    On enabling, SourceObj's Sprite's alpha was set to 0, but Copier's Sprite's alpha is still 1. Only after 1 frame, Copier's OnEnable() is called and set it alpha to SourceObj's. This leads to a flicker which I don't want

    EDIT: replicate project https://github.com/nguyentrong101094/TestColorLink
    I tested with Debug.Break() and it proved OnEnable() was called too late. What do I do to call it earlier?
    I'm using object pooling so I have to use OnEnable
     
    Last edited: Jul 6, 2020
  2. Adrian

    Adrian

    Joined:
    Apr 5, 2008
    Posts:
    1,066
    There's nothing that gets called earlier than OnEnable. Awake is called immediately before OnEnable the first time but not earlier in a way that would make a difference.

    This does sound like a bug, an object shouldn't be rendered before its OnEnable is called. I would recommend to make a simple test project to reproduce the issue and then report a bug with Unity.

    As a workaround, you could try to set the sprite to transparent in OnDisable, so that it won't be visible even if it's rendered before the new color can be set.
     
  3. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,745
    Keep in mind Debug.Break() does not stop anything until the very end of frame, so it proves nothing about order.

    If you want to prove order, attaching the debugger and setting breakpoints, or else using Debug.Log() can help you.

    I concur with @Adrian : this is either a bug (unlikely, but certainly possible), in which case please prove it with a small scene, or something else wrong in your initialization logic.

    Here is some timing diagram help:

    https://docs.unity3d.com/Manual/ExecutionOrder.html
     
  4. cdr9042

    cdr9042

    Joined:
    Apr 22, 2018
    Posts:
    173
    I tried Debug.Log(), it says the Origin's color's alpha is 1 at the moment OnEnable() happens. But on the scene, the Origin's color's alpha is 0. It seems Unity's Animator set the Origin's color's alpha to 0 after OnEnable and before rendering

    Here's a replicate project https://github.com/nguyentrong101094/TestColorLink
    Press "s" in SampleScenes to instantiate the object

    Expected result: Compliment (2) & GemText 2's color should always sync with TextMeshPro & Glow's color (alpha = 0)
    Actual result: Compliment (2) & GemText 2's color started with wrong color (alpha = 1), their color only sync with TextMeshPro & Glow's color after one frame
     
  5. geePrompt

    geePrompt

    Joined:
    Nov 6, 2013
    Posts:
    3
    Did you ever find a fix for this? I'm seeing the same thing with object pooling. Reused objects seem to render before their OnEnable is called.
     
  6. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    4,004
    That's completely impossible. OnEnable is actually called from inside SetActive or enabled = true. So code like this:

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



    Code (CSharp):
    1.  
    2. // some other place when the object with the above method is taken out of the pool and activated:
    3. Debug.Log("Before " + obj.gameObject.activeSelf);
    4. obj.gameObject.SetActive(true);
    5. Debug.Log("After " + obj.gameObject.activeSelf);
    6.  
    When you run this code you will see 3 debug logs if the object was deactivated previously

    Code (CSharp):
    1. "Before false"
    2. "OnEnable"
    3. "After true"
    4.  
    If the object "renders" it probably means you never deactivated it in the first place.