0
\$\begingroup\$

I have a world selection scene in which I have GameObjects representing worlds. The GameObjects have default properties when the scene is loaded. For example: the world name text is red and each world has a "locked" status. This initial state can be seen in the following screenshot:

enter image description here

On the Start() method I'm dynamically updating the properties on each GameObject based on values I'm reading from a save file. If a world is unlocked the name text is set to white. If a world is currently selected the name text is set to green. Here's what that looks like at run time:

enter image description here

The problem is that when the scene is loaded from another scene(e.g. by clicking a navigation button) the initial state of the GameObjects can be seen for a second and then they change to the proper values as dictated by the save file. How can I prevent this from happening? I was under the impression that anything that happens in the Start() method takes place before GameObjects are rendered.

Here's some slimmed down code of what my code looks like:

    var worldIcons = FindObjectsOfType<WorldIcon>().OrderBy(x => x.WorldNumber).ToList();
    var saveFile = GameSaveManager.GetSaveFile();

    if (saveFile == null)
    {
        Debug.LogError("Could not find save file while loading worlds");
        return;
    }

    foreach (var worldIcon in worldIcons)
    {
        var savedWorld = saveFile.Worlds.SingleOrDefault(x => x.Number == worldIcon.WorldNumber);

        if (savedWorld == null)
        {
            //add world to save file
            GameSaveManager.CreateWorld(worldIcon.Controller);

            //unlock first world by default
            if (saveFile.Worlds.Count == 0 && worldIcon.WorldNumber == 1)
                worldIcon.Unlocked = LevelMapController.LockedStatus.Unlocked.ToString();
        }
        else
        {
            worldIcon.DisplayName = savedWorld.Name;
            worldIcon.WorldNumber = savedWorld.Number;

            var button = worldIcon.GetComponent<Button>();

            if (savedWorld.Unlocked)
            {
                button.interactable = true;
                button.targetGraphic.color = Color.white;
                worldIcon.Unlocked = LevelMapController.LockedStatus.Unlocked.ToString();
            }
            else
                worldIcon.Unlocked = LevelMapController.LockedStatus.Locked.ToString();
        }
    }
\$\endgroup\$
  • \$\begingroup\$ What you describe as your expected behavior is indeed what should happen. If the script is enabled and the Start method doesn't yield return, then the method will run to completion before the scene is rendered for the frame that the objects have spawned. Can you walk us through the steps to build a Minimal Complete Verifiable Example, so we can reproduce this problem without access to your scene files or the rest of your loading code? \$\endgroup\$ – DMGregory Jan 22 at 11:23
  • \$\begingroup\$ I have seen some instances where the UI needs an extra kick to process layout changes that occurred during the frame, before drawing. So it might be your Start method is getting called when you expect, but for some reason the UI fails to update its cached drawing state before the render happens. It might be worth checking if the UI elements you're using or their containers expose some type of SetDirty/Rebuild method you can poke to remind them to update themselves. \$\endgroup\$ – DMGregory Jan 23 at 13:57
1
\$\begingroup\$

I finally figured out what was going on. It turns out that it only looked like the World GameObjects weren't initialized before the scene was rendered. Each world GameObject has a button whose Target Graphic property is set to a child Text object called DisplayName. This child object displays the world's name(World1, etc.) The button's Fade Duration property is set to 0.1 by default. The problem was fixed by changing this property to 0.

enter image description here

\$\endgroup\$
-1
\$\begingroup\$

I was under the impression that anything that happens in the Start() method takes place before GameObjects are rendered.

No, here is a link that breakdowns the unity events execution order

And also, loading files and loops might take time, a workaround will be to put your buttons under a CanvasGroup component with an alpha of 0 til everything is loaded.

\$\endgroup\$
  • \$\begingroup\$ "No"? The chart you've linked to does indeed show Start is called before the object is rendered for the first time (provided the script is enabled). It will run to completion in a single frame — even if it has to stop and load a file along the way, it will just delay the rendering rather than drawing part-way through — unless the code explicitly makes it a coroutine and has a yield return that lets the current frame finish rendering before the method resumes. \$\endgroup\$ – DMGregory Jan 22 at 11:19
  • \$\begingroup\$ The execution order diagram, as @DMGregory pointed out, is what led me to believe that the properties I change would be applied before the object is rendered for the first time. I don't believe I have any yield return calls anywhere in the initialization code but I will double-check later today. \$\endgroup\$ – Bruno Jan 22 at 14:15
  • \$\begingroup\$ @DMGregory This is true for objects that are instantiated in runtime. but how about those that already exist in the scene in their default setup values/states ? \$\endgroup\$ – Omar Guendeli Jan 23 at 13:23
  • \$\begingroup\$ It is also true for objects in the scene at start. Or at least it’s supposed to be, and is whenever I try. Apparently Bruno is not seeing this behavior. \$\endgroup\$ – Ed Marty Jan 23 at 13:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.