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?
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. As yet I am not that clever so I tend to use my words.
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…
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.
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
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.
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!
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).
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.
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.
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.
@catfink : something else that you can use is the RuntimeInitializeOnLoad method attribute. Methods marked with [RuntimeInitializeOnLoadMethod] are invoked once at startup.
using UnityEngine;
using UnityEngine.SceneManagement;
class MyClass : MonoBehaviour {
[RuntimeInitializeOnLoadMethod (RuntimeInitializeLoadType.BeforeSceneLoad)]
static void OnRuntimeMethodLoad () {
// Add the delegate to be called when the scene is loaded, between Awake and Start.
SceneManager.sceneLoaded += SceneLoaded;
}
static void SceneLoaded (Scene scene, LoadSceneMode loadSceneMode) {
Debug.Log(System.String.Format("Scene{0} has been loaded ({1})", scene.name, loadSceneMode.ToString()));
}
void OnDestroy (){
// Remove the delegate when the object is destroyed
SceneManager.sceneLoaded -= SceneLoaded;
}
}
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?
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?
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.