Exception When Starting Couroutine in Newly Activated GameObject

I’m working on a UI system that animates an element’s properties using coroutines. Every element that should be animated has a component called AnimatedUIElement which provides the function Enter().

The function Enter() is basically starting coroutines depending on the chosen animation settings, but only after calling Enable(), which sets the gameObject active with gameObject.SetActive(true);, updates its component references and makes the element interactable.

AnimatedUIElement also defines an Exit() function acting similar, but disabling the element after the animation.

    public class AnimatedUIElement : MonoBehaviour {
        private CanvasGroup canvasGroup;
        // ...
     
        public void Enter() {
            // ...
            TriggerEntry();
        }
     
        private void TriggerEntry() {
            PlayChildrenEntry(); // Calls Enter() on all animated Children
            Enable();
            StartCoroutine(SomeAnimation(...));
        }
     
        private void Enable() {
            gameObject.SetActive(true);
            Init();
            canvasGroup.interactable = true;
            canvasGroup.blocksRaycasts = true;
        }
     
        private void Init() {
            canvasGroup = GetComponent();
            // ...
        }
     
        private IEnumerator SomeAnimation(...) { ... }
     
        public void Exit() {
            // Similar to Enter, but disabling the element
        }
    }

Now I want to open the options panel by clicking a button in the game’s UI. The panel is inactive (unticked) in the beginning, while the AnimatedUIElement component is enabled.

133720-ui-hierarchy.png
UI Hierarchy: Options Button calls Enter() on Options Panel.

133721-button-setup.png
The desired setup in the Button Component calling OptionsPanel.Enter() (and MainPanel.Exit()).


But clicking the button throws the following exception:

Coroutine couldn't be started because the the game object 'Main Menu Button' is inactive!
UnityEngine.MonoBehaviour:StartCoroutine(IEnumerator)
AnimatedUIElement:AnimatePosition(Vector2, Vector2, Single, AnimationCurve) (at Assets/Animated UI Elements/AnimatedUIElement.cs:265)
AnimatedUIElement:TriggerEntry() (at Assets/Animated UI Elements/AnimatedUIElement.cs:93)
AnimatedUIElement:Enter() (at Assets/Animated UI Elements/AnimatedUIElement.cs:73)
AnimatedUIElement:PlayChildrenEntry(Transform) (at Assets/Animated UI Elements/AnimatedUIElement.cs:219)
AnimatedUIElement:TriggerEntry() (at Assets/Animated UI Elements/AnimatedUIElement.cs:79)
AnimatedUIElement:Enter() (at Assets/Animated UI Elements/AnimatedUIElement.cs:73)
UnityEngine.EventSystems.EventSystem:Update()

In this stack trace you can see that the Options Panel did not start coroutines as no animation was defined in its component, but called Enter() on its children, which are animated.

Using debug logs I found out that, while Init() is called immediately after Enter(), Awake() is called after the coroutines (which happen later in the program flow) throw errors. This makes it seem like the object is not available instantly after calling setActive(true).

Is there an explanation and possibly a fix for this behavior?

Curious…why are you not using the OnEnable method to start the coroutine?

Using it would ensure that the Monobehaviour is already enabled.

Your issue is most likely this event flow:

PlayChildrenEntry(); // <-- this causes the same code to execute on children
Enable(); // <--- this starts the coroutine

You most likely get the error on one of the children. Since the parent is not active yet (you do that after handling the children) all the children are inactive as well. SetActive only marks the object itself as active. However if the parent is inactive, all children will remain inactive. You probably want to switch those two method calls

private void TriggerEntry() {
    Enable(); // ensures this object is enabled
    PlayChildrenEntry();  // after that go through the child objects
    StartCoroutine(SomeAnimation(...));
}