I had a Sprite3D -> SubViewport -> PanelContainer -> Label, and I was resizing the subviewport size based on the label.size after chaging its text and didn't understand why I had to either await get_tree().process_frame to let a frame pass, or assign the label.size twice to the subviewport size, now I know why, it resizes each axis separately, interesting..
If you want to be able to change the scale of the text box and it still to be in the correct position compared to who is saying it: In the textbox display_text() function when you set global_position.x and y multiply it by the scale.x or scale.y accordingly. ex: global_position.y -= (size.y + 24)*scale.y
For anybody having issues with the NinePatchRect. Make sure when you're doing the Edit Region step that you're not resizing the box with the Orange Dots, but instead you're just clicking on the dotted lines and moving those. When you're done with the resize you should have a tictactoe board with dotted lines and the orange dots still on the edges of the Sprite.
Awesome tutorial! In case anyone else's code gets hang between the 2 resize awaits your empty text box in the 2d environment is tall enough to fit multiple lines in your current font and font size combination so the first resize doesn't necessarily causes a resize in y axis which results in the hanging. Just make the empty text box super small and it should always result in a resize as long as there's a letter in the text.
For anyone whose text box is appearing on the top left corner of the scene instead of above the NPC, be sure you're passing the correct node's global_position into the start_dialog function in your scene. Since I was attaching it to an AnimatedSprite2D inside a scene instead of attaching it to a root node it wasn't seeing the NPC's position to pass to the text box.
Followed everything in half speed, did everything i THINK right, but when i run i get an error that states :Invalid set index 'interact' (on base: 'null instance') with value of type 'Callable'. (It highlights my onready function func _ready(): interaction_area.interact = Callable(self, "_on_interact")
For anyone getting this error - make sure you get your interaction area node with @onready var interaction_area = $InteractionArea and that it's in your scene
BY THE WAY!!! If anyone is facing troubles with this in godot 4.3 then youre doing it wrong! Make sure it right! Mine works perfectly so if yours doesnt, fix it!
Great video, thanks! I had one small issue though, if the text is very short (less than 5 characters), it seems like the first "await resize" doesn't behave as I'd expect, I can't explain why, but it causes the text box to never go away. I fixed it by setting a min length to run that first resize if (text.length() > MIN_WIDTH): await resized If anyone knows of a better approach or can explain why this happens I'd be really happy to understand this better.
@user-ih3jm1dk7d sure, I've set it to 5 and that seems to work for me. I don't fully understand the details of it though so you might need a different value. I hope it helps. edit: to clarify, MIN_WIDTH is not a great name for it, as it is minimum text length really. I've set it to 5, as in 5 characters. So, if the text is longer than 5 characters, then it does the "await resized", otherwise it skips it. I'm still unaware of the larger implications of this or the underlying reason, but so far I haven't had any issues. If you gain some insight let me know as I'm interested :)
Problem is in "await resized", because "resized" is a signal. And for example, if you have margin container, that is bigger than whats inside of it, it doesnt emit a signal and code doesn't go any further
I had the same issue. Regardless of the min width, this will also happen if two consecutive lines of text are the exact same length (same character count). I solved it by only awaiting when the dialog_lines[current_line_index].length() is different from the previous length (current_line_index - 1). Although this would not catch the scenario some of you described where the the text is less than the min width.
If I have the start dialogue und continue dialogue on the same button. The dialogue finished and starts immediately again. I try making a workaround with a timer and a var = chat_finished but it doesn’t work.
I'm having a slight issue if you have any ideas on how to help; one of the places i'm trying to use the dialogue manager in game is a menu that runs while the game is paused. while I’ve been able to find alternate solutions to the "needs to be unpaused" elements, queue_free() in specific is my final hurdle from what I can tell. (at least, I assume queue_free() only works when the game is unpaused... anyways, are there any alternatives to queue_free() that may work when the game is paused?
I'm not sure, but you can try using free() instead of queue_free() as it doesn't wait until the next frame to free an object. But unfortunately, I think the bast course of action is to implement a custom time scale that will only affect specific nodes. It would work by multiplying their velocities, animation speeds, and whatever else needs to be paused, by a custom time scale amount. They eill effectively be paused, while your UI and audio will work like normal.
Great tutorial. Thank you for all the help! One problem I ran into is continuing to the next line of dialogue. My NPC just sits there on the first line of dialogue and doesn't continue. 😢 E to interact works and the label disappears, I just cant di anything for that point on
I'm not sure I understamd the problem. Does the first line text disappear, but the next one doesn't appear? What about the textbox? Can you make sure your current_line_index gets incremented by writing some print statements?
@DashNothing the textbox and the first line of dialogue appear. However, the subsequent lines do not appear and the textbox is stuck above the NPC with the first line of dialogue. I will check the current_line-index tomorrow night and get back to you!
so totally just let this minutiae consume my entire evening but if anyone else is confused by this, I thought for a very long time my NinePatchRect texture wasn't working properly, and spent a ton of time trying to fix a problem that wasn't there: what I was doing was using the scale mode tool, instead of using select mode to click and dragon the corner of the Textbox margin container. if this saves everyone, don't be like me. if you use the scale mode tool, it makes it look like the NinePatchRect is refusing to tile and scaling instead and looks like shit. I had to finally stop the video at the exact right instant and actually LOOK at what mode the editor was in. anyway um, I will continue with the tutorial now haha.
I love following your tutorials! I just have one issue I'd love your advice on. The "[E] to interact" label still appears on top of my dialogue box. How can I make it so once I hit the interact input, the label disappears so I can read the dialogue box without the "[E] to interact" text appearing on top?
Thank you! In your _input(event) function you'd get the label like $Label and call .hide() on it to hide it. In the _ready() function you would connect to DialogManager's dialog_finished signal and in the callback function call $Label.show() to show it again..
Thank you. You can see a use of scroll contaimer in my newest video about keybinding, where we use it to get a scrollable list of buttons. Is there anything specific about it that you would like to see?
Thank you so much for this tutorial! There's only an error that I just couldn't fix... When you use the connect function in the DialogueManager, connecting 'text_box.finished_displaying' to '_on_text_box_finished_displaying', it just doesnt work for me This is the error message: Invalid get index 'finished_displaying' (on base: 'MarginContainer'). I guess that means that it has something to do that I may wrote the name wrong or something.. but I really don't know, could you help?
I'm glad you enjoyed the video! Check if your finished_displaying signal in text_box.gd is spelled correctly. It needs to be spelled the same in text_box.gd and in DialogManager.gd when you connect it.
after a LOT of trial and error, for me the problem was that the preload("the_text_box_location.scn") was not the actual MarginContainer with the script but a parent node, therefore it couldn't load the proper script because it was searching one node above it. to fix this, i made the branch with the TextBox and its children a seperacte scene and preloaded that scene instead
A more in depth look at the test guy script would be appreciated! i followed the interaction manager and dialog manger videos and still a little bit confused. i see there is 2 signals on the test guy script for _on_interaction_area_body_entered and exited. i tried creating finding those signal in the node tab under the interactionarea object. where does the "InteractionLabel" come from??? is that a @onready var as well? much thanks!
I didn't go in depth with it because I only wanted to demonstrate the text box, whereas the interaction system I left up to the viewer. That being said, the system I had for this video was a bit hacky, with the InteractionLabel being a singleton any object can reposition. All in all I wouldn't recommend it. But if you're interested, I recommend the interaction system from this video: ruclips.net/video/ajCraxGAeYU/видео.htmlsi=T55JLX8nxWCiYC2b It's a very flexible system and works well with the dialog system from this video.
i have all the scripts written up with no errors, it is the implementation i am having trouble with. i have DialogManger, InteractionManager, InteractionArea scripts and object all good to go.@@DashNothing
10:35 For me, after the last line of dialog, the text box doesnt dissapear and just starts over right again. How can i make it dissapear after using it?
You probably have the same key to start and advance the dialog, so right after it gets dismissed it gets triggered again. You can call accept_event() at the end of the input function in the DialogManager to stop the event propagating further.
I'm not sure where I messed up, but I don't see the textbox UI appear? I added some prints and I can see it printing to console the text lines and responding to my taps
please please please post the source code for this. I have tried to follow the instructions pausing the video every few seconds and i can't get the dialog box to display correctly. I am sure it's one little checkbox somewhere but I can't see where.
thx for tutorial! i used this in godot 3.5 but i have some questions in code u use "await resized" in margin container but godot 3.5 don't have this method. what i can do?
As I expanded on it, I had a bunch of problems with this code skipping lines at random and a lot of bugs. But changing "_show_text_box" to be a "for each" that awaits for an input signal instead of having the input event call the function again got rid of basically all bugs
@@nexpersonal5066 Interesting. So you changed it to calling the show_text_box func inside a for eaxh loop? I wouldn't think that would make a difference because the logic flow remains the same.
@@DashNothing basically instead of having _show_text_box run once, then on the _input event call it again, I run a "for line in dialog_lines" inside of it, and in each loop await for a line_advanced signal, that is emitted in the input section, this fixes the issue of some dialog lines being skipped when chain calling the function (I implemented dynamic button generation in the chatbox for responses, and some responses spawned their own dialog boxes)
I'm assuming Godot 4.3 is different from 4.0. I've followed the tutorial probably 10 times so far, and I always get the error "Invalid access to property or key 'finished_displaying' on a base object of type 'Node2D'". My troubleshooting has mostly just been trying to ensure the signal is actually being recieved by the singleton. Regardless, great video. I implemented your interaction system from a separate video and it works beautifully. Thanks!
This tutorial was excellent, very well explained and enjoyable to follow along! I'm working on dialogues that have different branching. how would you go about adding conditions and in-game variables that can affect/be affected by dialogues, branching to a new line or entire new text? Maybe it's solvabe with a 2D array? so the first order would store the dialogue line, and the second order of the array would save a certain number of variables? it's seems that things can start getting a bit unmanageable though...
Thank you! I thought about it, and even though this is off the cuff, this is the direction I'd go in: - No, don't use nested arrays because the nesting would go crazy even at a few branching options. - There are tools to visually edit dialogs or ones that use a scripting language made for creating text based games with branching paths. You can find these in the Godot asset library. - If you want to do a quick custom implementation because your use case isn't too complicated (and I respect that and would probably do the same), I'd make seperate array variables for each branch of the dialog. For the lines where there's a decision to make I'd use a dictionary with the reference to the lines array a choice will lead to. Example: var hello = [ "Hey!", { text: "How are you?", choices: [ { text: "Good", next: goodResponse, }, { text: "Bad", next: badResponse, }, { text: "ATTACK", condition: player.has_weapon(), func: aggro_target(player) } ] } ] var goodResponse = [ "I'm glad!" ] var badResponse = [ "Sorry to hear that" ] I'd love to try to make that work. Hope this helps!
@@DashNothing Thanks for taking the time to reply and write this example, After thinking about it, as I have many text and branching, to make it easier to manage and edit I think that I will go with a Google Sheet, a tab will be a scene that contains all the dialogues for that scene. Each scene will have a column to jump to a relative row or ID in another scene if needed, so if I move a row (dialogue line) that contains a jump, the reference should not break as it´s relative. Then parse it all to a csv with the proper data, and import into the game as a List of Arrays. I'll give it more thought let's see, thanks again!
Hi! Thanks for the tutorial! I get an error at runtime on the text_box script at line 23: label.text = text_to_display "invalid set index 'text' (on base: 'nil') with value of type 'String'" You know what the cause might be? Thanks
Thanks! The error means that the label var is null. Check if you get the label correctly on the @onready var line. Either the path to the label or the label node name is wrong.
Thanks for the reply. The problem was I was starting the conversation calling the dialogueManager from the _ready() function of a npc i created...@@DashNothing
two questions: 1 - you "await" the textbox resizing signal. Without awaiting, is it possible that the resizing process takes longer than a frame and the calculations below it will be incorrect? 2 - please tell me this isn't your real typing speed because i had to pause this so often to follow along mentally and allow my little fingers to catch up
Without awaiting it is guaranteed that the calculations will be incorrect. The resize happens on the next available frame after changing the position, so we have to await it. And no, I speed up the typing so the video isn't unnecessarily tedious lol
@@DashNothingOne more question as I've been trying to tweak this. at 0:22 the line "I shouldn't waste my energy before ..." has the word "before" appear on the first line, then moves the word to the second line since it's too long to fit on the first. I would like the word to just appear on the correct line to begin with. I tried adding newline characters to where the text should break to a new line, but then the display_text() function breaks when it awaits resizing (lines 30-31 in the video) and I'm not sure why. Any ideas how to make that change work?
For this video I made a scene called InteractionLabel of type label and a script attached to it with a function to show and hide the label. Then I autoloaded that scene (you can autoload scenes as well as scripts which is quite nice). This approach isn't the best and I only did it as a quick and dirty way of getting the point across for the focus of this video, which is the dialog system. If you are interested in an easy and flexible interaction system that works with the dialog system from this video, check this out: ruclips.net/video/ajCraxGAeYU/видео.htmlsi=uiRgGisX6dkcqGpr
Thank you so much for the free tutorial! Incredibly useful and easy to follow. Do you or anyone else in the comments have any advice on how we should add the dialogue to objects/scenes in our game? That part was glossed over a bit and I'm a noob who would like more information. Thank you!
With long texts in my dialog the size.y after the two awaits only ever is 88, never greater, even if the text makes the box larger than that. This causes the text bubble to grow as more letters are added to it rather than stay the same size. Do you know why this could be happening?
I experimented and I couldn't recreate your problem. Is the number 88 something you use in a different place (like MAX_WIDTH)? Also ehat version of Godot are you using?
BY THE WAY I NEED HELP!!! I want to be able to speak to the npc if im in its area2d or something, idk how, no matter how far i am, pressing the dialogue button will always start the dialogue
I love it, just implemented it in my game. Question anyone : how would you prevent the player to move during the dialog ? is there a way to capture the input event ?
I implemented that feature in the follow up video to this one. Basically, I just call player.set_process(false) when the dialog starts and player.set_process(true) when it finishes.
wonderful tutorial! I'm trying to fix an error that says: "invalid get index '29' (on base 'String')", It let's the first line work, but then the ! breaks it I think, how to fix?
Great tutoriel! I haven't managed to make it work, evry time I run my code this error pops up:Invalid set index 'text' (on base: 'Nil') with value of type 'String'. Does anyone know how to fix this?
Seems like you don't have a reference to the label when you try to set its text. You should get the label at the top of the script with @onready. Make sure the name is correct, as it is in the scene tree (in case you renamed it).
hello thank you so much for your video but the problem now is how to make that text shown up when i interact with characters i am still a beginner and i don't know how to do that
@@Hwaz.17 i'm calling start_dialog from a Node2D node, so global position is a Vector2. Are you calling it from a Node3D, who's global_position is a Vector3?
@@Hwaz.17 Yeah, but the dialog is meant to work in 2D, thus requiring a Vector2 for position. You can try to adapt it for 3D, but that's not super straightforward.
i have a issue of there i am using godot 4.3 and there is an error coppy of ur code and 30 lines on dialogemanager text_box.finished_displaying.conect(... its return the error and tells me the finished_display geting invalid acces
Good vid, its working for the most part. For some reason, when i get to the sixth line in my seven line array, the textbox offsets to the bottom right, no longer accepts input to advance lines, and the text immediately displays all of itself instead of reading letter by letter. im not sure what to look at first to figure this out.
There is a problem with short lines that block the text box because the label is not wide enough to trigger a resize. Could it be that ykur sixth line is only a few characters long?
Hey! Thanks for the tutorial. I almost got it to work but I am getting the following error after the first line of text is displayed: Invalid get index '44' (on base: 'String'). line 44 is : label.text += text[letter_index]. I am a complete beginner at both Godot and programming at large, help would be really appreciated :)
This means your letter_index is somehow becoming larger than the number of letters in the text, which shouldn't happen. Is this error happening at the end of displaying the first textbox or after you press the advance dialog button to go onto the next textbox? Check 2 lines down from where the error happens (if letter_index >= text.length()). Make sure you wrote >= and not >. Hope this helps!
@@DashNothing ( if letter_index >= text.length(): ) is written properly, and the error is happening at the end of the first textbox being displayed. I am now somehow also getting an error for the _on_letter_display_timer_timeout() function at (_display_letter()). I guess this is because it's calling back to the function causing the error..
@@cmrdgi Check your textbox's timer and make sure oneshot is enabled. Otherwise it will keep timing down and trying to display a letter when it shouldn't.
Thanks for the great tutorial, im trying to make it so you can skip to the end of the dialogue when you press a button, i tried to just make the letter time really fast but that bugs out the audio, what other way can i do that? Thanks!
Good tutorial however, I'm confused on how you'd get the text box to disappear without the use of an area 2d, so after all lines by the character are said the text box disappears.
Im geting the error “Invalid set index ‘text’ (on base: ‘Nil’) with value of type ‘String’.” Is this because of a update that make this tutorial outdated or is it because I did something wrong?
The problem is that the label node might not be ready yet. and is therefore null a simple solution is to use call_deferred. in the show_tent_box() - might help you out. The best solution is probably to add another check/step, before display_text() that checks if label is ready/not null simple solution in show_text_box(): get_tree().root.call_deferred("add_child", text_box) text_box.call_deferred("display_text", dialog_lines[current_line_index])
Hi @DashNothing! Thanks for the awesome tutorial-it’s been really helpful! I followed along and got the dialog system mostly working, but I’m running into a problem where only the first line of dialog displays. After pressing the key to continue, it just repeats the first line instead of moving to the next one. The dialog never progresses to the following lines. Here’s what’s happening: The first line of dialog shows up correctly. When I press the key to advance, the finished_displaying signal seems to be received as expected. But the next line never appears. I’d really appreciate any tips on how to troubleshoot this! Thanks again for the tutorial.
The first line displays correctly when I use text_box.display_text(dialogu_lines[0]) and when I press space it repeats the same line(as expected), but when I use text_box.display_text(dialogu_lines[1]) it never displays anything
@@volmerandrei34 It could be that your one key press advances amd closes the dialog. What if you add a thrid line? Would two lines appear before it ends? And will dialog_lines[1] appear correctly? Also, if you want me to have a look at your code you can post your issue on my Discord
@@DashNothing could it be because in my NPC script I added @export to the lines Array? I tried more stuff and ended up deleting the first dialog line in the lines Array and now it doesn't display the second line(now first)
@@DashNothing I fixed it. I didn't want the text box to appear above the NPC but on the screen like UI so i added this lines offset_left = -Max_Width / 2 offset_right = Max_Width / 2 offset_bottom=-50. I changed them into offset_left = -Max_Width / 2+150 offset_right = Max_Width / 2-150 offset_bottom=-500 and now it works.
Everything works great, but when I interact, the dialog box spawns at the origin of the scene. It doesn't seem the give the global position. This is the code: extends Sprite2D const lines: Array[String] = [ "Hello World", "This is a test" ] func _process(delta): if Input.is_action_just_pressed("advance_dialog"): DialogueManager.start_dialog(global_position, lines)
In a line before DialogManager.start_dialog(...) can you write print(global_position) and in the output see if the position is correct. It should be something other than 0, 0. If that is correct, the problem is probably within the dialog manager's start_dialog function where it sets the textbox's position.
@@cn7rls Sounds like you didn't get the label from the tree correctly in the @onready var label = $... line. Double check if the path and the name of the label is correct.
@@cn7rls You can edit the text in the label in the editor, but then you can't display anything else and you have to remove a lot of the code that deals with displaying the text.
@DashNothing I saw a lot of typewriter tutorials but they all display all the text in one time and when I put a sound to it, it becomes so annoying. guess I'll try using the typewriter with a timer
would really love some help..please The code for the text box extends MarginContainer @onready var timer = $LetterDisplayTimer @onready var label = $MarginContainer/Label const MAX_WIDTH = 256 var text = "" var letter_index = 0 var letter_time = 0.02 var space_time = 0.06 var punc_time = 0.2 signal finished_displaying() func display_text(text_to_display: String): text = text_to_display label.text = text_to_display
@@DashNothing actually, it appears to ber something to do with the forward+ thing and not with your code, so sorry for my mistake. It happening after adding the dialogue code was coincidence.
First of all, awesome tutorial! Short and to the point, very nice :) I noticed when the camera is scrolling, the text sort of "jiggles" around a little bit. Is there a way I can fix this?
love this style of game dialog! but I was wondering if you knew a way to incorporate bbcode into the text? changing the label into a richtextlabel works and everything but I don't want the bbcode to be printed in the textbox along with the dialog, do you know if there's a way to make it skip the bbcode syntax so it doesn't get printed out?
Thanks! I would have to look into it, but I'd say you'd have to add the open and close bbcode tags into the label before showing the first letter of the line. At the dame time you'd have to change the string in the lines array to remove the bbcode tags. Then if the letters get put in between the tags in the rich text label I imagine it would work. Sounds like a good topic for a video, I'll keep it in mind.
Using Godot 4.2 I got this working. Very fun way of typing out text. However, I was hoping to add in a RichTextLabel to use BBCode. I tried a few things with mixed results. The best I could do is have a ColorRect over the label and the RichLabel on top. Does anyone know what would a good way to make RichLabelText work?
I actually did figure out how to adapt the rich text label into this over on my discord. It's actually pretty simple. If you're interested join and shoot me a message and I'll point you to it.
This is such a cool dialogue box! I am new to coding, and I am trying to replicate this in an earlier version of Godot our current project is running in, we can't use Godot 4, but 3.6 does not have the await functionality, and some other snippets throw an error - and signals confuse me. Does anyone know how to translate this code back one engine?
@@fyofyoriosity2350Godot 3 uses a yield() function instead of await. The yield function takes in 2 params: object who's signal you're waiting for, and the signal name. So in this case it would look like yield(self, "resized"). Assuming resized is a signal in Godot 3, which I'm not sure about. Reference: docs.godotengine.org/en/3.5/classes/class_%40gdscript.html#class-gdscript-method-yield
@@DashNothing #Thanks a ton! There are a few other things that don't parse correctly, #Can I ask about them as well? I'll try to leave a comprehensive list of all issues I encountered in case other Beginners 3.6 wants to follow~ #Any help would be damn appreciated. #The yield seems to be fine now~ 3.5 however has the following issues: #SOLVED #signals are having slightly different syntax - instead of ( signal fnished_displaying() ), #leave the brackets away ( signal finished_displaying ). #Similarily, don't write ( finished_displaying.emit(), but (emit_signal("finished_displaying") ). #OPEN #3.6 does not allow the user to simply write size.x - it will throw an error, saying #size was not declared in the current scope. Is it the same as self.size.x? #SOLVED (But with question) #3.6 does not have await, use yield(self, "resized") #Do I have to use yield twice here too for both x and y? #OPEN/SOLVED? #custom_minimum_size.x does not exist - Should use rect_min_size.x instead? #OPEN #Okay, the autowrap mode line is where I absolutely found no way to solve it. #TextServer seems to be an addition to GD4? But it does not at all exist; #And while not throwing errors for autowrap_mode, it also does not suggest that #parameter, which means it might not exist? Only label.autowrap is suggested #(I have to reiterate that I am a noob and there is probably something i am overlooking) #What do I write instead of ( label.autowrap_mode = TextServer.AUTOWRAP_WORD )? #OPEN #global_position.x also does not exist - can I use rect_global_position.x instead? #OPEN #var dialog_text : Array[String] = [] does not work under any circumstance. #I suspect syntax, the godot 3.6 documentation writes something like var array = [insertstuffhere], #but as a beginner I have no clue how to translate parts from one definition to another #and which ones are important : ' D It is fine if I write var arr_dialog_text : Array = [], #But how do I definte that the inside of the brackets need to be String type? #Similar question for start_dialog(position : Vector2, lines : Array[String): #OPEN # .instantiate() is not a thing, should I use .instance()? #OPEN #text_box.finished_displaying.connect(_on_textbox_finished_displaying) #Throws an error. Even if the function is made, it said it doesn't exit. #Solution potentially: #Make it into a string? The error goes away, but idk if it would still work #text_box.finished_displaying.connect("_on_textbox_finished_displaying") #OPEN # text_box.global_position does not exist. Which other to use? # especially when calling the function DialogManager.func_start_dialog(global_position, lines) #does not work. #------------------------------------------ code ------------------- #textbox script extends MarginContainer signal sig_finished_displaying_text onready var label = $MarginContainer/Label onready var timer = $LetterDisplayTimer const MAX_WIDTH = 256 #Textbox cannot be wider than this. Indicates line break. var text = "" var letter_index = 0 var letter_time = 0.03 var space_time = 0.06 var punctuation_time = 0.2 # ---- FUNCTIONS --------------------------------------------------------------- func func_display_text(text_to_display : String): text = text_to_display label.text = text
func_show_text_box() #------------------------------------------ code ------------------- #My Main from which I want to try seeing the textbox, no other controls extends Node const lines : Array = [ "Hello World!", "Lorem ipsum odor et deo.", "This is test dialogue." ] # Called when the node enters the scene tree for the first time. func _ready(): DialogManager.func_start_dialog(self.position * 3, lines)
Hello, I encounter this error and I can't find why it does this : At :6.50, timer.start(punctuation_time), timer.start(space_time) and timer.start(letter_time) appear red and the error said: ""Error at (44, 17): Identifier ''timer" not declared in the current scope" I'm using Godot 4 Thank you!
Make sure you get the reference to the timer node at the top of the script with the line @onready var timer =$... putting the name of the timer node in your scene after the $.
Hello, love the tutorials. I get en error on line 20 on textbox file. "Invalid set index 'text' (on base: 'Nil') with value of type 'String'.". I type everything as in video but it seems that when I use label.text I get nothing. The label var is set as @onready so there shouldnt be a problem
Make sure that the path to the label after $ in the @onready line is correct. Seems like Godot couldn't find your label at the path you provided and instead returns null.
@@DashNothing Yes, I tripple checked that in the first place. I even right clicked on the Label and copied its path to ensure that I don't make any typos. :/ Thats why I wrote the comment
@@kacpereee8181 You can check what's stored in var label by writing print(label) in the _ready() func to see if it was fetched. If it is in there, but it's null when you try to assign text, it could be that the variable is changed somewhere in the code.
Hello! really enjoyed the videos for the dialog and interact systems! I am running into an issue though. I have a test guy to interact with, he has line size of 4, and after i talk to him, after the second time I advance_dialog, it breaks and wont let me continue. I am not sure why this is happening. my dialog_lines.size() is 4 and my current line index is 2 when it happens. Also the text box changes location after I advance after the second box. Super confused and unsure on why this is happening and the solution. Been going through all the videos in regards to the dialog system and can't seem to find a solution to this... Any help would be awesome!
How long is the third line, then one that doesn't get displayed? There is a problem with short lines because the two resized signals of the textbox don't get called.
@@DashNothing third line is longer than the second line, the third textbox does show up but bugs out and changes location of previous textbox. But then I am unable to continue.
@@MistForgeStudios I haven't encountered that so far. Are there any special characters in the third one? What if you switch the third and second line? Does it get displayed then?
@@DashNothing Out of some strange trial and error, I commented out the line 25 - await resized, and now it works. I have no idea why that would make it work or why that would cause it to bug out. But yeah. it now works. weird. Also, thank you for the quick responses! :)
TextBox isn't a node right out of the box in Godot, instead you can use a Label node with a NinePatchRect node to make your own text box, just like in the video :)
Hey great Video! I followed along your Tutorial and managed to display the First Message, but the second message just pops up with the next text in a slightly different location and the scale from the previous text and doesn't want to advance further. I printed in the _unhandled_input(event) can_advance_line and is_dialog_active and can_advance_line seems to be at false and I don't understand why
@@zeroxxero5651 I found that changing the DialogManager's show_text_box function so can_advance_line is above text_box.display_text() line fixes this issue. So with that you can also have one letter lines (for example sometimes you'd like to just show "!").
Nicely done. The only thing i would think of to improve it is to generalize the text timer/label(make it his own scene), so you can have coherent text behaviors between different types of displaying text. I have played a lot of games, where it was so annoying that different types of text ran with different speeds/behaviors...
My solo npc dialogue its working fine but when i add another npc, both of them the dialogue are not working and not showing like pop up then disappear immediately 😞 Sorry my for grammar 😅
This happens because there is no check to see which npc is closer to the player before starting the dialogue, so both start it at the same time. You need to implement some sort of interaction system to handle interactions by distance to the player. Or you can check out my interaction system which works well with this dialogue system here ruclips.net/video/ajCraxGAeYU/видео.htmlsi=uiRgGisX6dkcqGpr
Hello, so I have been messing around with ur script for a few days, and I have a question: Do u have any idea how I could make a animation on resize? I have no idea how to do it, already tried a lot of things, none of them seem to work... If possible I would like to contact u trough discord.
I don't have a discord yet, but a popup animation for the text box is one of the things shown in the follow up video here: ruclips.net/video/QvkGnDNbLaQ/видео.htmlsi=JBMBSTvq2bDy8vsW
@@DashNothing I know how to make a popup animation, my issue is: Imagine that one npc have 2 lines of a dialogue, right? When I want to change from one to another, I dont want to make another popup instead, I want to resize with an animation the current bubble
To display the text box in 3D you could use the Sprite3D node and assign it a viewport texture to render a viewport with the UI. Here's an example for Godot 3.5, but the general method is the same for Godot 4: godotengine.org/asset-library/asset/127
hey man good video, also followed your interaction system video and it worked flawlessly. but for this one I've had a small issue. the dialog works fine however if I want to add another line of text then the first line runs fine but when i advance dialog it crashes the game (not godot just the game) and gives the following error: "Attempt to call function 'queue_free' in base 'previously freed' on a null instance." and it points to the "text_box.queue_free()" line in the DialogManager script. Any idea how to fix this? Thanks!
@@TheMxxji For each line of text a new text box is created. Your error suggests you somehow tried to use the same textbox twice. There is an example of using the dialog manager for multiple lines in the video, where I create the NPC. Passing in the array of strings eorks.
Hello! I'm trying to make your dialogue but when I start it, the only letter that display is the first one. I typed "text" and it wrote "t". Can someone help me pls?
@@legoreptyle1048 It could be because the same key is for starting a dialog and advancing lines. In the dialog manager you can call accept_ecent in dialog manager's _input() function to eat the input. Let me know if this helps.
Hi, a very cool video. only one thing I didn't understand is how to make the interact button appear when approaching a character? I don't understand where it is located in the code. and is there any other way to make the main character unable to move during the dialogue? so that he just stood in place, and at the end of the dialogue, the control appeared again?
The interaction is from this video: ruclips.net/video/ajCraxGAeYU/видео.htmlsi=payF90S3z1uuS9xS And stopping the player is in the textbox followup: ruclips.net/video/QvkGnDNbLaQ/видео.htmlsi=izBc9pxv88t2qBEw
Awesome tutorial but I have a question. I did type in both the codes being the textbox and the dialogue manager but now I'm stuck. I know you made a practice dude to print it out but I'm trying to make it so that the textbox immediately starts when the level loads so I tried to do a "func _ready():" but after that I'm completely lost. I placed the code in the textbox itself as well and that also doesn't seem to work.
Don't change the textbox or the dialog manager scripts - they work as is. Make a new scene that you want to be "talking" at the start of the game, define some lines in its script, and in the _ready() func call DialogManager.start_dialog, as shown for the NPC example in the video.
@@DashNothing Ok I set it up in a different project, did everything in the video and here's the thing. I have a character2d which has some vanilla movement code with the "const lines: Array[String] = ["hey wanna spar",]" at the top and the "func _ready():" beneath with the only line of code being "DialogManager.start_dialog" but its still not doing anything. am I supposed to place the textbox in the world itself because when I do that I get the box but no text. sorry if this is a hassle to read through because I've tried everything to get it to work and nothing has shown me progress yet.
We are taking away half of the width of the textbox from its global position to center it. If you didn't have that - there (and feel free to try this in Godot) you would just be setting the textbox's position coordinate to half of its width. This makes no sense and your textbox would be somewhere in the upper left corner of the screen.
At the end, you show a script of how to have a reference to DialogManager. But I don't know how to do the @onready for DialogManager correctly. How did you write it?
In the beginning I went to Project settings -> Autoload and loaded the DialogManager.gd script. This makes it available in all scripts in your project, so you don't need to load it with @onready, just call DialogManager from anywhere. You'll know you set it up correctly if Godot gives you autocomplete suggestion for DialogManager when you start typing. Hope that helps!
@@vindhyaganti9738 You can see the whole DialogManager script in the video. My commenz above didn't include a link, but RUclips recognized the words DialogManager(dot)gd as one. Sorry for the confusion :)
@@DashNothing Thank you so much! Just finished the tutorial, but when I try to add the "DialogManager.start_dialog(global_position, lines)" to my player script, I get the error that "lines" is not declared in the current scope. How do I resolve this?
@@vindhyaganti9738 Make sure your var lines is declared at the top of the script, along with your other variables. This way all functions will have access to lines. If you declared lines inside a function, other functions will not be able to use it
I got the textbox to work but now I want it to spawn under the player when I enable it, is there any way of doing this. Until than Imma just keep trying till it works.
When I go to test it I keep getting the error "Invalid get index '47' (on base:'String')", not sure where I went wrong. Any help would be appreciated. I checked timer to make sure it was set to one shot, and the code was written correctly (I think) as suggested to someone else experiencing this issue. However, it is still crashing with that error message.
Well it seems that your line has 47 characters, and the code tries to get the 48th character which doesn't exist. Func _display_letter() increases this index, and it should never reach a value larger than the string length because of the if letter_index >= text.length(), which is followed by a return statement. Can you post this part of your code here?
This is wild, i've been trying to do this ages but never thought to await resized. Thanks!
I had a Sprite3D -> SubViewport -> PanelContainer -> Label, and I was resizing the subviewport size based on the label.size after chaging its text and didn't understand why I had to either await get_tree().process_frame to let a frame pass, or assign the label.size twice to the subviewport size, now I know why, it resizes each axis separately, interesting..
If you want to be able to change the scale of the text box and it still to be in the correct position compared to who is saying it:
In the textbox display_text() function when you set global_position.x and y multiply it by the scale.x or scale.y accordingly.
ex: global_position.y -= (size.y + 24)*scale.y
Life saver you are
I really appreciate these tutorials! I really like how you lay out all of your nodes and whatnot. Keep doing what you're doing buddy!
For anybody having issues with the NinePatchRect. Make sure when you're doing the Edit Region step that you're not resizing the box with the Orange Dots, but instead you're just clicking on the dotted lines and moving those. When you're done with the resize you should have a tictactoe board with dotted lines and the orange dots still on the edges of the Sprite.
Omg!!! Youre a life saver! Thanks man
Awesome tutorial!
In case anyone else's code gets hang between the 2 resize awaits your empty text box in the 2d environment is tall enough to fit multiple lines in your current font and font size combination so the first resize doesn't necessarily causes a resize in y axis which results in the hanging.
Just make the empty text box super small and it should always result in a resize as long as there's a letter in the text.
or await a single frame by doing -> await get_tree().process_frame
I'm really liking your godot videos. Do you plan to continue the tutorials?
Thanks from Brazil
Thank you! Yeah, I'm working to get a video out every week
For anyone whose text box is appearing on the top left corner of the scene instead of above the NPC, be sure you're passing the correct node's global_position into the start_dialog function in your scene. Since I was attaching it to an AnimatedSprite2D inside a scene instead of attaching it to a root node it wasn't seeing the NPC's position to pass to the text box.
Works Perfect! thank you so much, im new at gameDev and godot so this was super helpful !
Followed everything in half speed, did everything i THINK right, but when i run i get an error that states :Invalid set index 'interact' (on base: 'null instance') with value of type 'Callable'. (It highlights my onready function func _ready():
interaction_area.interact = Callable(self, "_on_interact")
i hate that he talks so fast. these are great tutorials, but run so fast it's hard to keep up and understand what's happening.
For anyone getting this error - make sure you get your interaction area node with @onready var interaction_area = $InteractionArea and that it's in your scene
are you using godot 4? I had this error but I was using godot 3.5
I have learned so much just doing this along, a bit speedy but amazing tutorial !
Thank you very much my project really needed this !!
Thanks friend for sharing, your tutorials are amazing
BY THE WAY!!! If anyone is facing troubles with this in godot 4.3 then youre doing it wrong! Make sure it right! Mine works perfectly so if yours doesnt, fix it!
instant sub, no hesitation. great video!
Great video, thanks!
I had one small issue though, if the text is very short (less than 5 characters), it seems like the first "await resize" doesn't behave as I'd expect, I can't explain why, but it causes the text box to never go away. I fixed it by setting a min length to run that first resize
if (text.length() > MIN_WIDTH):
await resized
If anyone knows of a better approach or can explain why this happens I'd be really happy to understand this better.
bro can you tell me your MIN_WIDTH value? i also meet this problem, i want to fix it
@user-ih3jm1dk7d sure, I've set it to 5 and that seems to work for me. I don't fully understand the details of it though so you might need a different value. I hope it helps.
edit: to clarify, MIN_WIDTH is not a great name for it, as it is minimum text length really. I've set it to 5, as in 5 characters.
So, if the text is longer than 5 characters, then it does the "await resized", otherwise it skips it. I'm still unaware of the larger implications of this or the underlying reason, but so far I haven't had any issues. If you gain some insight let me know as I'm interested :)
Problem is in "await resized", because "resized" is a signal. And for example, if you have margin container, that is bigger than whats inside of it, it doesnt emit a signal and code doesn't go any further
@@booba_game that makes a lot of sense, I'll check this when I go back to my project and see how to clean it up so this behaviour is clearer.
Thanks!
I had the same issue. Regardless of the min width, this will also happen if two consecutive lines of text are the exact same length (same character count). I solved it by only awaiting when the dialog_lines[current_line_index].length() is different from the previous length (current_line_index - 1). Although this would not catch the scenario some of you described where the the text is less than the min width.
This video gave me headache :)
But it worked at the end haha
damn nice tutorial! short and straight to the point
If I have the start dialogue und continue dialogue on the same button. The dialogue finished and starts immediately again. I try making a workaround with a timer and a var = chat_finished but it doesn’t work.
Thx! this helped me a lot on my Indie game (even tho all the code gets me tired 😅)
real
I'm having a slight issue if you have any ideas on how to help; one of the places i'm trying to use the dialogue manager in game is a menu that runs while the game is paused. while I’ve been able to find alternate solutions to the "needs to be unpaused" elements, queue_free() in specific is my final hurdle from what I can tell. (at least, I assume queue_free() only works when the game is unpaused... anyways, are there any alternatives to queue_free() that may work when the game is paused?
I'm not sure, but you can try using free() instead of queue_free() as it doesn't wait until the next frame to free an object.
But unfortunately, I think the bast course of action is to implement a custom time scale that will only affect specific nodes. It would work by multiplying their velocities, animation speeds, and whatever else needs to be paused, by a custom time scale amount. They eill effectively be paused, while your UI and audio will work like normal.
great tutorial but i have problem when i call functon game crashes so i cant even see whats the problem any idea what could cause it?
You get an error in the debugger window at the bottom center of Godot when a crash happens. You will see what caused it in red text.
@@DashNothing godot crushes with game
@@sabajoglidze9823 Oh that is not normal. Best you can do is report an issue on Github.
Great tutorial. Thank you for all the help!
One problem I ran into is continuing to the next line of dialogue. My NPC just sits there on the first line of dialogue and doesn't continue. 😢 E to interact works and the label disappears, I just cant di anything for that point on
I'm not sure I understamd the problem. Does the first line text disappear, but the next one doesn't appear? What about the textbox? Can you make sure your current_line_index gets incremented by writing some print statements?
@DashNothing the textbox and the first line of dialogue appear. However, the subsequent lines do not appear and the textbox is stuck above the NPC with the first line of dialogue. I will check the current_line-index tomorrow night and get back to you!
How can I make a dialogue appear when I interact with an NPC?
so totally just let this minutiae consume my entire evening but if anyone else is confused by this, I thought for a very long time my NinePatchRect texture wasn't working properly, and spent a ton of time trying to fix a problem that wasn't there: what I was doing was using the scale mode tool, instead of using select mode to click and dragon the corner of the Textbox margin container. if this saves everyone, don't be like me. if you use the scale mode tool, it makes it look like the NinePatchRect is refusing to tile and scaling instead and looks like shit. I had to finally stop the video at the exact right instant and actually LOOK at what mode the editor was in.
anyway um, I will continue with the tutorial now haha.
Oof I know that feeling
yeah i have spent an hour just trying to figure out why mine isn't working. Would love to see the source code for this project DashNothing!
I love following your tutorials! I just have one issue I'd love your advice on. The "[E] to interact" label still appears on top of my dialogue box. How can I make it so once I hit the interact input, the label disappears so I can read the dialogue box without the "[E] to interact" text appearing on top?
Thank you!
In your _input(event) function you'd get the label like $Label and call .hide() on it to hide it.
In the _ready() function you would connect to DialogManager's dialog_finished signal and in the callback function call $Label.show() to show it again..
Very very good tutorial . Your work is next level .
Can you make tutorial about scroll container in godot 4 ??
Thank you. You can see a use of scroll contaimer in my newest video about keybinding, where we use it to get a scrollable list of buttons. Is there anything specific about it that you would like to see?
Thank you so much for this tutorial!
There's only an error that I just couldn't fix...
When you use the connect function in the DialogueManager, connecting 'text_box.finished_displaying' to '_on_text_box_finished_displaying', it just doesnt work for me
This is the error message: Invalid get index 'finished_displaying' (on base: 'MarginContainer').
I guess that means that it has something to do that I may wrote the name wrong or something.. but I really don't know, could you help?
I'm glad you enjoyed the video!
Check if your finished_displaying signal in text_box.gd is spelled correctly. It needs to be spelled the same in text_box.gd and in DialogManager.gd when you connect it.
after a LOT of trial and error, for me the problem was that the preload("the_text_box_location.scn") was not the actual MarginContainer with the script but a parent node, therefore it couldn't load the proper script because it was searching one node above it.
to fix this, i made the branch with the TextBox and its children a seperacte scene and preloaded that scene instead
GREAT VIDEO that helped me a lot
This is amazing, thank you
Nice nostalgia from the water level of Mario 64 :D
A more in depth look at the test guy script would be appreciated! i followed the interaction manager and dialog manger videos and still a little bit confused.
i see there is 2 signals on the test guy script for _on_interaction_area_body_entered and exited. i tried creating finding those signal in the node tab under the interactionarea object.
where does the "InteractionLabel" come from??? is that a @onready var as well? much thanks!
I didn't go in depth with it because I only wanted to demonstrate the text box, whereas the interaction system I left up to the viewer. That being said, the system I had for this video was a bit hacky, with the InteractionLabel being a singleton any object can reposition. All in all I wouldn't recommend it.
But if you're interested, I recommend the interaction system from this video:
ruclips.net/video/ajCraxGAeYU/видео.htmlsi=T55JLX8nxWCiYC2b
It's a very flexible system and works well with the dialog system from this video.
i have all the scripts written up with no errors, it is the implementation i am having trouble with. i have DialogManger, InteractionManager, InteractionArea scripts and object all good to go.@@DashNothing
is there a way a better way i can contact you? i really wanna implement your way of dialog and im a inch away from getting it to work@@DashNothing
this is beatiful!
10:35 For me, after the last line of dialog, the text box doesnt dissapear and just starts over right again. How can i make it dissapear after using it?
You probably have the same key to start and advance the dialog, so right after it gets dismissed it gets triggered again. You can call accept_event() at the end of the input function in the DialogManager to stop the event propagating further.
@@DashNothing wow such a fast response! Thank you for your awesome tutorials and dedication!
I'm not sure where I messed up, but I don't see the textbox UI appear? I added some prints and I can see it printing to console the text lines and responding to my taps
it might be behind your current layer!
Nice tutorial, thanks.
Much appreciated
please please please post the source code for this. I have tried to follow the instructions pausing the video every few seconds and i can't get the dialog box to display correctly. I am sure it's one little checkbox somewhere but I can't see where.
thx for tutorial! i used this in godot 3.5 but i have some questions
in code u use "await resized" in margin container but godot 3.5 don't have this method. what i can do?
You can use yield(self, "resized") and it will work exactly the same. Hope that helps!
thanks! it's work@@DashNothing
@@DashNothing I am using godot 3.5 and "custom_minimum_size" is not recognized, any idea?
@@mgomezmunoz92 I think you can use rect_min_size
As I expanded on it, I had a bunch of problems with this code skipping lines at random and a lot of bugs.
But changing "_show_text_box" to be a "for each" that awaits for an input signal instead of having the input event call the function again got rid of basically all bugs
@@nexpersonal5066 Interesting. So you changed it to calling the show_text_box func inside a for eaxh loop? I wouldn't think that would make a difference because the logic flow remains the same.
@@DashNothing basically instead of having _show_text_box run once, then on the _input event call it again, I run a "for line in dialog_lines" inside of it, and in each loop await for a line_advanced signal, that is emitted in the input section, this fixes the issue of some dialog lines being skipped when chain calling the function (I implemented dynamic button generation in the chatbox for responses, and some responses spawned their own dialog boxes)
I'm assuming Godot 4.3 is different from 4.0. I've followed the tutorial probably 10 times so far, and I always get the error "Invalid access to property or key 'finished_displaying' on a base object of type 'Node2D'".
My troubleshooting has mostly just been trying to ensure the signal is actually being recieved by the singleton. Regardless, great video. I implemented your interaction system from a separate video and it works beautifully.
Thanks!
Can you help me with the await problem? It says unexpected "await" in my script
Edit: NVM!! i just didnt put it in my func display text
This tutorial was excellent, very well explained and enjoyable to follow along! I'm working on dialogues that have different branching. how would you go about adding conditions and in-game variables that can affect/be affected by dialogues, branching to a new line or entire new text? Maybe it's solvabe with a 2D array? so the first order would store the dialogue line, and the second order of the array would save a certain number of variables? it's seems that things can start getting a bit unmanageable though...
Thank you!
I thought about it, and even though this is off the cuff, this is the direction I'd go in:
- No, don't use nested arrays because the nesting would go crazy even at a few branching options.
- There are tools to visually edit dialogs or ones that use a scripting language made for creating text based games with branching paths. You can find these in the Godot asset library.
- If you want to do a quick custom implementation because your use case isn't too complicated (and I respect that and would probably do the same), I'd make seperate array variables for each branch of the dialog. For the lines where there's a decision to make I'd use a dictionary with the reference to the lines array a choice will lead to. Example:
var hello = [
"Hey!",
{
text: "How are you?",
choices: [
{
text: "Good",
next: goodResponse,
},
{
text: "Bad",
next: badResponse,
},
{
text: "ATTACK",
condition: player.has_weapon(),
func: aggro_target(player)
}
]
}
]
var goodResponse = [
"I'm glad!"
]
var badResponse = [
"Sorry to hear that"
]
I'd love to try to make that work. Hope this helps!
@@DashNothing Thanks for taking the time to reply and write this example, After thinking about it, as I have many text and branching, to make it easier to manage and edit I think that I will go with a Google Sheet, a tab will be a scene that contains all the dialogues for that scene. Each scene will have a column to jump to a relative row or ID in another scene if needed, so if I move a row (dialogue line) that contains a jump, the reference should not break as it´s relative. Then parse it all to a csv with the proper data, and import into the game as a List of Arrays. I'll give it more thought let's see, thanks again!
Hello, so I noticed you didn't show how you made your interaction label class and subclasses, I was wondering if this was in a previous video?
I made the tutorial for the label and thr whole interaction system here ruclips.net/video/ajCraxGAeYU/видео.htmlsi=maNwZcsNesei19qb
Hi! Thanks for the tutorial!
I get an error at runtime on the text_box script at line 23:
label.text = text_to_display
"invalid set index 'text' (on base: 'nil') with value of type 'String'"
You know what the cause might be? Thanks
Thanks!
The error means that the label var is null. Check if you get the label correctly on the @onready var line. Either the path to the label or the label node name is wrong.
Thanks for the reply. The problem was I was starting the conversation calling the dialogueManager from the _ready() function of a npc i created...@@DashNothing
@@lucaortolani6907 had the same issue, thx for replying with fix
I’m confused. Where is that resized signal coming from?
two questions:
1 - you "await" the textbox resizing signal. Without awaiting, is it possible that the resizing process takes longer than a frame and the calculations below it will be incorrect?
2 - please tell me this isn't your real typing speed because i had to pause this so often to follow along mentally and allow my little fingers to catch up
Without awaiting it is guaranteed that the calculations will be incorrect. The resize happens on the next available frame after changing the position, so we have to await it.
And no, I speed up the typing so the video isn't unnecessarily tedious lol
@@DashNothing haha i see i see, thank you. this was a great vid.
@@DashNothingOne more question as I've been trying to tweak this. at 0:22 the line "I shouldn't waste my energy before ..." has the word "before" appear on the first line, then moves the word to the second line since it's too long to fit on the first. I would like the word to just appear on the correct line to begin with. I tried adding newline characters
to where the text should break to a new line, but then the display_text() function breaks when it awaits resizing (lines 30-31 in the video) and I'm not sure why. Any ideas how to make that change work?
Where do you put the InteractionLabel code? I know its an anutoload but I don't see it when its built and I'm having problems with my project.
For this video I made a scene called InteractionLabel of type label and a script attached to it with a function to show and hide the label. Then I autoloaded that scene (you can autoload scenes as well as scripts which is quite nice).
This approach isn't the best and I only did it as a quick and dirty way of getting the point across for the focus of this video, which is the dialog system.
If you are interested in an easy and flexible interaction system that works with the dialog system from this video, check this out: ruclips.net/video/ajCraxGAeYU/видео.htmlsi=uiRgGisX6dkcqGpr
Thank you so much for the free tutorial! Incredibly useful and easy to follow. Do you or anyone else in the comments have any advice on how we should add the dialogue to objects/scenes in our game? That part was glossed over a bit and I'm a noob who would like more information. Thank you!
I used this dialog system in my interaction tutorial. Cgeck it out: ruclips.net/video/ajCraxGAeYU/видео.htmlsi=7se_B4-1Hp8oDC06
@@DashNothing awesome, thank you again!!
With long texts in my dialog the size.y after the two awaits only ever is 88, never greater, even if the text makes the box larger than that. This causes the text bubble to grow as more letters are added to it rather than stay the same size. Do you know why this could be happening?
I experimented and I couldn't recreate your problem. Is the number 88 something you use in a different place (like MAX_WIDTH)? Also ehat version of Godot are you using?
BY THE WAY I NEED HELP!!!
I want to be able to speak to the npc if im in its area2d or something, idk how, no matter how far i am, pressing the dialogue button will always start the dialogue
@@isabellealejar-lo8sw Check out the interaction system tutorial ruclips.net/video/ajCraxGAeYU/видео.htmlsi=h0dHr8lNFu_1E99J
I love it, just implemented it in my game. Question anyone : how would you prevent the player to move during the dialog ? is there a way to capture the input event ?
I implemented that feature in the follow up video to this one. Basically, I just call player.set_process(false) when the dialog starts and player.set_process(true) when it finishes.
@@DashNothing tranks, I just looked the next video. Actually you check DialogManager.is_dialog_active at the beginning of _process.
wonderful tutorial! I'm trying to fix an error that says: "invalid get index '29' (on base 'String')", It let's the first line work, but then the ! breaks it I think, how to fix?
Great tutoriel! I haven't managed to make it work, evry time I run my code this error pops up:Invalid set index 'text' (on base: 'Nil') with value of type 'String'.
Does anyone know how to fix this?
Seems like you don't have a reference to the label when you try to set its text. You should get the label at the top of the script with @onready. Make sure the name is correct, as it is in the scene tree (in case you renamed it).
@@DashNothing I checked but still (also thanks for the quik answer)
hello thank you so much for your video but the problem now is how to make that text shown up when i interact with characters i am still a beginner and i don't know how to do that
I got a video for that too: ruclips.net/video/ajCraxGAeYU/видео.htmlsi=gaVv8iO0qUEoLaLk
@@DashNothing thank you so much
what is the equivalent of the await keyword in godot 3.5? is it "yield()" ?
Yes, it's yield()
@@DashNothing thanks!
10:47, Im getting an error saying function: agurment 1 should be "Vector2" but is "Vector3"
@@Hwaz.17 i'm calling start_dialog from a Node2D node, so global position is a Vector2. Are you calling it from a Node3D, who's global_position is a Vector3?
@@DashNothing im calling from a staticbody3d
@@Hwaz.17 Yeah, but the dialog is meant to work in 2D, thus requiring a Vector2 for position. You can try to adapt it for 3D, but that's not super straightforward.
i have a issue of there i am using godot 4.3 and there is an error coppy of ur code and 30 lines on dialogemanager text_box.finished_displaying.conect(... its return the error and tells me the finished_display geting invalid acces
its tell me (Invalid call. Nonexistent function 'conect' in base 'Signal'.) when i try to connect to signal
@@aTMiSKaMPoTi You misspelled it. Its connect with double n
@@DashNothing ty i fix it it was my bad but now i am trying to fix the size its seems too big for my view and i cant scale it
Good vid, its working for the most part. For some reason, when i get to the sixth line in my seven line array, the textbox offsets to the bottom right, no longer accepts input to advance lines, and the text immediately displays all of itself instead of reading letter by letter. im not sure what to look at first to figure this out.
There is a problem with short lines that block the text box because the label is not wide enough to trigger a resize. Could it be that ykur sixth line is only a few characters long?
Useful and good tutorial. 👍
Great tutorial
Your pixalated font and margins look nice and crisp while mine look blurry.
Try putting the resize mode to viewport or canvas_items in project settings -> window
Great tutorial man!! thanks!!
Didn't work for me, using Godot 3.5 with equivalent methods
why use godot 3.5?
So cool video! But it dont work in my project, it doesnt give me an error it just appear the first thext and then stop working.
Shame you couldn't make it work, but I'm glad you enjoyed the vid
Hey! Thanks for the tutorial. I almost got it to work but I am getting the following error after the first line of text is displayed:
Invalid get index '44' (on base: 'String'). line 44 is : label.text += text[letter_index]. I am a complete beginner at both Godot and programming at large, help would be really appreciated :)
This means your letter_index is somehow becoming larger than the number of letters in the text, which shouldn't happen. Is this error happening at the end of displaying the first textbox or after you press the advance dialog button to go onto the next textbox?
Check 2 lines down from where the error happens (if letter_index >= text.length()). Make sure you wrote >= and not >.
Hope this helps!
@@DashNothing ( if letter_index >= text.length(): ) is written properly, and the error is happening at the end of the first textbox being displayed. I am now somehow also getting an error for the _on_letter_display_timer_timeout() function at (_display_letter()). I guess this is because it's calling back to the function causing the error..
well actually it is now displaying the first letter, then breaking at (_display_letter()) only
@@cmrdgi Check your textbox's timer and make sure oneshot is enabled. Otherwise it will keep timing down and trying to display a letter when it shouldn't.
@@DashNothing omg, stupid mistake hahaha. Thanks so much for the help! Hope you have a great day :)
Thanks for the great tutorial, im trying to make it so you can skip to the end of the dialogue when you press a button, i tried to just make the letter time really fast but that bugs out the audio, what other way can i do that?
Thanks!
@@thabeetle1288 Maybe you could stop the letter timer and just add the rest of the characters into the textbox immediately
somehow my text just expands endlessly vertically
you can try increasing the custom minimum size of the label
Bro i had problem in show_label is non existence which is in base margine container help me bro to figure it out
Post the error message and I'll try to help out.
@@DashNothing bro thanks but i fix it thanks for replying bro ❤️
Good tutorial however, I'm confused on how you'd get the text box to disappear without the use of an area 2d, so after all lines by the character are said the text box disappears.
Im geting the error “Invalid set index ‘text’ (on base: ‘Nil’) with value of type ‘String’.” Is this because of a update that make this tutorial outdated or is it because I did something wrong?
The problem is that the label node might not be ready yet. and is therefore null
a simple solution is to use call_deferred. in the show_tent_box() - might help you out. The best solution is probably to add another check/step, before display_text() that checks if label is ready/not null
simple solution in show_text_box():
get_tree().root.call_deferred("add_child", text_box)
text_box.call_deferred("display_text", dialog_lines[current_line_index])
I am using Godot 4.3. The sprite of the box isnt resizing when I enter a text.
Hi @DashNothing!
Thanks for the awesome tutorial-it’s been really helpful! I followed along and got the dialog system mostly working, but I’m running into a problem where only the first line of dialog displays. After pressing the key to continue, it just repeats the first line instead of moving to the next one. The dialog never progresses to the following lines.
Here’s what’s happening:
The first line of dialog shows up correctly.
When I press the key to advance, the finished_displaying signal seems to be received as expected.
But the next line never appears.
I’d really appreciate any tips on how to troubleshoot this! Thanks again for the tutorial.
The first line displays correctly when I use text_box.display_text(dialogu_lines[0]) and when I press space it repeats the same line(as expected), but when I use text_box.display_text(dialogu_lines[1]) it never displays anything
@@volmerandrei34 It could be that your one key press advances amd closes the dialog. What if you add a thrid line? Would two lines appear before it ends? And will dialog_lines[1] appear correctly?
Also, if you want me to have a look at your code you can post your issue on my Discord
@@DashNothing could it be because in my NPC script I added @export to the lines Array? I tried more stuff and ended up deleting the first dialog line in the lines Array and now it doesn't display the second line(now first)
@volmerandrei34 @export is not the issue
@@DashNothing I fixed it. I didn't want the text box to appear above the NPC but on the screen like UI so i added this lines offset_left = -Max_Width / 2
offset_right = Max_Width / 2
offset_bottom=-50. I changed them into offset_left = -Max_Width / 2+150
offset_right = Max_Width / 2-150
offset_bottom=-500 and now it works.
Everything works great, but when I interact, the dialog box spawns at the origin of the scene. It doesn't seem the give the global position. This is the code:
extends Sprite2D
const lines: Array[String] = [
"Hello World",
"This is a test"
]
func _process(delta):
if Input.is_action_just_pressed("advance_dialog"):
DialogueManager.start_dialog(global_position, lines)
In a line before DialogManager.start_dialog(...) can you write print(global_position) and in the output see if the position is correct. It should be something other than 0, 0.
If that is correct, the problem is probably within the dialog manager's start_dialog function where it sets the textbox's position.
@@DashNothingOh, man. I am so stupid. I just forgot the line during 9:19 ...
Thanks for the quick help👍 earned a sub
I get an error "invalid set index 'text' (on base:Nill) with value of type 'string'
@@cn7rls Sounds like you didn't get the label from the tree correctly in the @onready var label = $... line. Double check if the path and the name of the label is correct.
@DashNothing is there anyway to just display the dialog box in the scene? I tried, but I got confused
In a scene* like a message for player or something like that
@@cn7rls You can edit the text in the label in the editor, but then you can't display anything else and you have to remove a lot of the code that deals with displaying the text.
@DashNothing I saw a lot of typewriter tutorials but they all display all the text in one time and when I put a sound to it, it becomes so annoying. guess I'll try using the typewriter with a timer
func _display_letter (): is giving me a "Standalone lambdas cannot be accessed. Consider assigning it to a variable." error
Sounds like you're missing the keyword func in front of the _display_letter function to me
@DashNothing i seem to have a problem advancing my dialog, it desplays the text boc and first line no problem but dont want to go to the next line
Very good video. What software do you use to create the text box sprite?
@@ssihanji6802 Thanks. I used Aseprite
Hi, great tutorial! I have a question. Can you implement this with a localization system?
I did this tutorial and my whole screen glitching up when it spawns.. :/
would really love some help..please
The code for the text box
extends MarginContainer
@onready var timer = $LetterDisplayTimer
@onready var label = $MarginContainer/Label
const MAX_WIDTH = 256
var text = ""
var letter_index = 0
var letter_time = 0.02
var space_time = 0.06
var punc_time = 0.2
signal finished_displaying()
func display_text(text_to_display: String):
text = text_to_display
label.text = text_to_display
await resized
custom_minimum_size.x = min(size.x, MAX_WIDTH)
if size.x > MAX_WIDTH:
label.autowrap_mode = TextServer.AUTOWRAP_WORD
await resized
await resized
custom_minimum_size.y = size.y
global_position.x -= size.x / 2
global_position.y -= size.y + 24
label.text = ""
_display_letter()
func _display_letter():
label.text += text[letter_index]
letter_index +=1
if letter_index >= text.length():
finished_displaying.emit()
return
match text[letter_index]:
"!", ".", ",", "?":
timer.start(punc_time)
" ":
timer.start(space_time)
_:
timer.start(letter_time)
func _on_letter_display_timer_timeout():
_display_letter()
Code for the Dialogue Manager.
extends Node
@onready var text_box_scene = preload("res://textbox.tscn")
var dialog_lines: Array[String] = []
var current_line_index = 0
var text_box
var text_box_position: Vector2
var is_dialog_active= false
var can_advanced_line = false
func start_dialog(position: Vector2, lines: Array[String]):
if is_dialog_active:
return
dialog_lines = lines
text_box_position = position
_show_text_box()
is_dialog_active = true
func _show_text_box():
text_box = text_box_scene.instantiate()
text_box.finished_displaying.connect(_on_text_box_finished_displaying)
get_tree().root.add_child(text_box)
text_box.global_position = text_box_position
text_box.display_text(dialog_lines[current_line_index])
can_advanced_line = false
func _on_text_box_finished_displaying():
can_advanced_line = true
func _unhandled_input(event):
if (
event.is_action_pressed("advance_dialog") &&
is_dialog_active &&
can_advanced_line
):
text_box.queue_free()
current_line_index += 1
if current_line_index >= dialog_lines.size():
is_dialog_active = false
current_line_index = 0
return
_show_text_box()
Just..why would my whole screen be glitching up. Any help, I would be very thankful of.
@@CosmicKingEvol Well that's new. How exactly is it glitching up? What do you see?
@@DashNothing actually, it appears to ber something to do with the forward+ thing and not with your code, so sorry for my mistake. It happening after adding the dialogue code was coincidence.
First of all, awesome tutorial! Short and to the point, very nice :)
I noticed when the camera is scrolling, the text sort of "jiggles" around a little bit. Is there a way I can fix this?
@@kent8547 Try setting the stretch mode to viewport in project settings -> window
love this style of game dialog! but I was wondering if you knew a way to incorporate bbcode into the text? changing the label into a richtextlabel works and everything but I don't want the bbcode to be printed in the textbox along with the dialog, do you know if there's a way to make it skip the bbcode syntax so it doesn't get printed out?
Thanks! I would have to look into it, but I'd say you'd have to add the open and close bbcode tags into the label before showing the first letter of the line. At the dame time you'd have to change the string in the lines array to remove the bbcode tags. Then if the letters get put in between the tags in the rich text label I imagine it would work.
Sounds like a good topic for a video, I'll keep it in mind.
Using Godot 4.2 I got this working. Very fun way of typing out text. However, I was hoping to add in a RichTextLabel to use BBCode. I tried a few things with mixed results. The best I could do is have a ColorRect over the label and the RichLabel on top. Does anyone know what would a good way to make RichLabelText work?
I actually did figure out how to adapt the rich text label into this over on my discord. It's actually pretty simple. If you're interested join and shoot me a message and I'll point you to it.
@@DashNothing Awesome, done and done. Thank you.
Great tutorial, unfortunately, in my case, the box shows up, writes down the first character and stops.
its only typing the first character and stops
@@gameswithzeta6779 Could be an issue with the timer. I'd debug it's timeout callback, if it gets executed at all.
@@DashNothing will check it out thanks
This is such a cool dialogue box! I am new to coding, and I am trying to replicate this in an earlier version of Godot our current project is running in, we can't use Godot 4, but 3.6 does not have the await functionality, and some other snippets throw an error - and signals confuse me. Does anyone know how to translate this code back one engine?
@@fyofyoriosity2350Godot 3 uses a yield() function instead of await. The yield function takes in 2 params: object who's signal you're waiting for, and the signal name. So in this case it would look like yield(self, "resized"). Assuming resized is a signal in Godot 3, which I'm not sure about. Reference: docs.godotengine.org/en/3.5/classes/class_%40gdscript.html#class-gdscript-method-yield
@@DashNothing
#Thanks a ton! There are a few other things that don't parse correctly,
#Can I ask about them as well? I'll try to leave a comprehensive list of all issues I encountered in case other Beginners 3.6 wants to follow~
#Any help would be damn appreciated.
#The yield seems to be fine now~ 3.5 however has the following issues:
#SOLVED
#signals are having slightly different syntax - instead of ( signal fnished_displaying() ),
#leave the brackets away ( signal finished_displaying ).
#Similarily, don't write ( finished_displaying.emit(), but (emit_signal("finished_displaying") ).
#OPEN
#3.6 does not allow the user to simply write size.x - it will throw an error, saying
#size was not declared in the current scope. Is it the same as self.size.x?
#SOLVED (But with question)
#3.6 does not have await, use yield(self, "resized")
#Do I have to use yield twice here too for both x and y?
#OPEN/SOLVED?
#custom_minimum_size.x does not exist - Should use rect_min_size.x instead?
#OPEN
#Okay, the autowrap mode line is where I absolutely found no way to solve it.
#TextServer seems to be an addition to GD4? But it does not at all exist;
#And while not throwing errors for autowrap_mode, it also does not suggest that
#parameter, which means it might not exist? Only label.autowrap is suggested
#(I have to reiterate that I am a noob and there is probably something i am overlooking)
#What do I write instead of ( label.autowrap_mode = TextServer.AUTOWRAP_WORD )?
#OPEN
#global_position.x also does not exist - can I use rect_global_position.x instead?
#OPEN
#var dialog_text : Array[String] = [] does not work under any circumstance.
#I suspect syntax, the godot 3.6 documentation writes something like var array = [insertstuffhere],
#but as a beginner I have no clue how to translate parts from one definition to another
#and which ones are important : ' D It is fine if I write var arr_dialog_text : Array = [],
#But how do I definte that the inside of the brackets need to be String type?
#Similar question for start_dialog(position : Vector2, lines : Array[String):
#OPEN
# .instantiate() is not a thing, should I use .instance()?
#OPEN
#text_box.finished_displaying.connect(_on_textbox_finished_displaying)
#Throws an error. Even if the function is made, it said it doesn't exit.
#Solution potentially:
#Make it into a string? The error goes away, but idk if it would still work
#text_box.finished_displaying.connect("_on_textbox_finished_displaying")
#OPEN
# text_box.global_position does not exist. Which other to use?
# especially when calling the function DialogManager.func_start_dialog(global_position, lines)
#does not work.
#------------------------------------------ code -------------------
#textbox script
extends MarginContainer
signal sig_finished_displaying_text
onready var label = $MarginContainer/Label
onready var timer = $LetterDisplayTimer
const MAX_WIDTH = 256 #Textbox cannot be wider than this. Indicates line break.
var text = ""
var letter_index = 0
var letter_time = 0.03
var space_time = 0.06
var punctuation_time = 0.2
# ---- FUNCTIONS ---------------------------------------------------------------
func func_display_text(text_to_display : String):
text = text_to_display
label.text = text
yield(self, "resized")
rect_min_size.x = min(self.size.x, MAX_WIDTH)
if (self.size.x > MAX_WIDTH):
print(label.autowrap_mode)
label.autowrap_mode = true
# = TextServer.AUTOWRAP_WORD
yield(self, "resized") # wait for x resize
yield(self, "resized") # wait for y resize
rect_min_size.y = self.size.y
rect_global_position.x -= self.size.x / 2
rect_global_position.x -= self.size.y + 24 #to put text a little bit above the speaker
label.text = ""
func_display_letter()
func func_display_letter():
label.text += text[letter_index]
letter_index += 1
if letter_index >= text.length():
emit_signal("sig_finished_displaying_text")
return
#If there are still characters to display:
match text[letter_index]:
"!", ".", ",", "?", ":":
timer.start(punctuation_time)
" ":
timer.start(space_time)
_:
timer.start(letter_time)
# ---- SIGNALS ---------------------------------------------------------------
func _on_LetterDisplayTimer_timeout():
func_display_letter()
#------------------------------------------ code -------------------
#DialogManager
extends Node
onready var text_box_scene = preload("res://scenes/ui/textbox/textbox.tscn")
#var dialog_text : Array[String] = []
var dialog_lines : Array = []
var current_line_index = 0
var text_box
var text_box_position : Vector2
var is_dialog_active = false
var can_advance_line = false
# --- FUNCTIONS ---------------------------------------------------------------
func func_start_dialog(position : Vector2, lines : Array):
if is_dialog_active:
return
dialog_lines = lines
text_box_position = position
func_show_text_box()
is_dialog_active = true
func func_show_text_box():
text_box = text_box_scene.instantiate()
text_box.finished_displaying.connect("_on_textbox_finished_displaying")
get_tree().root.add_child(text_box)
text_box.Position2D = text_box_position
text_box.display_text(dialog_lines[current_line_index])
can_advance_line = false
func func_on_textbox_finished_displaying():
can_advance_line = true
func func_unhandled_input(event):
if (
event.is_action_pressed("ui_click") &&
is_dialog_active &&
can_advance_line == true
):
text_box.queue_free()
current_line_index += 1
if (current_line_index >= dialog_lines.size()):
is_dialog_active = false
current_line_index = 0
return
func_show_text_box()
#------------------------------------------ code -------------------
#My Main from which I want to try seeing the textbox, no other controls
extends Node
const lines : Array = [
"Hello World!",
"Lorem ipsum odor et deo.",
"This is test dialogue."
]
# Called when the node enters the scene tree for the first time.
func _ready():
DialogManager.func_start_dialog(self.position * 3, lines)
Hello, I encounter this error and I can't find why it does this : At :6.50, timer.start(punctuation_time), timer.start(space_time) and timer.start(letter_time) appear red and the error said:
""Error at (44, 17): Identifier ''timer" not declared in the current scope"
I'm using Godot 4
Thank you!
Make sure you get the reference to the timer node at the top of the script with the line @onready var timer =$... putting the name of the timer node in your scene after the $.
@@DashNothing Omg! That's it :D Thank you for the video and thank you for the fast answer! Have a nice day/evening!
Hello, love the tutorials. I get en error on line 20 on textbox file. "Invalid set index 'text' (on base: 'Nil') with value of type 'String'.". I type everything as in video but it seems that when I use label.text I get nothing.
The label var is set as @onready so there shouldnt be a problem
Make sure that the path to the label after $ in the @onready line is correct. Seems like Godot couldn't find your label at the path you provided and instead returns null.
@@DashNothing Yes, I tripple checked that in the first place. I even right clicked on the Label and copied its path to ensure that I don't make any typos. :/ Thats why I wrote the comment
@@kacpereee8181 You can check what's stored in var label by writing print(label) in the _ready() func to see if it was fetched. If it is in there, but it's null when you try to assign text, it could be that the variable is changed somewhere in the code.
Hello! really enjoyed the videos for the dialog and interact systems! I am running into an issue though. I have a test guy to interact with, he has line size of 4, and after i talk to him, after the second time I advance_dialog, it breaks and wont let me continue. I am not sure why this is happening. my dialog_lines.size() is 4 and my current line index is 2 when it happens. Also the text box changes location after I advance after the second box. Super confused and unsure on why this is happening and the solution. Been going through all the videos in regards to the dialog system and can't seem to find a solution to this... Any help would be awesome!
How long is the third line, then one that doesn't get displayed? There is a problem with short lines because the two resized signals of the textbox don't get called.
@@DashNothing third line is longer than the second line, the third textbox does show up but bugs out and changes location of previous textbox. But then I am unable to continue.
@@MistForgeStudios I haven't encountered that so far. Are there any special characters in the third one? What if you switch the third and second line? Does it get displayed then?
@@DashNothing Out of some strange trial and error, I commented out the line 25 - await resized, and now it works. I have no idea why that would make it work or why that would cause it to bug out. But yeah. it now works. weird. Also, thank you for the quick responses! :)
why cant I find TextBox but only TextEdit and it doesnt work if i use TextEdit
TextBox isn't a node right out of the box in Godot, instead you can use a Label node with a NinePatchRect node to make your own text box, just like in the video :)
Thanks now it works, great tutorial!@@DashNothing
Hey great Video!
I followed along your Tutorial and managed to display the First Message, but the second message just pops up with the next text in a slightly different location and the scale from the previous text and doesn't want to advance further.
I printed in the _unhandled_input(event)
can_advance_line and is_dialog_active
and can_advance_line seems to be at false and I don't understand why
I noticed I get the same thing happening if the line is only one letter long. Is this the case for your second line?
@@DashNothingIt was just one Test Word but after changing it to a whole sentence it works just perfectly now!
@@zeroxxero5651 I found that changing the DialogManager's show_text_box function so can_advance_line is above text_box.display_text() line fixes this issue. So with that you can also have one letter lines (for example sometimes you'd like to just show "!").
@@DashNothingyou're a hero! everything works fine now! thank you for your fast responses
Nicely done. The only thing i would think of to improve it is to generalize the text timer/label(make it his own scene), so you can have coherent text behaviors between different types of displaying text. I have played a lot of games, where it was so annoying that different types of text ran with different speeds/behaviors...
Great idea. That would also allow adjusting text speed in the settings.
My solo npc dialogue its working fine but when i add another npc, both of them the dialogue are not working and not showing like pop up then disappear immediately 😞
Sorry my for grammar 😅
This happens because there is no check to see which npc is closer to the player before starting the dialogue, so both start it at the same time. You need to implement some sort of interaction system to handle interactions by distance to the player. Or you can check out my interaction system which works well with this dialogue system here ruclips.net/video/ajCraxGAeYU/видео.htmlsi=uiRgGisX6dkcqGpr
Hello, so I have been messing around with ur script for a few days, and I have a question: Do u have any idea how I could make a animation on resize? I have no idea how to do it, already tried a lot of things, none of them seem to work...
If possible I would like to contact u trough discord.
I don't have a discord yet, but a popup animation for the text box is one of the things shown in the follow up video here: ruclips.net/video/QvkGnDNbLaQ/видео.htmlsi=JBMBSTvq2bDy8vsW
@@DashNothing I know how to make a popup animation, my issue is: Imagine that one npc have 2 lines of a dialogue, right? When I want to change from one to another, I dont want to make another popup instead, I want to resize with an animation the current bubble
@@DashNothing and ty for the answer btw
Its a really neat tutorial. Do you know how this can be implemented for 3d games? Thanks
To display the text box in 3D you could use the Sprite3D node and assign it a viewport texture to render a viewport with the UI. Here's an example for Godot 3.5, but the general method is the same for Godot 4: godotengine.org/asset-library/asset/127
hey man good video, also followed your interaction system video and it worked flawlessly. but for this one I've had a small issue. the dialog works fine however if I want to add another line of text then the first line runs fine but when i advance dialog it crashes the game (not godot just the game) and gives the following error:
"Attempt to call function 'queue_free' in base 'previously freed' on a null instance."
and it points to the "text_box.queue_free()" line in the DialogManager script. Any idea how to fix this? Thanks!
@@TheMxxji For each line of text a new text box is created. Your error suggests you somehow tried to use the same textbox twice. There is an example of using the dialog manager for multiple lines in the video, where I create the NPC. Passing in the array of strings eorks.
Godot?! LIKE ACE ATTORNEY?!
Not quite haha
Heh, even I was thinking of him when I first heard of Godot.. turns out the creators named the program kind of as a reference to him
could you explain it to someone who never played ace attorney? what is the reference here?
@@fu_gamesit's a character from ace attorney
No like what ill godot o your mum if u dont do your homework
Hello! I'm trying to make your dialogue but when I start it, the only letter that display is the first one. I typed "text" and it wrote "t". Can someone help me pls?
Is the timer getting set after every letter? Its timeout callback is what makes new letters appear.
@@DashNothing I fixed it but if I press space after the end of the dialogue it keeps looping. Why?
@@legoreptyle1048 It could be because the same key is for starting a dialog and advancing lines. In the dialog manager you can call accept_ecent in dialog manager's _input() function to eat the input. Let me know if this helps.
Hi, a very cool video. only one thing I didn't understand is how to make the interact button appear when approaching a character? I don't understand where it is located in the code. and is there any other way to make the main character unable to move during the dialogue? so that he just stood in place, and at the end of the dialogue, the control appeared again?
it's a different video ruclips.net/video/ajCraxGAeYU/видео.html
The interaction is from this video: ruclips.net/video/ajCraxGAeYU/видео.htmlsi=payF90S3z1uuS9xS
And stopping the player is in the textbox followup: ruclips.net/video/QvkGnDNbLaQ/видео.htmlsi=izBc9pxv88t2qBEw
@@DashNothing спасибо, обязательно попробую 👍
Awesome tutorial but I have a question. I did type in both the codes being the textbox and the dialogue manager but now I'm stuck. I know you made a practice dude to print it out but I'm trying to make it so that the textbox immediately starts when the level loads so I tried to do a "func _ready():" but after that I'm completely lost. I placed the code in the textbox itself as well and that also doesn't seem to work.
Don't change the textbox or the dialog manager scripts - they work as is. Make a new scene that you want to be "talking" at the start of the game, define some lines in its script, and in the _ready() func call DialogManager.start_dialog, as shown for the NPC example in the video.
Thanks man, definitely deserving of a sub dude.
@@DashNothing Ok I set it up in a different project, did everything in the video and here's the thing. I have a character2d which has some vanilla movement code with the "const lines: Array[String] = ["hey wanna spar",]" at the top and the "func _ready():" beneath with the only line of code being "DialogManager.start_dialog" but its still not doing anything. am I supposed to place the textbox in the world itself because when I do that I get the box but no text. sorry if this is a hassle to read through because I've tried everything to get it to work and nothing has shown me progress yet.
Why is it "global_position.x -= size.x / 2" and not "global_position.x = size.x / 2". I'm not sure I understand why its "-=". Thanks!
We are taking away half of the width of the textbox from its global position to center it. If you didn't have that - there (and feel free to try this in Godot) you would just be setting the textbox's position coordinate to half of its width. This makes no sense and your textbox would be somewhere in the upper left corner of the screen.
@@DashNothing Ohhhhh! Thanks so much for the explanation and the great tutorial!
Hello thanks for the tutorial, that was really good. But do you know how to put it in a 3d scene?
@@Technobote You'd have to use a quad mesh and a viewport texture to render the 2D elements (textbox) onto it.
Thanks
At the end, you show a script of how to have a reference to DialogManager. But I don't know how to do the @onready for DialogManager correctly. How did you write it?
In the beginning I went to Project settings -> Autoload and loaded the DialogManager.gd script.
This makes it available in all scripts in your project, so you don't need to load it with @onready, just call DialogManager from anywhere. You'll know you set it up correctly if Godot gives you autocomplete suggestion for DialogManager when you start typing.
Hope that helps!
@@vindhyaganti9738 You can see the whole DialogManager script in the video. My commenz above didn't include a link, but RUclips recognized the words DialogManager(dot)gd as one. Sorry for the confusion :)
@@DashNothing Thank you so much! Just finished the tutorial, but when I try to add the "DialogManager.start_dialog(global_position, lines)" to my player script, I get the error that "lines" is not declared in the current scope. How do I resolve this?
@@vindhyaganti9738 Make sure your var lines is declared at the top of the script, along with your other variables. This way all functions will have access to lines. If you declared lines inside a function, other functions will not be able to use it
I got the textbox to work but now I want it to spawn under the player when I enable it, is there any way of doing this. Until than Imma just keep trying till it works.
im very new to all of this, how exactly do i make this npc and add the text to it and activate it...
When I go to test it I keep getting the error "Invalid get index '47' (on base:'String')", not sure where I went wrong. Any help would be appreciated. I checked timer to make sure it was set to one shot, and the code was written correctly (I think) as suggested to someone else experiencing this issue. However, it is still crashing with that error message.
Well it seems that your line has 47 characters, and the code tries to get the 48th character which doesn't exist. Func _display_letter() increases this index, and it should never reach a value larger than the string length because of the if letter_index >= text.length(), which is followed by a return statement. Can you post this part of your code here?
@@DashNothing
func _display_letter():
label.text += text[letter_index]
letter_index += 1
if letter_index >= text.length():
finished_displaying.emit()
@@jasonozolins8781 So you're missing a return after finished_displaying.emit(). Check the vid at 6:12
@@DashNothing Thanks so much! That fixed it. I appreciate the help.