Godot Version
Godot 4.4
Question
Struggling with how to come up with my own dialogue box system, in similar vein to Undertale and Deltarune, I watched this YouTube tutorial:
While it seems to be for an older version of Godot (Godot 3, to be specific), I followed along and managed to get it all working exactly how the video showed me to - Just a simple textbox displaying some scrolling dialogue in a _ready() function.
extends CanvasLayer
const CHAR_READ_RATE = 0.0210
@onready var textbox = $"."
@onready var textbox_container = $TextboxContainer
@onready var start_symbol = $TextboxContainer/MarginContainer/HBoxContainer/Start
@onready var end_symbol = $TextboxContainer/MarginContainer/HBoxContainer/End
@onready var label = $TextboxContainer/MarginContainer/HBoxContainer/Textbox
@onready var boxspr = $TextboxContainer/MarginContainer/HBoxContainer/TextureRect
var tween: Tween
# state machine for dialogue
enum State {
READY,
READING,
FINISHED
}
var current_state = State.READY
var text_queue = []
var dialog_spr_queue = []
func _ready():
_hide_textbox()
func _process(delta):
match current_state:
State.READY:
if !text_queue.is_empty():
display_text()
display_sprite()
State.READING:
if Input.is_action_just_pressed("skip"):
label.visible_ratio = 1
end_symbol.text = ">"
tween.kill()
change_state(State.FINISHED)
typing_dialog_sfx.stop()
State.FINISHED:
if Input.is_action_just_pressed("interact"):
change_state(State.READY)
_hide_textbox()
func queue_text(next_text):
text_queue.push_front(next_text)
func display_sprite():
var next_face = dialog_spr_queue.pop_back()
boxspr.texture = next_face
func queue_face(next_face):
dialog_spr_queue.push_front(next_face)
func _hide_textbox():
start_symbol.text = ""
end_symbol.text = ""
label.text = ""
textbox_container.hide()
func show_textbox():
start_symbol.text = "
>"
textbox_container.show()
func display_text():
tween = get_tree().create_tween()
var next_text = text_queue.pop_back()
label.text = next_text
label.visible_ratio = 0.0
change_state(State.READING)
show_textbox()
tween.tween_property(label, "visible_ratio", 1.0, len(next_text) * CHAR_READ_RATE)
tween.connect("finished", on_tween_finished)
func on_tween_finished():
end_symbol.text = ">"
change_state(State.FINISHED)
func change_state(next_state):
current_state = next_state
match current_state:
State.READY:
print("Changing to be ready...")
State.READING:
print("Changing to be reading...")
State.FINISHED:
print("Changing to be finished...")
When I had it working, I got to implementing how it is in games usually - Talk to an NPC, object, whatever, and itāll show some dialogue, and the conversation will end, the box disappearing.
I did manage to sort of do that. I put an Area2D on a node, and when my player character enters that node, they can press an interact button and the box and text will appear. However, I noticed the dialogue was looping, so I simply added a signal that checks if you are in the NPCās area or not.
@onready var textbox = $"../CharacterBody2D/Camera2D2/Textbox"
var area_on = false
func _process(delta):
pass
func _physics_process(delta):
pass
func _unhandled_input(event):
if Input.is_action_just_pressed("interact") && area_on == true:
textbox.queue_text("Dialogue stuff.")
textbox.queue_text("Dialogue stuff and more.")
textbox.queue_text("And that's all I have to say.")
area_on = false
func _on_area_entered(area):
area_on = true
print("Inside area.")
func _on_area_exited(area):
area_on = false
print("Out of area.")
if ā area_on = falseā is at the end of that if statement, then the text doesnāt loop, and it simply ends. But thatās not the end of any issues ā I canāt speak to the NPC again if Iām in the same spot, Iād have to move out of the NPCās area zone and walk back in again to speak to them again. Iām aware itās because Iām turning off the area. But I canāt help but scratch my head to try and figure out a solution to not use that āarea_onā variable.
I had tried using the _hide_textbox() function from the tutorial code to hide the textbox after the conversation would normally end. But that hides the textbox from the start of the first line the NPC would speak. So Iām not seeing any lines at all. That goes for any value ā So, If I wanted to, say, after a certain line passes in the characterās conversation: Give a player a health addition, make a character dig around in their nose, do anything - It would all happen on the first line, from what Iām gathering.
I also am curious if I should be using any _input() functions for the dialogue, or even if I should be using an āifā statement for that matter. If I were, for the sake of this question, put the dialogue all in an ā_on_area_entered(area)ā signal, the dialogue does work smoothly, no looping or anything. But I donāt want that signal to make the dialogue occur.
I should also probably mention that Iām new to Godot and programming in general. Iād say Iām getting a grip on the engine as a whole, but with this specific case, I think thereās just something very crucial Iām missing. Any help I would highly appreciate.