Initializing hidden UI elements at runtime / Controlling UI visibility

Heya!

I’m struggling with my inner conscience with regards to the following issue… I am writing a game demo where one of the key architectural features is to demonstrate the Observable/Observer pattern, dependency injection, yada yada.

Maybe I’m over thinking it, but here is the situation… I have standard UI dialogs in a canvas, and their game objects are set to inactive by default, so you don’t see them all overlapping in the editor.

In their Awake() and OnDestroy() methods, they attach/detach themselves from the observeables ( via delegates )

e.g.

void Awake()
{
    someManager.onUpdateSomething += this.OnUpdate;
}


void OnDestroy()
{
    someManager.onUpdateSomething -= this.OnUpdate;
}

Now, the issue is that when the game objects are disabled, Awake does not get called, so the listeners do not get hooked up.

My current solution is to use Resources.FindObjectsOfTypeAll in a master component which will force them to active. Now as I write it, I realize I can be more general and simply call an Init() method there instead of SetActive(true). But anyhoo, here is that bit…

        private void InitializeAllUIControllers() {
            var uiControllers = Resources.FindObjectsOfTypeAll<BaseUIController>();

            foreach (var uiController in uiControllers) {

                if (!uiController.gameObject.activeSelf) {
                    uiController.gameObject.SetActive(true);
                    // TODO: uiController.Init() would be better here
                }
            }
        }

So, my question is… is there a more graceful way of getting a component to self initialize if the game object is inactive? Alternative suggestions to the above ( other than not using observers )? In a non-Unity environment, I’d simply put them in the constructor, but we all know that’s risky business in Monobehaviours.

What if you had a parent container object which is active always, and contains the subscription attach/detach code, with actual UI elements which are inactive at the start as children of that container object?

Another option: leave the only the CanvasRenderer component disabled at start, instead of the whole GameObject.

The parent approach is one I considered, and will rethink. It just seemed a bit excessive to need a second component to do this, but maybe if all of the logic is in the parent script, and the actual UI gameObject without a custom script, that could work. I’ll mull that one over. It sounds promising.

For the second, I investigated this, but for the life of me, could not figure out how to disable the CanvasRenderer so assumed that you cannot. There is no checkbox in the UI to do this. You wouldn’t happen to know how, would you? Assuming this would also not just be a visual thing, and that mouse clicks, etc. would pass through when it is disabled?

Ah yeah, this is true. The CanvasRenderer is not a Behaviour, so it cannot be enabled/disabled. One possibility would be enabling/disabling the individual UI component, such as Image, Text, Button, etc… But that might be a little harder to do in a generic way.

I would probably go with the first option between those two. And maybe someone else will come along with something even cleaner ¯_(ツ)_/¯

Hey, your first suggestion was the winner in my mind. It’s especially good since I can frame it in terms of MVC ( Model-View-Controller ) where the parent is the controller, leaving the game object representing the panel/dialog as a view. I can keep the view free of any custom scripts. This is definitely the tidy solution I was looking for.

6643294--758446--upload_2020-12-20_14-30-29.png

The only real difference in the script is that instead of referring to gameObject, we refer to mainView. It’s otherwise identical.

I’ve been struggling on how to logically separate game objects in terms of views and controllers in Unity, and this certainly helps.

Thx for you fabulous suggestion :smile:

1 Like