Search Unity

Built-in way to check if Start has been called on a MonoBehaviour?

Discussion in 'Scripting' started by botojomomo, Jan 21, 2017.

  1. botojomomo

    botojomomo

    Joined:
    Dec 28, 2016
    Posts:
    7
    I have some functionality that I want to guarantee to happen after all of my game objects have run Start. I have worked around it using events, but I have one small race condition that would be much easier to handle if I could simply check "if gameobject.startWasAlreadyCalled" - however, I can't find anything that does this.
    • I can certainly subclass everything and add this, but that would be a pain.
    • Using Awake/Start won't work, because I already rely on that ordering.
    • Checking if the object is enabled doesn't work, because it's enabled before Start.
    Is there some way to check that a GameObject/MonoBehaviour has finished Start (or is pre-first Update)?
     
  2. AndyGainey

    AndyGainey

    Joined:
    Dec 2, 2015
    Posts:
    216
    You can use the script execution order settings to guarantee that one script's Start() (and other) functions are called after all other scripts. So you put your special handling into the Start() function of a script that has a single instance in your scene, bump up the execution order value for that script, and you can be confident that once Start() on this script is called, Start() has already been called for all your other scripts with the default execution order.
     
  3. botojomomo

    botojomomo

    Joined:
    Dec 28, 2016
    Posts:
    7
    Thanks @AndyGainey that's a good idea, but that won't solve the issue. It's not a matter of script execution order, it's a matter of all of the scripts being ready before new functionality happens.
     
  4. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    No, I don't think there is any built-in way to do that. Of course you could do it yourself by adding a public "isReady" accessor that you init to false and then set to true in Start. But built-in, no.

    One thing I sometimes do in cases like this, though, is defer whatever it is that needs to happen after all scripts are started until my own Update call. In other words, I have a "didStartupStuff" flag in my script, and in Update, I just say

    Code (csharp):
    1. if (!didStartupStuff) {
    2.     DoStartupStuff();
    3.     didStartupStuff = true;
    4. }
    This is guaranteed to run after all scripts in the scene have finished their Start methods, and it keeps the knowledge of this problem in the script that actually has such a problem, rather than in all the other scripts which are simply trying to go about their business.
     
  5. takatok

    takatok

    Joined:
    Aug 18, 2016
    Posts:
    1,496
    Both of @JoeStrout suggestions are what I would use (either an isReady bool, or an Update call). Using Script Execution order can lead to some very hard to find logical bugs if you end up using it a lot and I would avoid it when necessary.
     
    ASymShade, fnnbrr and Kondziu2504 like this.
  6. Eden_

    Eden_

    Joined:
    Jan 4, 2019
    Posts:
    2
    A bit later for replying this... but I was facing the same problem. It can be a problem when you want first to activate a gameObject, for instance from a pooling system, and after that change some parameters which are modified in the Start function. First, you should realize if these parameters must be modified in the Start function. Usually, if you can give the value directly from the inspector this is not an issue. Nevertheless, if you have a system of scriptableobjects where you store the data and the parameters are given by this scriptableobject. Then it is normal that you type everything in the Start function. In that case, I found as a solution to create a simple "BaseMonobehaviour" class from where all your monobehaviour scripts inheritate. There you can define all these type of controllers without built-in way, i.e. isStarted.

    Code (CSharp):
    1. private bool isStarted = false;
    2.  
    3. public virtual void Start()
    4. {
    5.     // Start from the very last child, This must be called the last !
    6.     isStarted =  true;
    7. }
    8.  
    9. public bool HasBeenStarted
    10. {
    11.     get { return isStarted; }
    12. }
    Probably is not the best solution, it is always better to make everything as much independent as possible, but this can be usefull to pack scripts of a certain type with static properties.
     
  7. berniegp

    berniegp

    Unity Technologies

    Joined:
    Sep 9, 2020
    Posts:
    42
    Unity already has a "DidStart" flag on the C++ side since it needs to somehow make sure not to call Start() more than once. I'm looking into the possibility of exposing that in C#.

    For Unity devs: this is case #1339138
     
    pr9inichek, Genghis42, Ryiah and 5 others like this.
  8. LethalInjection

    LethalInjection

    Joined:
    Jan 25, 2015
    Posts:
    37
    Not sure if you misunderstood Andy's suggestion. I had the same problem as you. Here is what works for me.

    I set a global "isdone" bool when all the scripts have run "start" and I have that singleton code script run last in the script execution order.

    One can run wait coroutines while the "DidStart" variable is set to false at the top of your "Start" method.


    *As far using the C++ object, if you could figure it out how to expose with reflection, it might be slow to check it in Update.

    I have found multicast delegates are helpful to run empty code block if awaiting start or initialization to complete instead of conditionals.

    An interface might be easier instead of subclassing/inheritance.
     
    Last edited: May 27, 2021
  9. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,992
    You did realise that berniegp works at Unity Technologies? ^^. He was talking about officially exposing this currently internal state to the managed side. So it wouldn't be any slower than reading "isActiveSelf", "enabled" or "layer" since they are all stored on the native side. Actually almost all states of a gameobject or components are stored on the C++ side which includes the transforms position / rotation, the gameobjects name. The things that are stored on the managed side are mainly your own custom scripts.
     
  10. berniegp

    berniegp

    Unity Technologies

    Joined:
    Sep 9, 2020
    Posts:
    42
    Yes that's correct @Bunny83 :)

    I was addressing the initial issue in the subject: "Built-in way to check if Start has been called on a MonoBehaviour?". I could have clarified that bit up front, but there you go!
     
    Baste likes this.
  11. adamgolden

    adamgolden

    Joined:
    Jun 17, 2019
    Posts:
    1,555
    This would be great - I currently do something similar to what @JoeStrout suggested in a number of places, it would feel much cleaner just having a property to check like .enabled or gameObject.activeSelf etc., also I hope the devs can consider (if not already might be like this) that one might do something like
    if (!started) Start();
    and expect the property would then return true were it queried again immediately after the call.

    That is, it would be nice having access to something like .started, being able to explicitly call Start early for whatever reason and to be sure it then won't automatically be called again when it normally would be. If it's possible without a performance hit.

    ..either way, even an exposed .started or whatever it's actually called would be useful. For example as a lightweight lookup after instantiating something under a parent somewhere down a hierarchy that may or may not be disabled somewhere above it at the time. Start wouldn't run immediately if it was (if I recall correctly), so being able to check .started would have usefulness even for that alone.
     
  12. berniegp

    berniegp

    Unity Technologies

    Joined:
    Sep 9, 2020
    Posts:
    42
    This would have many more ramifications internally than just exposing the flag. It certainly won't be done as part of this request I quoted. I can't predict the future direction, but I don't expect such a feature (manual Start() call) to land any time soon unfortunately.
     
  13. adamgolden

    adamgolden

    Joined:
    Jun 17, 2019
    Posts:
    1,555
    Yeah.. I imagined if there's nothing already checking if Start has been called yet, there would be overhead/a performance hit to having that in place (and whatever other ramifications, I don't know enough about how things work behind the scenes to even think about this intelligibly).. I'd rather just keep adding a few lines where needed than lose any performance at all just to avoid having to add a few lines where needed or change whatever to avoid the need to begin with. But still, it should be useful to have access to that DidStart if that comes to be, thanks either way for looking into it.
     
  14. dkoslu

    dkoslu

    Joined:
    Jan 27, 2016
    Posts:
    1
    How about a new lifecycle phase, PostStart or SecondStart, with a built-in callback that happens after all the Start() invocations are complete, and before the first Updates are called?

    void Start() {
    // initialization
    }

    void PostStart() {
    // a second phase of initialization. All other GameObjects' Start()s have been called.
    }

    void Update() {
    // each frame
    }

    It is an issue I've run across many times. This feels like the right solution, and avoids having to put initialization code into Update().
     
    Joe-Censored likes this.
  15. Sphax84

    Sphax84

    Joined:
    Jun 23, 2015
    Posts:
    36
    Why not use Awake in such case?
     
  16. berniegp

    berniegp

    Unity Technologies

    Joined:
    Sep 9, 2020
    Posts:
    42
    Awake() is indeed one way to have 2-tiered initialization.

    The problem with PostStart (or any other name) is that someone will then need a 3-tiered initialization system :)

    For more complex needs, it's best to have a custom solution that manages initialization in the order you need it. There are many ways to achieve this. Other solutions built-in the engine would incur at least some cost in the general case where we don't need this strict ordering.
     
    Bunny83 likes this.
  17. berniegp

    berniegp

    Unity Technologies

    Joined:
    Sep 9, 2020
    Posts:
    42
    Last edited: Oct 3, 2022
  18. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,992
    Ohh, that's really great. We need much more things like that :) Unity has a lot internal state you can't really query. Of course exposing everything would be a total overkill but some essential things would be great. Maybe also start introducing a "isAlive" read only property (or better as an IsAlive() extension method) so we may can get rid of the == operator in the far future as it was suggested back then. Though that would not have priority. Breaking changes are of course something you want avoid.

    Though there are many small things where it would be convenient to have the ability to query the state. For example an "isDone" / "hasFinished" bool on the Coroutine type.
     
    Fep310 and mopthrow like this.
  19. mopthrow

    mopthrow

    Joined:
    May 8, 2020
    Posts:
    348