I have a problem with Awake/Start when loading a scene Additively. I want the scene which I load additively to be the active scene when Awake/Start is invoked on the objects of that scene. Like this:
LevelOne is loaded at the beginning with some objects belonging to LevelOne.
LevelTwo is loaded additively with some objects belonging to LevelTwo.
Some of the objects belonging to LevelTwo spawns new gameobjects on Awake/Start belonging to LevelTwo
LevelOne is unloaded at some point, destroying only the objects belonging to LevelOne.
However, it seems like I cannot do this - or at least I donât know how. When Start/Awake is called (step 3) on the objects of LevelTwo, the Active Scene of the SceneManager is still set to LevelOne, making the objects belong to LevelOne. I can change the Active Scene of the SceneManager but not before Start/Awake is called. The result is that when LevelOne is unloaded (step 4), the objects created on Awake/Start is removed as well.
I have created a simple project which loads another level additively after a second. An object in the newly loaded level spawns a cylinder. After 2 seconds, the first level is then unloaded and unfortunately also destroys the cylinder.
How can I solve this issue? I am not keen on having to keep track of all the objects spawned and moving them to the new scene. Nor would I be especially pleased by inventing my own âAwakeâ and calling that after I can call SceneManager.SetActiveScene.
The project is made with Unity 5.3.2p2 - run the scene called LevelOne.
I have taken a look at my options and setting the active scene before creating any object seems to be one of them. For my sample project that would mean changing
private void Awake()
{
Debug.Log("The current scene is " + SceneManager.GetActiveScene().name);
GameObject.Instantiate(prefab);
}
to
private void Start()
{
Scene scene = SceneManager.GetSceneAt(SceneManager.sceneCount - 1);
Debug.Log("Setting scene " + scene.name);
SceneManager.SetActiveScene(scene);
Debug.Log("The current scene is " + SceneManager.GetActiveScene().name);
GameObject.Instantiate(prefab);
}
I have to change Awake to Start as setting the active scene on Awake seems to have no effect at all. For my real project, this is going to require a decent amount of rewriting, but perhaps I shouldnât have been spawning objects on Awake in the first place :\ I guess I will have to check that the active scene has been set correctly in all scripts which need to spawn objects on Awake (which I will have to replace with Start).
When using LoadSceneMode.Single instead of LoadSceneMode.Additive, Unity sets the active scene to the new scene, even for Awake calls. It seems like it would be useful to have an additional parameter for SceneManager.LoadScene* calls, which would specify whether the new scene should become the active scene as soon as it is loaded.
There is SceneManager.MoveGameObjectToScene(GameObject go, Scene scene). It says that âIt is required that the GameObject is at the root of its current sceneâ, but thatâs not a problem. In your case, this should suffice:
private void Awake()
{
Debug.Log("The current scene is " + SceneManager.GetActiveScene().name);
var go = GameObject.Instantiate(prefab);
SceneManager.MoveGameObjectToScene(go, gameObject.scene);
}
Note that I havenât tried this, so there might be problems - but those problems would be bugs.
So, sorry I didnât get back to you earlier. Got wrapped up with lunch and some code I was writing for other stuff.
Anyways, it appears you resolved your issue to an extent. And yeah, I was going to mention the Awake vs Start. Really Awake shouldnât be considered as a method to really perform anything beyond setting up internal references in that script.
The documentation is a bit vague since it predates SceneManager:
But really, you can take this to mean that you shouldnât really use Awake for game logic. But rather as just setting up references. Like a Constructor.
Use the âStartâ message for actual game logic.
As for your project, I noticed you never actually even set the active scene, this means the active scene wonât change until after LevelOne has been unloaded (which is delayed). I also noticed your âwaitâ delays for the loading and unloading depended on update.
I went and modified the project with coroutines, set the active scene, and moved the logic to Start for adding things to the new scene. Check it in linked source zip.
@Baste Yeah that seems like it would work while also allowing me to keep my object creation in Awake, as long as I check which scene the new object belongs to and move it if its not the correct one.
@lordofduct I looked at your solution and stumbled on this:
I did not know that you can yield an AsyncOperation in a coroutine. I looked at unityâs execution order Script Lifecycle Flowchart and I couldnât see when yield AsyncOperation is actually being run, but from your example I can see that it is between Awake and Start (I setup SpawnObject to execute before LoadLevelAsyncAdditiveAfterAWhile in the MonoManager/Script Execution Order to make absolutely sure that it wasnât at the same time as Start).
That means that my rewriting efforts would be limited to moving object creation from Awake to Start and moving SceneManager.LoadSceneAsync to a coroutine which also sets the active scene - avoiding any per-object checks.
For my project I think that solution will be the easiest and cause me the least head aches in the future.
Big thank you to the both of you, your responses have been super helpful