Having synced cameras in two viewports: why does adding a second button de-sync my cameras?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By toblin

Hi forum.

NOTE: For any future reader - I believe this can serve as a general instruction for anyone who is interested in having synced cameras in two (or more) viewports.

What I’m after
I want to have two viewports, each of which should contain different worlds and objects in them. I then want each viewport to have a camera, and these cameras should move in a syncronized way based on user input. Hence if I move the camera in one viewport, the camera in the other viewport should move in the exact same way. Basically showing the same location in two parallel worlds.

Problem
This all works fine with the below structure where I only have ONE button (say TestButton1). With one button, the cameras are synced, even when the button is pressed. If I add a second button however, SUDDENLY pressing either button causes the cameras to no longer be synced.

Question
As the title reads: any ideas why this would happen? Could it be a bug?

Hypothesis
It might have something to do with the camera scripts, although I can’t figure out why they would be the cause since the buttons don’t seem to interact with anything except the root node. The camera scripts that I use can be found in the links in step 3) in the replication section below.

How to replicate
You can replicate this by:

  1. Re-creating the below structure but with only one button (say TestButton1) at first. Connect the button’s pressed signal to the root node and just make it print something. Make sure to use the 2D-editor to set the size of the TabContainer.

  2. Enable the Stretch option in the Viewport containers, and setup new worlds in the Viewports, where you load the standard environment in each world.

  3. Installing the Camera Control Script(*) from the AssetLib, and replace the included scripts with the following scripts of the same names: camera_control.gd and camera_ control_gui.gd. Then attach the camera_control.gd to both cameras.

  4. Run the project and confirm that the cameras are in fact synced. Click a button by pressing escape, which enables you to move the mouse freely. Switch viewport by freeing the mouse (through pressing the escape key) and then switching tabs on the top of the screen.

  5. Add a secondary button (TestButton2), set it up like the first button (making it print something by a signal to the root node), and confirm that the cameras are no longer synced if 1) you press one of the buttons, and then 2) move the mouse AND move the camera with the arrow keys.

  6. Remove the secondary button (TestButton2), and confirm that the cameras are once again synced.

(*) by Maujoe, MIT.

Project structure:

Node (script attached where button-clicks are handled)
    -- TabContainer
    -- -- Main (ViewportContainer)
    -- -- -- Viewport
    -- -- -- -- Camera (with script camera_control.gd)
    -- -- -- -- Spatial
    -- -- -- -- -- CSGMesh
    -- -- Child (ViewportContainer)
    -- -- -- Viewport
    -- -- -- -- GridContainer
    -- -- -- -- -- TestButton1 (button with signal to the root node)
    -- -- -- -- -- TestButton2 (button with signal to the root node)
    -- -- -- -- Camera (with script camera_control.gd)
    -- -- -- -- Spatial
    -- -- -- -- -- CSGMesh

Here are the scripts that is run in the root node when the buttons are pressed:

func _on_TestButton1_pressed():
    print("testing 1!")

func _on_TestButton2_pressed():
    print("testing 2!")