Search Unity

  1. Unity 2019.2 is now released.
    Dismiss Notice

Important function deprecated. What is the new alternative?

Discussion in 'Scripting' started by MrDude, Aug 8, 2016.

?

Unity's replacing a rock solid system with an error prone delegate version "because it's better"

  1. Adding & removing delegates and requiring multiple functions to replace 1 function "is better"

    2.9%
  2. I disagree. Having a 1 function system that can't break is a much better idea

    37.7%
  3. I prefer completely revamped systems that come without any documentation. More please

    5.8%
  4. Just add detailed documentation and explain the nuances of this system and I'll be happy

    53.6%
Thread Status:
Not open for further replies.
  1. MrDude

    MrDude

    Joined:
    Sep 21, 2006
    Posts:
    2,569
    OnLevelWasLoaded... I use it as a very important part of some of my kits and yet you have decided to remove it in future versions of Unity. What am I supposed to do as an alternative?

    My flow:
    - Scene loads
    - objects have their Awake functions called which registers them to various events
    - OnLevelWasLoaded gets called and triggers the events that the objects registered to

    Now that function call is deprecated and I have no idea how to continue doing the above flow without resorting to adding a yield statement into some objects Start function saying "wait 2 seconds then start the events that should have been called immediately on scene load"

    I notice in the new SceneManager class there are two events called sceneLoaded and activeSceneChanged but all the info I have on them is "Add a delegate method to them". That is super unhelpful. When do I add this? Do I add this BEFORE the only scene that uses OnLevelWasLoaded is loaded or do I call it inside the scene and if the latter, when? During Awake? Is this event triggered before or after the scene is finished loading?

    In this instance you are destroying one of my kits by removing a function I depend on and give me no idea how to simulate it. I am guessing these two methods might come in handy but have no info on when they are called, in what order, what gets loaded before what, what gets called before what...

    Some assistance please. How is OnLevelWasLoaded intended to be replaced correctly? I don't want to hack my way around only to find out 6 months later "Oh, is that how I should have ACTUALLY done it?" or "Oh, so Awake is called AFTER that. I see now" some something similar. Please, what is the correct intended replacement for this function?

    Thanks
     
    Last edited: Aug 8, 2016
    Harinezumi and Bigben77 like this.
  2. TaleOf4Gamers

    TaleOf4Gamers

    Joined:
    Nov 15, 2013
    Posts:
    791
  3. MrDude

    MrDude

    Joined:
    Sep 21, 2006
    Posts:
    2,569
    I don't know. That is the point.

    If you read my post you will see I ask about the very thing you linked me to. Now if you can tell me where in the docs it explains what order the events get called in and wether or not this event gets called before or after Start or before or after OnEnabled ... hell, now that you can even load multiple scenes together, does this get called only when the scene is first loaded or when you change to it also...

    There is 0 information on how these work. I want to trigger code in 1 scene and one scene only but I need to trigger it after all scene objects have had a chance to run their Awake functions. How in the world will I know when all objects have run their Awake functions? OnLevelWasLoaded ran after Awake so that was simple... When does this event fire?

    If you can link me to more info on the item I asked a question about, that would be useful
    Pointing me to the item I asked about just because you were to lazy to spend 2 minutes reading, not so useful.

    I'll tweet my question a soon as I can figure out how to fit the question into one sentence for people with short attention spans.:p As yet I am not that clever so I tend to use my words. ;)
     
    Last edited: Aug 8, 2016
    Harinezumi and Ericroy like this.
  4. MrDude

    MrDude

    Joined:
    Sep 21, 2006
    Posts:
    2,569
    Load a scene asyncroniously... When does the sceneLoaded event get triggered? When the scene contents are loaded or after you changed to it?
    What about the activeSceneChanged... If you load 5 scenes together when does this get called?

    I have 1 scene that has to listen for these events. Do I add the delegate function to a static object during startup and have it persist for the duration of the entire game just so that when I go to that one scene, I am ready for it? Or must I do a check in all other 50 scenes and say "If scene to load is that one, register a delegate function to the event"? Or maybe I can register to the event during Awake so I can register to the event in the actual scene that I need it in... But does the event get called first or does it wait till everything has been initialized? When is it called?

    Here's an interesting one... OnAwake is called after Start if the object is in the scene when the scene loads and is disabled in the prefab. In contrast, if you instantiate the object at runtime then OnEnabled is run first, then Start and THEN the script is disabled like it was in the prefab.

    So OnEnabled can be run either before or after Start. So what about these events... Do they also have special conditions as to when they are called before or after these Start, onEnabled and Awake functions? I don't know... all they say is "Register a delegate function" and that is all the info I have...
     
  5. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    4,283
    OnLevelWasLoaded is missing from the upgrade notes too, which is a pain. The reply in the bottom of this post by @SteenLund makes me suspect that Unity deprecated this method without understanding what it does. That's probably because it's never been documented that it actually runs on objects in the scene.

    I have noticed that if you hook on SceneManager.sceneLoaded in awake, you'll get the callback in the same scene. That's hacky though, and might break.

    Unity also refuses to properly document the order in which these things happens. @Alex_May and the rest of the documentation team seems to be doing a great job, but they're fighting an uphill battle. There's a big post here you can add to about the SceneManager documentation issues.
     
    dbraunst and MrDude like this.
  6. MrDude

    MrDude

    Joined:
    Sep 21, 2006
    Posts:
    2,569
    Hey, thanks for that, man. Will go check out those threads. If Unity really didn't understand what it does (???) then perhaps we can get them to change their minds about the deprecation!!!

    +1 on the hacky way of adding to Awake. I want to know if this is how it's supposed to be or just plain dumb luck that it works some of the time. I have loads of dynamic objects loading and need precise control over what gets called when. Can't afford to try and hope. Again, thanks for the links. Will check them out
     
    MaximumTre likes this.
  7. catfink

    catfink

    Joined:
    May 23, 2015
    Posts:
    130
    Been testing this in my project to try to work out how the scenemanager class will replace OnLevelWasLoaded functionality and it's not working. At least not the way I've seen is described in a few threads now as how it should be implemented.

    So my current code uses OnLevelWasLoaded to detect when the scene has finished loading so it can do some dynamic gameobject initialization activities once the scene has finished loading. The monobehaviour containing OnLevelWasLoaded is not a singleton, so it's going to startup like all my other scripts and then once everything is initialised OnLevelWasLoaded will be called and the dynamic gameobjects will be created once the scene is good and ready for them.

    So I tried putting a setup for the callback in the awake function of the mono behaviour:

    SceneManager.sceneLoaded += OnSceneLoaded;

    So when my scene starts the awake function is run, registers the call back and hopefully once the scene loading has finished I get my callback and it calls OnSceneLoaded to go initialize the dynamic game objects.

    The issue I have is that the order of events has changed,it appears that the scene isn't actually loaded properly yet and I get null reference exceptions where it tries to access game objects that aren't initialised yet. Exactly what the OP on this thread was concerned about. Sure the level loaded event has fired but exactly when this event has fired is unclear and in my case the scene is not ready.
     
    Last edited: Sep 25, 2016
  8. MrDude

    MrDude

    Joined:
    Sep 21, 2006
    Posts:
    2,569
    I contacted Unity support and pointed them to this thread. hopefully someone will have a look at this soon and give us an answer.

    I just requested that they leave this method alone and remove it from the deprecation list. Please, if you agree with that, say so now so that by the time the Unity guys come looking they can see that I am not the only person who wants this method to remain in Unity!
     
    astracat111 and Ericroy like this.
  9. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    8,192
    Well, sure, for what it's worth, I'm with you. It makes sense to have an OnLevelWasLoaded callback, even in the modern world of SceneManager. I don't see any advantage to removing it.

    But, if they feel it's somehow cleaner to use the sceneLoaded callback, then they absolutely have to make sure it is invoked at the right time (when OnLevelWasLoaded is currently invoked).
     
    MrDude likes this.
  10. catfink

    catfink

    Joined:
    May 23, 2015
    Posts:
    130
    My feelings exactly, right now it's replacement does not work the same and the result is going to be a lot of broken assets. In my project my code doesn't work using the new method but equally I have several assets also using OnLevelWasLoaded and I'd be happy to bet they don't work on the new method either as the timing of when this is called is clearly different.

    The documentation doesn't make clear what the new timing is either, so this makes it unclear what the solution should be. There is an option of doing a yield for a few seconds after the new call back has been called to hopefully give it all time to complete (thanks MrDude for the private message on that). However, I think we would all probably agree that this is a hack at best and unreliable at worst.

    Absolutely, either leave it in OR make the new method have the same timing. I don't care which as long as one or other method is available and has the correct timing.
     
    Last edited: Sep 26, 2016
    astracat111, JoeStrout and MrDude like this.
  11. Hobonicus

    Hobonicus

    Joined:
    Aug 12, 2014
    Posts:
    40
    I too would very much like some information on the timing of sceneLoaded and how to properly replace OnLevelWasLoaded.

    Generally speaking, how much longer could we expect OnLevelWasLoaded to be available?
     
  12. Reign_of_Light

    Reign_of_Light

    Joined:
    Oct 15, 2014
    Posts:
    19
    I'd really like to know that as well.
     
  13. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Posts:
    25,604
    Sounds incredibly immature and irresponsible, should be sorted out at once. There is no excuse :)
     
  14. MrDude

    MrDude

    Joined:
    Sep 21, 2006
    Posts:
    2,569
    Well good news, bad news, chaps. I got some feedback in the form of "Please try this and let me know if it doesn't work".

    The method he had me try is the very same "add an event listener in awake and remove the listener in the response when it get's called"-thing that we've read about in here. Well I tried it in my own project and even printed the time at which the event fired and when the normal function ran and the time (save any rounding performed by the ToString() method of the float) is exactly the same. My project worked just fine so I could not provide him with a sample of how this method is NOT a valid method.

    More importantly, though, is the very fact that this is how he suggested I do it. This suggests to me that this is, in deed, the new way that Unity expects us to emulate this functionality moving forward. My original question was "Now that this function is removed from MonoBehaviour, how do we emulate it's use" and the answer is the add a function to the event during Awake and remove it again after it was called. And there you have it.

    Granted, there is still a level of confusion around if and when it gets called when loading multiple scenes at once but at least one issue has been addressed (hacky as it might feel to me personally)

    @catfink
    Roberto said that he looked at the Unity source code and according to what he saw, this should work. He said if I had an example of where it did NOT work I should submit the project to him using the bug reporter. I suggest you do that with that project you have. If he says it is supposed to work and on my end it DOES work then perhaps you have found an actual bug in the code logic so definitely send them that project if you can.
     
  15. catfink

    catfink

    Joined:
    May 23, 2015
    Posts:
    130
    OK, it definitely doesn't work for me. It would be easier to debug what's going on if the debugger worked in our project but unfortunately it doesn't (another long term issue we have had, setting a breakpoint just crashes unity). I'm not sure I can create a simplified version of what we are doing but I will look at it again and see if there is some level of explanation I can pinpoint as to why it fails.
     
  16. ricardo_arango

    ricardo_arango

    Unity Technologies

    Joined:
    Jun 18, 2009
    Posts:
    63
    @catfink: something else that you can use is the RuntimeInitializeOnLoad method attribute. Methods marked with [RuntimeInitializeOnLoadMethod] are invoked once at startup.

    https://docs.unity3d.com/ScriptReference/RuntimeInitializeOnLoadMethodAttribute.html

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.SceneManagement;
    3.  
    4. class MyClass : MonoBehaviour {
    5.    [RuntimeInitializeOnLoadMethod (RuntimeInitializeLoadType.BeforeSceneLoad)]
    6.    static void OnRuntimeMethodLoad () {
    7.       // Add the delegate to be called when the scene is loaded, between Awake and Start.
    8.       SceneManager.sceneLoaded += SceneLoaded;
    9.    }
    10.    static void SceneLoaded (Scene scene, LoadSceneMode loadSceneMode) {
    11.          Debug.Log(System.String.Format("Scene{0} has been loaded ({1})", scene.name, loadSceneMode.ToString()));
    12.    }
    13.    void OnDestroy (){
    14.       // Remove the delegate when the object is destroyed
    15.       SceneManager.sceneLoaded -= SceneLoaded;
    16.    }
    17. }
     
    Last edited: Oct 14, 2016
  17. MrDude

    MrDude

    Joined:
    Sep 21, 2006
    Posts:
    2,569
    Wait, I am confused now...
    So we call a function AFTER the scene has loaded to register to an event that is called AFTER the scene has loaded?

    Does this mean we can have any function we want, named anything we want, just run after the scene has loaded just by adding that attribute to it? If so, then why do we still need the SceneManager event to trigger another function after that? And isn't the attribute a much simpler way of triggering code after a scene has loaded? Shouldn't THAT be the go to method for replacing the old function? Sure that one took the argument of the loaded level index but I never used that anyway (and I can check the loaded scene's name if I needed to) so isn't this the easier alternative?
     
    MarkIskra likes this.
  18. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    4,283
    That's for static methods only, so it's a completely seperate use case from OnLevelWasLoaded.
     
  19. MrDude

    MrDude

    Joined:
    Sep 21, 2006
    Posts:
    2,569
    I don't see the relevance of the static function in this instance. If I have a custom class that I want to run after the level was loaded, if that class is only in the scene I want loaded, does it matter wether the function is static or not?

    But point taken. I didn't look at Roberto's code closely enough. I see he triggers a static function from a static function and THAT might be an issue... Though, if you are in the habit of creating Instance variables in Awake or as getters then passing all your init code via the instance variable is not that much of a stretch.

    You say "completely separate use case", though. By my reckoning it should work just fine. Please, enlighten me. What am I missing? Why is this so "completely separate" that it can't be used as I described? I mean, I am aware of static functions and how they can be used anywhere at any time and I am well aware that static values changed remain changed for the duration of the game and all that stuff I know... but how does using static functions with that attribute differ from having a normal, non-static function that gets triggered after a scene was loaded?
     
  20. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    4,283
    The static method gets called exactly once, whereas the OnLevelWasLoaded will be called once per object of the type. So if you have 0 or many objects, you're getting a different amount of calls.

    The static method also has to get access to the specific objects. That's easy to do exactly for singetons, but a mess for all other object types, as they'll have to be added to a list or found or something like that.
     
  21. MrDude

    MrDude

    Joined:
    Sep 21, 2006
    Posts:
    2,569
    Okay, I getcha now.

    I have never used it on more than one object per scene at a time so that is why it seemed okay to me... but yeah, if you are gonna use it on a per-object basis I see what you mean.
     
  22. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    8,192
    That is a very cool attribute, though. Like @MrDude, I think this will serve my purposes in pretty much all cases.
     
  23. Carwashh

    Carwashh

    Joined:
    Jul 28, 2012
    Posts:
    362
    Using Awake() only works once though, if you have a gameObject persistent through scenes (using DontDestroyOnLoad), then how do you call the method again?

    For example, I have my UI persistent throughout the game, displaying the correct elements for the loaded scene
    Code (csharp):
    1.  
    2. void Awake()
    3.     {
    4.         SceneManager.sceneLoaded += OnLevelFinishedLoad;
    5.     }
    6.  
    7. // void OnLevelWasLoaded(int sceneID)
    8.     void OnLevelFinishedLoad(Scene scene, LoadSceneMode mode)
    9.     {
    10.         int sceneID = scene.buildIndex;
    11.         Debug.Log("scene id: " + sceneID);
    12.  
    13.         // main menu
    14.         if (sceneID == splashScreen || sceneID == mainMenu)
    15.         {
    16.             SetMainMenu(true);
    17.             SetFinishScreen(false);
    18.         }
    19.  
    20.         // Cup select
    21.         if (sceneID == cupSelect)
    22.         {
    23.             SetMainMenu(false);
    24.             SetFinishScreen(false);
    25.         }
    26.  
    27.         // finish screen
    28.         if (sceneID == finishRaceScreen)
    29.         {
    30.             SetMainMenu(false);
    31.             SetFinishScreen(true);
    32.         }
    33.  
    34.         // track
    35.         if (sceneID >= race)
    36.         {
    37.             SetMainMenu(false);
    38.             SetFinishScreen(false);
    39.         }
    40.  
    41.         SceneManager.sceneLoaded -= OnLevelFinishedLoad;
    42.     }
    Is the best way to just have a public method which adds the delegate again when called? Which is called from a script which is only present in the newly loaded level.
     
  24. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    8,192
    No, once you've added the delegate (just once) it should get called every time a level loads. At least, that's my understanding. Are you finding it doesn't work that way?
     
  25. Carwashh

    Carwashh

    Joined:
    Jul 28, 2012
    Posts:
    362
    Yep.

    In my mainmenu scene I load the UI, with a DontDestroyOnLoad, which includes the above code - and is being called when the scene loads.
    Pressing Play then loads the next screen (currently cup select, in a racing game), the delegate doesn't get called, I fixed(?) this by adding SceneManager.sceneLoaded+= OnLevelFinishedLoad; to the play button.
    Upon finishing the race and reaching the finish (results) screen, the delegate doesn't get called. As this is loaded automatically, I'd (currently) require a script in the finish screen scene to call a method on the GameUI class.
     
  26. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    8,192
    Ah — but that's probably because you're removing the delegate at the end of OnLevelFinishedLoad, so of course it doesn't get called after that. Sorry I didn't notice this before. Have you tried this without removing your delegate?
     
  27. Reign_of_Light

    Reign_of_Light

    Joined:
    Oct 15, 2014
    Posts:
    19
    I wanted to confirm that I get NullReferenceExceptions, too, when using the sceneLoaded-delegate instead of OnLevelWasLoaded() in my game.

    Specifically I've got many, many levels, and the interface adapts to each whenever a new scene (=level) was loaded. To do this it looks for a certain object in the scene via GameObject.Find(...) and that's where the NullReferenceException occurs.
    As catfink already pointed out, the sceneLoaded-delegate is obviously being called at a point in time when the scene isn't yet fully loaded, at least that is how it appears to me as well. Using the very same GameObject.Find(...) command in OnLevelWasLoaded() works just fine.
     
    catfink likes this.
  28. Carwashh

    Carwashh

    Joined:
    Jul 28, 2012
    Posts:
    362
    I had not, I had that because I thought you have to remove the delegate..?

    It works if I don't remove the delegate, looks like I need to go do some more reading up on delegates. Thanks for the help.
     
  29. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    8,192
    Can you confirm this theory by putting a Debug.Log in your delegate method, and in the Awake method of the object you're trying to find?

    According to the docs/Unity claims in this thread, your delegate method should be invoked after all Awakes. But from what you said above, it sounds like that's not the case — a simple example proving this will be very helpful.
     
  30. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    8,192
    A delegate is just a reference to a method (possibly inside an object). I think the advice to remove the delegate is based on the assumption that you want the receiving object to be properly destroyed and cleaned up when the next scene is loaded. But if you're calling DontDestroyOnLoad on it, and you want it to be invoked on every subsequent scene load, then just don't remove the delegate and you should be fine.

    Actually, rather than removing the delegate in the delegate method itself, it seems to me you should (as a general rule) remove it in OnDestroy. Then, if your object is destroyed for whatever reason, the delegate gets cleaned up. If not, then it keeps working. Perhaps you can give that a try for us?
     
  31. MrDude

    MrDude

    Joined:
    Sep 21, 2006
    Posts:
    2,569
    Now can anyone please answer my last question from before, please? I am still confused about that...

    Why does Roberto use the attribute to run a function after the scene loads and IN that function that runs after the scene has loaded he registers to an event that is triggered after the scene loads...

    Isn't that like taking a circle and making it round or taking the Mona Lisa and making it art? It's like making water wet. What am I missing there?
     
  32. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    8,192
    I don't see the problem. Awake doesn't tell you that the scene is done loading. It's called during scene loading. If that's good enough for you, then just use that. But if you need to know when the scene is done loading, register for the delegate, and it'll be called at that time.

    Moreover, if (as we just discussed with @Carwashh) you have an object intended to live across scene changes (because you've called DontDestroyOnLoad on it), then Awake wouldn't help you out there either, because it's already awake. Again, register for the delegate to get called when the next scene is done loading.

    (EDIT: Or, use the [RuntimeInitializeOnLoadMethod] attribute on a static method, which in many cases will be even easier.)
     
  33. MrDude

    MrDude

    Joined:
    Sep 21, 2006
    Posts:
    2,569
    No, sorry you lost me... or you didn't understand the question. I didn't ask anything about Awake. Scroll up to post number 16 and look at the code sample. THAT is what I am on about. Why is he only registering to the sceneLoaded delegate AFTER the scene has loaded?
     
  34. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    8,192
    Oh yeah! You're right, in post #16, he's using the attribute (which should make the method only fire after the scene is loaded) to register a delegate (which would only fire after the scene is loaded). That does seem rather pointless.

    I thought you were asking about the technique in post #23, where you register in Awake. That one makes sense to me. But combining the attribute and the delegate does not.

    @ricardo_arango, can you explain what the thinking was there?
     
  35. Reign_of_Light

    Reign_of_Light

    Joined:
    Oct 15, 2014
    Posts:
    19
    I just tried what you suggested, and indeed, the delegate method fires its Debug.Log(), but not the Awake method of the object the delegate method is looking for. Instead I get my NullReferenceException from exactly the line in the delegate method where it's trying to find said game-object.
     
  36. MrDude

    MrDude

    Joined:
    Sep 21, 2006
    Posts:
    2,569
    I am thinking there might be a 1 frame delay like when calling instantiate the Start function is only called the next frame. I wonder... just out of curiosity and nothing else, I am curious to hear how this would work in your situation.... It shouldn't but what do you have to loose, right?

    In your objects that you want to init after scene load, in their awake function, register them to losten for an event you yourself created in some or other fioe that will always be there. Hell make a
    Code (csharp):
    1. static public System.Action afterLoaded;
    ...in any script you want, since it's static, and register your objects to that. When the scene loads, instead of using GameObject.Find, just trigger that event and see if your objects respond.

    Shoukd be interesting to see :D
     
  37. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    8,192
    OK, this seems like strong evidence that the delegate method is not firing at the expected point in the load sequence (it's firing before all objects in the scene have been created and awakened).

    I hope you'll take the time to reproduce this in a trivial project (empty except for the 1 object and 1 or 2 scripts needed to demonstrate the problem), and submit a bug report to Unity. This is an important issue which I don't think is getting much attention.
     
  38. MrDude

    MrDude

    Joined:
    Sep 21, 2006
    Posts:
    2,569
    I just went and created a sample project and i did NOT see this bug coming.... :O

    You can download the project here: http://guild.site/Downloads/UnitySample.zip

    Here is what I did:

    1. I made a scene with absolutely nothing in it except a button that calls Script1. Script 1 just loads Scene 2
    2. Scene 2 has 1 object in the scene and also instantiates a new object of the same kind. Hence 1 in scene object and 1 object spawned during awake.
    3. The object that gets loaded (and the one in the scene) have both the attribute and the event responder

    A $1 million to anyone who can guess what happened...

    1. As expected the attribute only triggered once.
    2. Both the object that was in the scene and the one that was instantiated during awake responded to the event as expected.

    Ding ding ding! Everything works as expected then, right? Wrong. I added the attribute to the objects that get loaded in scene 2. Only on those objects. The ones in scene 2. Everybody with me so far? Well the attribute caused the function to run in scene 1. As soon as the project started, the attribute was triggered.

    Either this is one hell of a serious bug or that might have been what Ricardo was trying to say above... have one script that registers all objects in all other scenes to the event in advance, before you even load the scenes... although I have no idea how you are gonna do anything on objects that are in scenes that are not loaded...

    I submitted it as a bug but clearly this is not the bug mentioned above. Hopefully, though, my project can be used as a skeleton to help you recreate yours.
     
  39. MrDude

    MrDude

    Joined:
    Sep 21, 2006
    Posts:
    2,569
    PLEASE IGNORE THE FOLLOWING

    I just need to rant as I just noticed something else that pissed me off something fierce! I reported a bug yesterday. Twice. A bug that causes Unity to crash EVERY SINGLE TIME. I can make Unity crash on a second's notice, just tell me NOW and I can make it crash... They already marked my bug report as "Closed". Nice, I have at least half a dozen crashes per day and they say "meh"... WTF? Set the build target to iOS, hit Apple+B, wait a while and there you get your nice little Unity screen replaced with the bug reporter... so they can go "Meh, who cares about building for iPhone anyway?"

    NOW YOU CAN START PAYING ATTENTION AGAIN

    I don't think that bug report is going to receive much attention. Call me crazy but that is what I think... :(
     
    Last edited: Oct 10, 2016
  40. MrDude

    MrDude

    Joined:
    Sep 21, 2006
    Posts:
    2,569
    @Reign_of_Light
    I wonder if that isn't exactly the problem.

    Consider this code:
    Code (csharp):
    1.  while (transform.childCount > 0) Destroy(transform.GetChild(0));
    That code will cause an infinite loop because you are doing a "while" that prevents the next frame from happening and the Destroy only happens AFTER the end of the current frame. I wonder if in your case, GameObject.Find can't find objects in the frame when the event gets triggered.

    In my example project above it showed that the instantiated object's Awake was called before the event triggered so by all accounts Find should be able to find it. Have you inspected the flow of your project very carefully to make sure stuff is loaded when you think it is? I have a frequent problem of running into nullreference exceptions but mine happens on objects I destroyed a few frames or a few minutes ago. The game object has been destroyed but some or other script is still holding a reference to a variable in the script attached to the destroyed object thus keeping the script in memory even though the object is long gone. I call an event, it responds and goes "Hey, where is the game object I was attached to?"

    My friend and I have deemed this phenomenon "Jaco Errors". You know what the problem is and spend days trying to fix it and then the problem was in a completely different place altogether and was totally not what you expected. Like spawning a script, printing off some value in the script via debug.log and then getting a null reference exception on the very next line in the same function saying you are trying to use something you already destroyed... and this is happening in Start... wtf? Hello, duplicate script destroyed 10 minutes ago but the event is still registered to so the script on the destroyed object is still called.

    Error seems obvious and yet you are looking in the wrong place entirely. Should not focus on why Start is not working but what OnDestroy does... Since my sample project proves you wrong, perhaps the problem is somewhere else unexpected? I really don't know, I am just spitballing ideas here. By all means, use my sample and recreate the bug. I know what it is like to want to move forward and be blocked by something stupid you just can't control. I consider myself the king of workarounds at this point :( So good luck in figuring this out...
     
  41. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    8,192
    This is what I would expect. The attribute gets added to a static method, right? Static methods aren't "on" any object at all. They're not associated an instance of the class. They're global methods, accessible always.

    If you happened to make such a method in a MonoBehaviour class (as opposed to some other class), and happened to put that MonoBehaviour on some objects in certain scenes, well... all that is entirely unrelated to the static method and when it gets called.

    Or have I missed your point again?
     
  42. MrDude

    MrDude

    Joined:
    Sep 21, 2006
    Posts:
    2,569
    Nah, this time you got it right, I just didn't expect that answer... though if I think about how I use static classes/functions/variables then I guess it does rather make sense...

    Static classes don't get instantiated or initialised or anything like that but monobehaviors need to at least BE in the scene before you can make use of them. I figured the static function would be called after scene load on the scene it is in but since it's a static function, it's like you say: they are not "on" anything so I suppose it would make sense to call it when it does in case other static values require the output from that etc...

    Okay, so 2 things:
    1. I admit to being wrong here. This makes sense and my surprise was just because I didn't think it through
    2. This means this tag bets used on game start only and unfortunately thus cannot serve our needs as a replacement :(

    So we are back to the Awake method again...
     
  43. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    8,192
    Hang on, what's this? It should get used on game start but also on the loading of any other scene. Or so I thought.

    The docs on this are quite unclear:

    (emphasis added)

    So we don't know whether it's after the loading of any scene, or only the initial loading of the game — but clearly this wasn't written by a careful writer (e.g. "an runtime"), so I assumed it was just poor writing about a reasonable (called after loading of any scene) behavior.

    Can you confirm whether this is called on only the first scene, or after loading any scene?
     
  44. MrDude

    MrDude

    Joined:
    Sep 21, 2006
    Posts:
    2,569
    DL my project... See for yourself...
    On game start only... :(

    So funny... I tried to make you see the surprise I found when code I only have in scene 2 was loaded in scene 1 and then never again. You made me see that it makes sense for it to happen that way... and after you made it make sense to me you only THEN realise what I was trying to say and now YOU are the confused one :p lol Funny stuff :p
     
  45. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    8,192
    Yeah, I thought you were saying it happened in scene 1 (where you didn't expect it), in addition to scene 2. It would make sense for it to fire in every scene. But it makes no sense for it to fire only on the first scene — or at least, it is not useful for it to do so. :(
     
  46. MrDude

    MrDude

    Joined:
    Sep 21, 2006
    Posts:
    2,569
    OMG dude... I just realised what is going on in that code sample! We missed something very important in that code! This has the potential to really kick some serious ass but also means we need to be super bloody careful with it's use and, to be honest, I think the Awake method is safer. At least @catfink 's issue might be solved with this (maybe). Per-object basis, hmmmmmm.... Should be interesting to see how you use that.

    Anyway, pay attention to the fact that in that code sample he never removes the listener! That was the giveaway.

    Instead of registering your class during awake and removing the responder upon the event being triggered, you register all your classes at the start of the game and then, when you eventually load the scene it means the class is already registered and listening for the event. This does mean that (as I would understand it) that a lot of code that is not currently loaded will receive a callback on every scene change though... so I guess you might have to add an Instance variable and have your responder do an "if (!Instance) return" to prevent runtime errors galore...

    I have not tested this but it seems like that is the intention. Register your classes to listen for this at game start and never stop listening... or register during Awake and stop listening in the function that gets called. So we now have 2 alternatives to the deprecated function.
     
  47. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    8,192
    Well yeah, that's what this guy was saying above...

    That guy really seems to be on the ball, we should listen to him. ;)

    I've recently moved to 5.4 and the deprecation warning is getting on my nerves, so I'll be testing this myself soon. I'm pretty certain the delegate method will work as described. I'm less certain what's going on with the [RuntimeInitializeOnLoadMethod] attribute, since you say that only fires on the first scene, which seems pretty pointless to me. But I'll test that too, just to be sure.
     
  48. catfink

    catfink

    Joined:
    May 23, 2015
    Posts:
    130
    This looks like it may work for my scenario, I'm going to try this.
     
  49. catfink

    catfink

    Joined:
    May 23, 2015
    Posts:
    130
    Nope, definitely not using that, it's a static function and it just fires on every scene load rather than the specific scenes I'm interested in, leaving me having to verify and then search for an instance to latch onto to run my functionality ...... not very nice at all. I'm going to build a simple project that fails and submit it as a bug. The basics of it are easy, create a serialized game object in your scene, in the awake function of a class on another gameobject register the handler. In the called onsceneloaded function try to access the serialized gameobject ..... FAIL. That's my scenario.

    The serialized gameobject throws a null reference exception ..... or in other words the scene is not loaded yet. Do exactly the same with OnLevelWasLoaded and it will work.
     
    Last edited: Oct 11, 2016
    Shorely likes this.
  50. catfink

    catfink

    Joined:
    May 23, 2015
    Posts:
    130
    This is very frustrating, I made a simple project that does the same as above and it doesn't fail, yet in my game the exact same thing does fail.... every single time. If I swap OnLevelWasLoaded back in it works everytime. No other code changes.
     
    Eluem likes this.
Thread Status:
Not open for further replies.