Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Game Initialization: Dealing with Timing Issues

Discussion in 'Scripting' started by gdbjohnson, Mar 17, 2015.

  1. gdbjohnson

    gdbjohnson

    Joined:
    Dec 16, 2014
    Posts:
    40
    I have a best practice question regarding initializing game resources. I am getting incrementally frustrated at the lack of control I seem to have with the timing of certain initialization functions that my game requires. Unity is great in how flexible it is, but with great flexibility comes great responsibility.

    When I'm testing various screens, I have manager script that sets my scene up for a proper run. In particular, if something is enabled (visible) when it shouldn't be for the run, then my manager disables it. However, the sneaky Awake() method is still getting called before my Manager can say "hey, you aren't ready to be woken up yet". And, this Constructor is often assuming that certain resources are loaded elsewhere.

    Is the "Awake" method on a gameobject loaded in the scene the only entry point available to me for game init? And if so, is it possible for me to create an Awake() call that I can *guarantee* runs first, before any other code?
     
  2. Strategos

    Strategos

    Joined:
    Aug 24, 2012
    Posts:
    255
  3. Zaladur

    Zaladur

    Joined:
    Oct 20, 2012
    Posts:
    392
    In general, the rule I use is 'initialize self reliant portions in Awake, and portions reliant on other code in Start. If you have code that relies on other objects already being initialized, I generally advise you throw it into a Start method instead.

    This may not always be the right answer, and you can change the Script Execution Order to force certain scripts to execute before others.

    http://docs.unity3d.com/Manual/class-ScriptExecution.html
     
    Kiwasi likes this.
  4. gdbjohnson

    gdbjohnson

    Joined:
    Dec 16, 2014
    Posts:
    40
    @Zaladur Generally I have followed this as well. But, sometimes I will assume that a resource is loaded when it isn't. This has happened generally with my Singleton managers. I guess the annoying timing errors I'm dealing with are due to not strictly following the Awake vs. Start rule, and I'll just have to solve them one by one.

    It's too bad that Unity doesn't allow me to insert game-wide init methods before loading in UI... something similar to didFinishLaunchingWithOptions on iOS. It would be cleaner than having init firing in an arbitrary order.
     
  5. gdbjohnson

    gdbjohnson

    Joined:
    Dec 16, 2014
    Posts:
    40
    @Strategos I do appreciate a good RTFM comment when it is deserved. But, not sure about this case.
     
  6. gdbjohnson

    gdbjohnson

    Joined:
    Dec 16, 2014
    Posts:
    40
    @Zaladur wanted to follow up with this, in case you had more ideas. I tried editing the Execution order of my script, but it still did not solve this bug. It's not a huge bug, more of an annoyance during testing, but I feel there must be a general solution to it that I feel I'm missing.

    I have a Settings script that loads basic game defaults, all performed in Awake(). I want this to run before any other code, ideally, since other scripts will reference these settings at any time during my app lifecycle.

    I am noticing that code I have in another script in OnEnable() is always executing first, before Awake() is running in my Settings script, regardless of where I put it in Execution priority, as you suggested, causing an error.
     
  7. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Several options. I've used all of them at times.
    • Use a pre loader scene
    • Use a single GameObject and have it load everything else in the level
    • Make Awake or Start a coroutine and force it to wait a couple of frames before doing its stuff
    • A straight out check to see if the resource is ready before attempting to access it
    • Writing the code in such a way that there are no conflicts
    Execution priority does not change the order of Awake, Start ect. It changes the order your scripts run. So if you have two Awakes on the same frame it will choose which one to run based on the execution priority.
     
    gdbjohnson likes this.
  8. TheSniperFan

    TheSniperFan

    Joined:
    Jul 18, 2013
    Posts:
    712
    @BoredMormon gave some good tips. Here's my take on them, just to give you a little more input to make a decision.

    "Pre-Loader" scenes could be considered 'the Unity-way' of doing things. They're often easy to set up and use nothing but the build-in tools. See LoadLevelAdditive and DontDestroyOnLoad in the documentation for more information.

    Having a root object spawn the rest is something I haven't done before. Doesn't this break the workflow unless you write yourself some editor-script that disables everything except the root object before building/running?

    Making Awake and/or Start coroutines and force them to wait a couple of frames is one of the dirtier hacks I've read here on the forums. Point is: If you have to go that way, you probably made some really, really poor design decisions before. You shouldn't have to do that.

    Always(!) checking whether the resource is available sounds like a reasonable thing at first, but also falls under the 'you shouldn't have to' category. The thing is if script A relies on script B, such a check won't fix the problem. It will merely prevent a NPE from being thrown, but script A still won't work because script B still isn't there. So you're just wasting CPU-cycles to not even fix a problem that shouldn't be there to begin with.

    Writing your code in a way that prevents temporal dependencies (which is what your problem is called), is the first thing you should do.
    Does the 'tick' really have to come before the 'tock'? ;)


    Some additional questions you should ask yourself:
    1. Does 'x' need to be a component inheriting from MonoBehavior, sitting on a GameObject? Is a 'normal' class not the better approach?
    2. Do the datastructures, components, etc. initialize themselves? If not: Was this really a good idea?
    3. Why does 'a' need to happen before 'b' happens, before 'c' happens? What have I done wrong? Is it possible to chain them in a way that only 'a' is happens, which triggers 'b' after its done, which then triggers 'c'? (See point 1)
    4. Do they really have to be called in a specific order, or is the problem something completely different?
     
    NotaNaN, gdbjohnson and Kiwasi like this.
  9. gdbjohnson

    gdbjohnson

    Joined:
    Dec 16, 2014
    Posts:
    40
    @BoredMormon "if you have two Awakes on the same frame it will choose which one to run based on the execution priority." My OnEnable on Script B is firing before my Awake on Script A with a Execution priority of -1000. So, I guess this means that OnEnable can end up preceding Awake? This seems to contradict my understanding of the expected behaviour. Why is my Script A with priority -1000 is being beat somehow in execution by Script B with OnEnable?

    Thanks for the list of ideas on setting up init regarding PreLoader scenes. While I haven't architected my app this way, I think I will make some changes based on your feedback.

    In general, I agree with all your comments regarding dependency. The modular paradigm with Unity requires that you code in this way. But, I would argue that app initialization should be one exception. Implicitly, this already happens: Unity loads its environment first, before Awakes are called. As a developer, it would be nice to have the option to hook into the space right after the Unity environment loads, but before the GameObjects are processed. Until then, I guess I have to re-write my singletons. :(