I’ve checked your project, and here are some of my ideas on how this can be done:
- Textbox instantiating is handled by the DialogueManager.show_text_box function. There, we can find this line of code:
get_tree().root.add_child(_text_box)
- This will add your TextBox node as a child of a node yielded by calling get_tree().root. Instead of this root node, we need to have our CanvasLayer node. Let’s create a new variable in dialogue_manager.gd:
var ui : CanvasLayer
Now we can replace the node that we call the add_child method on with the new ui variable that is supposed to hold cache of the CanvasLayer:
ui.add_child(_text_box)
instead of:
get_tree().root.add_child(_text_box)
However, this will result in an error since we’ve declared the new variable in the DialogueManager but not assigned it with anything. This raises the architectural question of how we can get the actual value with the cache of the required node. - As a quick solution, I suggest that your LevelMap should do this:
@onready var ui: CanvasLayer = $TextBoxLayer
func _ready():
DialogueManager.ui = ui
This will get the cache of the CanvasLayer node that you’ve called TextBoxLayer in your scene and store it in the ui variable. By the way, I suggest changing TextBoxLayer to something like UILayer since you don’t need an individual CanvasLayer for every UI element you have, they can share the same layer.
Next in the _ready function we pass value of the LevelMap’s ui variable to the DialogueManager’s ui variable that we’ve declared before.
This will work, but the TextBox you are instantiating will be offset. I had some problems sticking to your original implementation of passing global position to the Textbox instance since your project scaling system is rather convoluted, and Godot corrupted (again) some .tscn files for me. So, I’ve made a UI system that will remain in the same place on the screen instead. I also noticed the GJ_2024 part in the name of your project, so I suppose that stands for the game jam. I don’t want to force beginners who are tight on a game jam schedule to learn the idiosyncrasies of Godot’s UI system and leave you with the system that you may not fully understand for now, but this is the only way I found to make everything look properly.
I’ve added a CenterContainer as a child of TextBoxLayer:
┖╴TextBoxLayer
┖╴CenterContainer
Anchor points of the CenterContainer are L=0.5, T=0.8, R=0.5, B=0.8. This can be configured by setting Layout / Anchors Preset property in the inspector to Custom for the CenterContainer. Anchor Points section will appear below the Anchors Preset.
Finally, the code also needs some minor adjustments:
level_map.gd
@onready var center_container: CenterContainer = $TextBoxLayer/CenterContainer
func _ready():
DialogueManager.ui = center_container
dialogue_manager.gd
Declaration of the ui variable (changed static type):
var ui : CenterContainer
And the final form of the show_text_box function:
func show_text_box():
_text_box = text_box_scene.instantiate()
_text_box.finished_displaying.connect(on_text_box_finished_displaying)
ui.add_child(_text_box)
_can_advance_line = false
_text_box.display_text(_dialogue_lines[_current_line_index])