Instantiating objects into a specific scene?

So I use a system where I always have one scene loaded (Scene-1) which has things that I need consistent throughout every level. I do additive scene loads to Scene-1 as levels (Scene-X) so that only one level is loaded at a time while Scene-1 is constantly there.

I have an issue now that when I instantiate something in a Scene-X it becomes rooted in Scene-1. That object is specific to Scene-X and so when I unload Scene-X for another Scene-Y the instantiated object, since it is rooted in Scene-1, carries over to Scene-Y.

How do I move the instantiated objects in Scene-1 to Scene-X, or better yet, how do I specifically set which scene the instantiated gameObjects will be instantiated into?

3 Likes

Alright this makes sense but now I’ve run into another problem!
When the objects become instantiated, they did so on Start() which still have them instantiated in Scene-1 (as it was the active scene. With setting the active scene to Scene-X (right after the additive async operation is finished, on the same frame) they still instantiated in Scene-1 since it was the active scene at the time.
I’ve now resorted to using the OnEnable(), OnDisable() and OnLevelFinishedLoading(Scene scene, LoadSceneMode mode) delegates that come with SceneManagement (basically THIS). Essentially I’m now running this script in OnLevelFinishedLoading(). However this is only called when Scene-1 has finished loading and not Scene-X, which is what I want. Changing OnEnable() to Update() or something doesn’t work either.

How do I get this to run only once Scene-X has finished loading and is the active scene?

Okay, found my solution! Here’s how to structure your script to start as soon as your desired level has finished loading and has become the active scene:

using UnityEngine.SceneManagement;

public class CLASS : MonoBehaviour {

    void Awake(){
        SceneManager.activeSceneChanged += OnLevelFinishedLoading;
    }

    void OnDestroy(){
        SceneManager.activeSceneChanged -= OnLevelFinishedLoading;
    }

    void OnLevelFinishedLoading (Scene previousScene, Scene newScene) {
        if (newScene == desiredScene)
                        //do something//
    }
}
3 Likes

I found this trick useful when needing to instantiate a gameObject into a specific scene that is not necessarily the active scene. In this case we are parenting the instantiated object to transformInScene. transformInScene is in the scene you want to instantiate the instantiatedGameObject into.

// transformInScene is any transform in the target scene
GameObject instantiatedGameObject = Instantiate(prefab, transformInScene);
// move instantiated gameObject to root of scene
instantiatedGameObject.transform.SetParent(null);
24 Likes
9 Likes

I used the asyncLoad.completed event to make sure I was setting the active scene before anything in the scene had their Start/Awake called:

private int _currentSceneIndex;

private IEnumerator LoadSceneCoroutine(int sceneBuildIndex)
{
    AsyncOperation asyncLoad = SceneManager.LoadSceneAsync(sceneBuildIndex, LoadSceneMode.Additive);

    _currentSceneIndex = sceneBuildIndex;
    asyncLoad.completed += ActivateTransientScene;

    while (!asyncLoad.isDone) { yield return null; }

    // ..etc
}

private void ActivateTransientScene(AsyncOperation op)
{
    Scene scene = SceneManager.GetSceneByBuildIndex(_currentSceneIndex);
    SceneManager.SetActiveScene(scene);
}
4 Likes