How to change active scene to always match scene a given gameObject is in?

This is sort of an extremely specific question. Apologies if it is confusing.

I have two scenes - SceneA and SceneB - running simultaneously in Unity. SceneB is a proxy of SceneA so I will commonly be copying full gameObjects from SceneA to SceneB. The problem is that when any gameObject in SceneB executes Update(), it doesn’t have the correct scene active.

To illustrate the problem, SceneA has a gameObject with a script that contains this code:

private void Update()
{
  Scene activeScene = SceneManager.GetActiveScene();
  Scene myScene = this.gameObject.scene;
  Debug.Log("Active Scene is '" + activeScene.name + "';  This scene is "  + myScene.name);
}

This will output “Active Scene is SceneA; This scene is SceneA”

However, when I copy this gameObject to SceneB, the output will be “Active Scene is SceneA; This scene is SceneB”.

My goal is to have the output be “Active Scene is SceneB; This scene is SceneB”.

Obviously, I could simply write into the code to change the active scene with SceneManager.SetActiveScene(SceneB), but since my goal is to copy any gameObject with any script from SceneA to SceneB, adding this code to every single script is not a feasible.

So it seems like I must have some kind of functionality that senses when the Update() is about to be called in SceneB and change the active scene to SceneB just before Updates() are executed.

I thought I might be able to do this by adding an event to the player loop, maybe by strategically placing a custom event in the loop at the right place. Unfortunately, I do not think this is a solution either. The problem here is that the events of the player loop is interlaced across scenes. For example:

  1. SceneA Update()
  2. SceneB Update()
  3. SceneA LateUpdate()
  4. SceneB LateUpdate()

Furthermore, the execution of these events across gameObjects doesn’t even seem to be grouped by scene, but rather by the order the gameObjects were created at runtime.

  1. SceneA Update() (GameObject1)
  2. SceneB Update() (GameObject2)
  3. SceneA Update() (GameObject3)
  4. SceneA LateUpdate() (GameObject1)
  5. SceneB LateUpdate() (GameObject2)
  6. SceneA LateUpdate() (GameObject3)

How can I reliably manage to change the active scene so that Update() always has the scene that the GameObject lives in set to be the active scene without making calls to SceneManager.SetActiveScene(…) in the Update() event inside of every script?

I would make a wrapper around the “move to other scene” call you’re currently using (the one that calls SceneManager.MoveGameObjectToScene();) and do the work there.

This functionality has a certain code smell to it. Are you sure you want to do this?

Also, if this is a new scene, you cannot move into it until after end of frame, same way that you cannot set it active until next frame. Scenes don’t get loaded until end of frame.

1 Like

Thanks for the reply.

Nope :upside_down_face:! I am quite dubious of my approach at this point considering I’m coming up against much more push back from unity-engine than I anticipated. The funny thing is, doing this same approach with physical objects that do not have use for Update() is easy. But once you need to switch the active scene depending on which gameObject is calling (in order to get things like Physics.Raycast to execute against the right scene), things get tricky. I also definitely have a lurking feeling that this approach might just be creating more work than it’s worth, but I gotta continue for the knowledge.

I do not see how this could work, though I am interested. Could you expand your explanation? The reason I don’t see it working out:
Imagine there is a separate ‘CopyManager’ GameObject (which lives in SceneA) that handles copying GameObjects from SceneA to SceneB, and tracks all the necessary information. How could the CopyManager know when to set SceneB as the active scene without screwing up things for all the objects in SceneA? I imagine that I could just wait for Update() to be called on the CopyManager, do something like:

SceneManager.SetActiveScene(sceneB);
foreach(var gameObject in CopiedToSceneBGameObjects){
   gameObject.SendMessage("Update")
}
SceneManager.SetActiveScene(sceneA);

However, I imagine that this is less than ideal since “Update” will get called again naturally by the player loop system which will potentially cause bugs down the line. Additionally, I am not sure that Time.deltaTime will have the value I am hoping… EDIT: I just did a test and Time.deltaTime is surprisingly not an issue.

Is there another way that I am not thinking of?

What is the particular reason you have for needing the active scene to match gameObject.scene on the objects in the proxy scene? This is not completely clear to me from the posts in this thread so far. If it is just for something like Physics.Raycast, you can use the PhysicsScene type to ensure you are using the right physics context. (When you load or create a scene, specify LocalPhysicsMode.Physics3D, then scene.GetPhysicsScene() will give you a PhysicsScene that you can use to perform physics queries on just that scene. From there you can do gameObject.scene.GetPhysicsScene().Raycast etc.)

1 Like

Thanks for your reply.

Yes, the Physics class is what is gumming things up. Physics.Raycast or Physics.SphereCast end up referencing the incorrect scene.

Let me describe my use case. I am creating a system that creates proxy scenes. When a gameObject collides with a specific trigger, it is copied from SceneA to SceneB. This copy of that gameObject includes ANY behaviour script that was on the original. My goal is for any script (written by people besides myself) to “just work” in this new scene, just as it would in the old scene. The only thing that seems to be stopping me is that Physics.Raycast (etc) executes against the wrong scene. I could of course do what you suggested by altering the scripts from Physics.Raycast to gameObject.scene.GetPhysicsScene().Raycast, but I do not have control over the End-User’s scripts. As such, I would love to find a solution that doesn’t require any changes to the scripts on these GameObjects.

It doesn’t seem like too much to ask the end user to refrain from using Physics.Raycast and switch entirely to gameObject.scene.GetPhysicsScene().Raycast, but it is not ideal. If I can’t find a solution that is zero-code-changes-necessary for the end user, I will probably scrap this feature.

Unfortunately I don’t believe there is an easy way to achieve what you want. If you want to avoid requiring users to use a PhysicsScene then you’re essentially forced to somehow work around the singleton nature of the static Physics methods. Your options in that situation are very limited and I’m not sure it’s even possible without doing some kind of code patching/hooking.

1 Like