Godot Version
4.4
Question
The game is a 3D game.
I have a bunch of buttons on a “Home” screen (UI with some animated 3D elements in the background). On the buttons is a signal with code like this:
func _on_button_1_pressed() -> void:
G.scene_switcher(1)
“G” above is a shorthand variable for my autoload scene (singleton) called GameManager.
In the GameManager are the following functions:
func scene_switcher(level:int):
var switch_level_path = "res://Scenes/Levels/level_" + str(level) + ".tscn"
call_deferred("deferred_switch", switch_level_path)
func deferred_switch(switch_level_path):
ResourceLoader.load_threaded_request(switch_level_path)
var packed_scene = ResourceLoader.load_threaded_get(switch_level_path)
get_tree().call_deferred("change_scene_to_packed", packed_scene)
The scene changes just fine and when the level opens it is playable with no errors other than the one in the title of this topic.
As you can see from the code, call_deferred does not alleviate this issue.
The full text of the error is as follows:
E 0:00:03:740 get_global_transform: Condition “!is_inside_tree()” is true. Returning: Transform3D()
** <C++ Source> scene/3d/node_3d.cpp:466 @ get_global_transform()**
One annoying part of this is that I can’t tell from the error text whether the error is thrown because of something in the UI Home Screen or something in the new scene that is opened.
I do know that it is not particular to one scene being opened. Any time I open a scene with a button on the UI Home Screen Godot throws this error. Additionally, as a test case, I have tried switching scenes without using a button on the UI Home Screen with code like this:
get_tree().change_scene_to_file("res://Scenes/Levels/level_1.tscn")
I placed this code in my Player script so that when the player is out of lives and dies the scene automatically changes. Still get the error.
Worth noting you are probably overcomplicating your code and delaying the scene change by a frame or more when using call_deferred
so much. You aren’t loading the scene asynchronously since load_threaded_request
immediately followed by load_threaded_get
is equivalent to using load
.
The error means during the scene change some script is still running and the node is no longer part of a scene tree so it fails. Is there any more lines of code in this script? Or in your not-deferred example?
Yeah, I used that call_deferred redundantly on purpose. Also, used the asynchronous code as an alternate - just wanted to see if made any difference compared to just using:
get_tree().change_scene_to_file("res://Scenes/Levels/level_1.tscn")
It might be overcomplicated but it’s working and I doubt it is the cause of this error.
Regardless, yes, there is tons of other code in the script. Too much to include here - nearly 1,000 lines of code. As I said, I have a lot of buttons. Most of the code is not doing anything until a signal is emitted. No process or physics loops are running. The only thing I can think of that might be trying to do anything is an animation that is on a loop. I guess, it makes little sense to me that it should be trying to run when the scene closes?
Is there any code directly after change_scene_to_file
, within that function? Any more deferred functions within that logical process?
Nothing at all. that code is running in the GameManager Singleton.
The animation is running a copy of the player character I positioned in the middle of the screen. You see the character and it’s idle animations. All the code in the player script is behind conditions that make the character completely non-interactive in the UI Home Screen Like this:
func _physics_process(delta: float) -> void:
if Player_in_UI == false:
_camera_pivot.rotation.x += _camera_input_direction.y * delta
_camera_pivot.rotation.x = clamp(_camera_pivot.rotation.x, tilt_lower_limit, tilt_upper_limit)
_camera_pivot.rotation.y += _camera_input_direction.x * delta
_camera_input_direction = Vector2.ZERO
#
# Calculate movement input and align it to the camera's direction.
var raw_input := Input.get_vector("move_left", "move_right", "move_up", "move_down", 0.4)
etc. etc.
So nothing in the Physics process is doing anything in this UI Home screen. At least, none of my code is running.
Turns out that the Player script is the culprit. Deleting the player first - This code:
func _on_button_1_pressed() -> void:
$"../num_num_player".queue_free()
G.scene_switcher(1)
eliminates the error.
Now I just need to refactor my overcomplicated and redundant mess of code - as you pointed out.
Did you connect every button to a new function? You may be able to reduce that dramatically with lines like this
var all_buttons := $levels.get_children() # or get_tree().get_nodes_in_group("LevelButtons")
for level_button: Button in all_buttons:
var level_number: int = level_button.name.to_int()
# connecting pressed to scene_switcher directly based on the button's name
level_button.pressed.connect(G.scene_switcher.bind(level_number))
Could you replace your Player with a un-scripted only the player model and a AnimationPlayer?
Thanks for helping me think through this! Cheers!