Godot Version
v4.5.stable.official [876b29033]
Question
Hello all! I’m hoping to get advice with options to solve my issue and if it is caused by my implementation or a bug with the engine.
In short I’m building a system to handle level selection and sending the player back to the hub scene; however, one of my instanced custom class’s export variable becomes null in very specific circumstances.
Before I get into my custom warp class I should probably explain my project structure as that is likely the cause of the issue.
The project’s Main Scene is set to main.tscn
and has a custom class root node called ‘Main’:
extends Node
class_name Main
@onready var scene_holder: Node = %SceneHolder
@onready var menu_holder: Node = %MenuHolder
@export var title_scene:PackedScene = null
@export var menu_scene:PackedScene = null
var scene:Scene = null
var menu:Menu = null
var allow_menu_toggle = false
func _ready() -> void:
update_scene(title_scene)
menu = menu_scene.instantiate()
menu_holder.add_child(menu)
menu.canvas_layer.visible = false
menu.connect('scene_update_request',update_scene)
func _input(event: InputEvent) -> void:
if allow_menu_toggle and event.is_action_pressed("ToggleMenu"):
menu.canvas_layer.visible = !menu.canvas_layer.visible
func update_scene(new_scene:PackedScene) -> void:
var scenes := scene_holder.get_children()
for s in scenes:
s.disconnect('scene_update_request',update_scene)
s.disconnect('allow_menu_toggle',update_allow_menu_toggle)
s.queue_free()
scene = new_scene.instantiate()
scene_holder.add_child(scene)
scene.connect('scene_update_request',update_scene)
scene.connect('allow_menu_toggle',update_allow_menu_toggle)
func update_allow_menu_toggle(allow:bool,toggle_on:bool=false) -> void:
allow_menu_toggle = allow
if allow and toggle_on: menu.canvas_layer.visible = true
The idea being that with the above class object I can have a settings menu as a child of MenuHolder
and the current level as a child of SceneHolder
. This should allow me to update the current level without disrupting the continuity of the menu settings and other game state variables stored in Main
without having to save/load data each time a level is changed.
Note the title_scene
and menu_scene
are export vars defined in main.tscn
. For now the title_scene can just be pointed to the hub level for testing.
Which brings us to problem. I’ve created a Warp
class that extends Area3D with the following code:
extends Area3D
class_name Warp
@export var warp_location:PackedScene
func _ready() -> void:
print('warp_location: ',warp_location)
connect("body_entered",_load_new_scene)
func _load_new_scene(body:PlayerController) -> void:
print('warp_location: ',warp_location,' (in load)')
if !warp_location or warp_location is not PackedScene: return
if body is not PlayerController: return
var main:Main = get_tree().get_first_node_in_group("Main")
main.update_scene(warp_location)
The above class has the exported variable warp_location
which will be the level loaded as a child of Main.SceneHolder via the update_scene function.
I have 2 levels, hub.tscn
and hub2.tscn
with an instance of the Warp class and the export variable assigned to the other level.
When I load into the hub.tscn
I am able to trigger the update scene function without issue and am dropped into hub2.tscn
; however, in hub2.tscn
the warp fails as the warp_location
variable is null
. I verified this with the print statements I added to the Warp class. And when checking hub2.tscn
I can see the export variable is assigned as hub.tscn
To add to the confusion I created a hub3.tscn
level and when I point hub2’s warp to hub3.tscn
it works without issue, but now hub3.tscn
’s warp will not work when it references hub or hub2.
It seems that when I attempt to reference a PackedScene that has already been instantiated previously the Warp’s export variable becomes null.
The extra icing on top is when I close the project and open it up again I’m unable to open any of the hub tscn files due to a dependency issue. I can fix my clicking fix dependency
and assigning another random packed scene in place where it used to reference the other hub.
Note: I am using export vars as I’m attempting to avoid hard-coding file paths. But maybe this is my only solution…
Output:
warp_location: <PackedScene#-9223371998233623023>
warp_location: <PackedScene#-9223371998233623023> (in load)
warp_location: <PackedScene#-9223371998384617970>
warp_location: <PackedScene#-9223371998384617970> (in load)
warp_location: <null>
warp_location: <null> (in load)
Is this a bug, is this the engine trying to prevent a circular dependency, do I have some misunderstanding in my logic?
Thanks for reading and providing thought!