Godot Version
4.3
Question
edit: I’ve solved the problem. I can’t delete the post, I guess, but I no longer need help.
I’m still very new to Godot (and programming in general), so please forgive any misuse of terminology or sloppy code.
I have a dialog system–the kind with a text box at the bottom of the screen. It technically works in action, but I think the way I programmed it is very inefficient for something that I would have to make hundreds of times, and I’m struggling to make it easier to ‘mass-produce’.
Each instance of dialog takes two scripts attached to two different objects-- one for the hurtbox of the object the player interacts with, and another that spawns in the dialog box and loads the text.
I implement each ‘conversation’ (from interaction to the end of speaking) by copying a script I pre-wrote for both the hurtbox and dialog-box, then changing certain parts to fit the node-path.
Below is the script for the hurtbox:
extends Area2D
# The hurtbox that will load the dialog when the player interacts with it.
@onready var dialogCHANGETHIS2 = preload('res://dialogs/CHANGETHIS').instantiate()
# Load the dialog you want to play. Change the quotations. Change the name of the variable and dialog path in quotations.
@onready var camera = get_tree().get_root().get_node("world/CanvasLayer")
@onready var player = get_tree().get_root().get_node("world/player")
# The path will vary depending on the name of the greater scene. Do '[main scene]/player'.
@onready var animations = get_tree().get_root().get_node("world/player/AnimationPlayer")
# Gets the animation player for player stoppage. Make sure the path is correct.
var speaking = false
# Determines whether the player is currently reading the dialog.
func _on_area_entered(area):
if speaking == false:
load_dialog()
# Loads the dialog when the player's interaction hitbox collides with this object's hurtbox.
func load_dialog():
# Please remember to attach the node function.
camera.add_child(dialogCHANGETHIS2)
speaking = true
# Change the name of the variable. Spawns in dialog.
animations.play(player.DIRECTION)
animations.advance(0)
animations.active = false
global.canmove = false
# Stops the player and animations
func done_dialog():
camera.remove_child(dialogCHANGETHIS2)
speaking = false
animations.active = true
global.canmove = true
# This removes the dialog from the screen and allows the player to re-interact with it. Also allows the player to move.
Below is the script for the dialog box:
extends Control
# The y value for box position is 91. This puts it bottom center of the screen.
@onready var CHANGETHIS1 = get_tree().get_root().get_node('/root/world/CHANGETHIS')
# This is changed to whatever the node path for the hurtbox is.
var dialog = [
'I\'m talking right now.',
'As it turns out, I am still talking.',
'Here is a third line. I will make this one longer, to test dialog scroll speed.'
]
# This dialog is to be changed to whatever the desired dialog is. The page corresponds to the instance of dialog.
var portrait = [
'0',
'2',
'3',
]
#Each portrait is matched with the same page number of dialog. Please continue to mark the hashtags with descriptions of...
#...the portrait to avoid confusion.
# 0: Neutral
# 1: Confounded
# 2: Neutral, eyes averted
# 3: Shine, curious
# 4: Weak smile
# 5: shocked/interested, 'wow'
var page = 0
# the page variable corresponds to the instance of dialog. Dialog and portrait both advance with the page.
var finished = false
# finished corresponds to whether the text for the page is done displaying. It is only whether the PAGE is finished.
var fullyfinished = false
# fullyfinished is the actual "finished" for dialog. When this is true, dialog will disappear.
@onready var text = $RichTextLabel
# Please keep this variable named 'text' for conciseness and simplicity.
func _ready():
load_dialog()
text.visible_ratio = 0
# Runs the load_dialog function and begins the first page at no completion when this is added as the player's child.
func _process(delta):
if Input.is_action_just_pressed('interact') and finished == true:
text.visible_ratio = 0
load_dialog()
# Advances the page if the player clicks interact and the text is done scrolling.
if finished == false and Input.is_action_just_pressed('interact'):
text.visible_ratio += 1.0
# Instantly completes the text if the interact button is pressed while the text is not done scrolling.
if text.visible_ratio == 1.0:
finished = true
else: finished = false
# Determines whether the text for the page is done scrolling.
func load_dialog():
if page < dialog.size():
text.parse_bbcode(dialog[page])
# The 'meat' of the text, runs the text if dialog is not run on the last page.
if finished == false: $Timer.paused == false
# Runs the text scrolling function if it is not done scrolling.
if finished == true: $Timer.paused == true
# Stops scrolling if text is done scrolling. Unsure if this part is necessary, but whatever
$Portraits.frame = float(portrait[page])
# Updates portrait number as the page updates. Make sure the correct portrait set is a child, named 'Portraits'.
else:
CHANGETHIS1.done_dialog()
fullyfinished = true
# Closes dialog if the dialog is finished.
page += 1
func _on_timer_timeout():
if text.visible_ratio < 1.0: text.visible_characters = text.visible_characters + 1
# Text scroll function. Reveals one character per timer tick if text is not done scrolling. Does not need to be an if.
#elif page == x and text.visible_ratio < 1.0: text.visible_ratio = text.visible_ratio + 0.05
# ^^ Unimportant potential line to use for "..." dialog to make the periods appear slower.
if fullyfinished == true:
page = 0
load_dialog()
fullyfinished = false
# Resets dialog when finished so it can be played once more upon re-interaction. Not always necessary depending on dialog.
# Not using this function will play the last line of the dialog when re-interacting. This can be helpful for...
#... 'I said what I said' kinds of dialog where it needs not to be replayed.
I’d like to know if it’s possible to simplify the process of creating dialog. There are a few issues that slow down the process:
- There are two different scripts that both need to reference each other; it would be ideal if one ‘conversation’ could be condensed into only one script/object, but I don’t know if that’s possible.
- Working with the Canvas Layer is a bit annoying, as I’ve needed to manually set it to the right position with no visual indicator every time I make a new main scene.
If there are any possible improvements, I would love to know; I’ve been throwing myself at simplifying the process to little success.