To try everything Brilliant has to offer-free-for a full 30 days, visit brilliant.org/JaceVarlet . The first 200 of you will get 20% off Brilliant’s annual premium subscription.
Dialogue history is such a must feature for any game that has a lot of dialogue and has puzzles/interactions based on those past dialogues. I wish my 90s adventure games had it xD
Really cool video! As a programmer/CS student who had only very little contact with programming games (and even less with localization), this video had really nice pacing and depth. It was interesting, well structured and (for me) very understandable, both why and how you did things - even without knowing anything about the engine. I of course can't speak for people who don't have any knowledge about programming, but I feel like the different levels of explaining, showing demos, file structures etc. really had something for everyone to understand without it feeling either repetitive or too complex. So really outstanding work on that video! Also happy to see you getting some sponsorship and looking forward to more videos like this one!
I've always wondered how localization was generally implemented. Definitely interesting to get a peek under the hood of some popular localization components and a walkthrough. Thanks Jace, helps a POT.
it's very cool that you've got the dialog system working with most of the features you wanted :) thanks for talking us through it i worry a little that the code for showing dialog is separated from where you're writing dialog. and is that gonna be hard to manage in the future when there's a lot more of it? i dunno. i've been trying to build a dialog system for a fair while, and i can't get it straight in my head how things should best be organised. your solution is at least easy to understand!
The decision to separate the dialog data and the dialog functionality was a very deliberate one as I don't really want dialog in my code. I also considered a system that could just output the dialog as they are ordered in the container which would mean I wouldn't need to look up and push indices. The downside with that is that not all dialog is sequential. However, I did create a PUSH_DIALOG_RANGE function (can't remember if i briefly mentioned it in the video or not) which can easily push a range of uninterrupted dialog that was ordered in the container. As for what will happen when there is more dialog: that is where the containers come in! Since the containers will only have a very small subset of dialog that I will be working with, it makes it really easy to find the relevant dialog and implement it.
A RUclips sponsorship and a Twitch sponsorship all in one week! Congrats Jace, that's huge! Fascinating video as well, thanks for the insight into how the game works.
Great video and congrats on the sponsorship! I realized early on when writing a game that dialog gets out of control pretty quickly especially with many branches and conditions. I ended up writing my own dialog design app (Chat Mapper) but then realized that this would be a full time job by itself and got burnt out. 😅 It’s really interesting to see your thought process and I will be following your progress! Also a huge fan of C# and I try to use it on projects whenever I can.
Thank you. This was extremely interesting and very useful in the conceptual and, uuum, ?system analysis/design? aspects. I am not a professional programmer, just a self-teaching hobbyist so I found your coverage of, and suggestions about, the broader aspects (even things like getting the localisation in place up front) very helpful. Thanks again.
I love the insider knowledge on your takes and reasoning behind what you're doing with your game. I've seen how difficult translations can be from fans trying to translate games and it's taken several years. Glad to see this being thought of so early. Also, happy to see the sponsor, congrats :)
Excellent work with this! It's great that you put it in a plugin, having the gui interfaces added in to the Godot editor is perfect for the tool dev process that frees you to be creative ❤
I love the condensed overview of your Twitch stream progress. Unfortunately I can't make some of your streams and fall behind on the VODs so it's nice to get a summary or check-in. I hope to be able to make my own plugins as well one day, so this video has been very inspirational! I'm currently working on my own game and I have been terrified of the dialogue process since I'm not the best programmer and I'm also new to game development, animation, art, etc. The project is turning out to be a monumental task, but I've done a lot of learning and it's getting smoother each day. I've recently figured out exactly how I want to do my animations and kind of settled on an art style as well. I think it's finally time to tackle dialogue...
This was interesting and engaging while also being fairly easy to understand. I think you did a great job explaining what you've been working on and how it works. I do have some rudimentary understanding of C++, but I wouldn't call myself a programmer at all.
very interesting! three points come to mind: since you showed an PO editor - will the translators get to see the portrait? for example if you choose irony/jesting portraits maybe that context gets lost to them unless you describe it in more words. second point: how long did it take to setup? feels like a 200h task. i'm constantly underestimating the stuff i want to implement so would be curious. and last: maybe you also need a system for non-modal dialogs? e.g. vendors screaming without stopping your movement / chase sequences / etc
First point: No, actually! And this is a great point and a big oversight on my part - thank you! It would be great to embed some information regarding that. Perhaps I can hook the portrait/emote name into the translator comment that might help a bunch. Second point: The system itself took a couple weeks worth of work to design and set up but i was also learning the engine at the same time so a lot of time was spent lost on that. The plugin was about a week or so to learn and implement. Third point: non-modal is definitely something that is also on the to do list and i also forgot to mention in the video. I'm still undecided if I want all dialog to be in a big, traditional dialog box or floating over people's heads. But if I go with the traditional box, I would still like some overhead stuff for emotes or short phrases that don't interrupt the game flow. Thanks for the feedback and also great questions!
I would definitely suggest giving your dialogue entries names/identifiers, rather than referring to them by number. This decouples them from "whatever order you happened to create them in" and would allow you to rearrange them without needing to worry about indexes!
I considered this too but I feel that it just moves the goal posts around. Right now the indices don't *have* to be in order so re-ordering isn't completely necessary and can handle last minute additions by just appending them at the end of the container but add them earlier in the code. For blocks of dialog (not interrupted by pauses or actions) I can use PUSH_DIALOG_RANGE to push a range of dialog regardless of order and so inserting new dialog and extending the range works great here. Using entry names would involve coming up with meaningful names, spelling them correctly, and if the content of the dialog changes then i might need to change the name and then change the code anyway. So names does solve the re-ordering issue, but currently I'm not seeing how it would meaningfully improve the workflow overall. Thanks for the feedback!
@@jembawls Fair enough! There are pros and cons to both. In particular my own system doesn't work too well with adding more lines of dialogue into a cutscene block - it certainly works but not as easily as yours. Your video has given me some ideas!
I guess one benefit would be to easily see the dialog contents directly in the editor, if there were meaningful names. Could be useful when you a couple of months later edit the files. Could a plugin give annotations in the editor? Show the actual content. Would be really cool.
Creating a que for a system I found is necessary sometimes. In my game I have a roll up scoring system. I had to do a bit of debugging to find out why it never counted accurately when it scores really fast. My solution was to put the scores into a que (in my case an array), and let a timer handle the displaying of those scores, using a tween and a function. That way the scoring can occur even when the roll up display is still going with a previous score.
Being able to watch these insights in the code of a skilled Game Dev, and so well explained, it's an amazing learning source for aspiring game devs, thanks a lot for sharing this!
so related to dialog history, i’d love to see some sort of way to attach info to who characters are, past notable events, etc. it’s something i often run into with long news stories with a bunch of people, remembering who tf jones is after no mention for 8 paragraphs.
On the accessibility part with bigger font sizes, something I've not seen done in games that have heavy dialog is TTS for dialog menus. There's voices now for all kinds of PDA type smart speakers, etc., and we're no longer limited to Microsoft Sam-era TTS. If you can't find a free TTS voice pack, maybe crowdfund it to pay a VA to provide a sampling that you can use to train a voice model for your game. Or just have them read out the lines I guess and extend the reusable dialog box to accept a sound string. edit: oof, the editor UI gonna need some work. Depending on the size of your game scope, I think you'll have a tedious time with the dialog later. :) edit 2: As a programmer, your dialog should probably dynamically load the scene featuring the dialog and populate it from a json or other type of file. Something like, bool Dialog.Show(string title, string text, Character source) where "title" and "text" can be key lookups in your localization, and "source" is a simple info struct containing character state.
If possible, it would be good if you could have the plugin generate a file for you that contains the container names as constant strings. Then you could use those in your code and have less worries about misspelling something or issues from renaming things. Magic strings are more descriptive than magic numbers but it's still nice to have the compiler check that kind of stuff for you.
If you want to improve the system further, have you considered using signals? You could simply have one dialog_trigger signal with an attached string parameter, and then you could easily set things up by just writing the string id of an event, like "walked_behind_counter", in your dialog plugin, and then sending the same string id in your code, without having to worry about hardcoding the specific dialogue location You could also use something like an enum but this is a bit more flexible, so e.g. you can configure the dialogue first before adding the actual triggering code You could also have a dialog_advanced signal that attaches the data of the previous dialogue, which again you could then just configure all that entirely within the plugin
I noticed that you are doing two separate dialogs for the multi choice options depending on whether the multiple choice includes the amongus or not. That means there are two separate translations for that multiple choice that are duplicated. If there are a lot of places where the actions of the player might affect the number of choices to a dialog but not the dialog itself, then all the duplication might cause issues in the future.
This is a really nice system! The only difference, honestly, between what you made and what a "professional" plugin would look like is probably the actual UI for adding dialogue, but you'll do well with what you've got. Honestly the only change I would make is making the dialogue in a container and the multiple choice options have string IDs instead of numbers, since the performance implications are negligible but it basically forces you to write in the code which dialogue option you meant to use. Comments are nice, but forcing you to write what you would put in a comment in the code is nicer.
Thanks! Glad you like it! As for the string IDs: The short answer is I don’t think I like the workflow of coming up with string IDs every time I create or meaningfully edit a dialog string is appealing to me. It’s true that having the string ID would add more context to the code, but the code is already filled with context (dialog container, function/event/cutscene name, comments) so I’m not sure if the trade away from indices is worth it. But my mind is open, and I’ll keep it in consideration as I continue to work on the game in case i can see more value in it. Thanks for the comment!
Dunno how much experience you have with l10n, so feel free to ignore if you're good there. You mentioned you have to work on the portion that handles wildcard substitution - whatever system CS uses for this, if possible use named substitution rather than positional - this allows translations to swap the order of wildcards if needed by the language. I while ago I had to go through my code-base (Python) and change all of my translated strings from positional (ex: 'Put the %s in the %s' vs 'Put the {item_name} in the {container_name}').
I think tracking dialoge by Npc instead of location for your game might make finding things even easier! Maybe then add a "scene" subfolder or similar to really be able to tweak and find everything easier should you have a couple thousend lines of code.
This is what I had initially intended actually but very quickly realised when it came time to implement dialog that tracking only by NPC doesn't really make a lot of sense. First of all: which NPC should the chips on the shelf be allocated under? 😂 Secondly, if you have 2 NPC's talking to each other in 1 cutscene - which NPC should the dialog be under? Their respective text could of course remain under their own NPC containers, but that then means loading 2 containers and trying to find the right dialog in 2 different locations, which may be very tricky because NPC's will have different amounts of dialog and their dialog will be paced differently. I find it makes the most sense for the containers to represent ~~whatever makes sense~~. So containers can be a location, an NPC, or a cutscene - whatever makes organising and executing that text easiest.
@@jembawls You are absolutely right! I did not consider those situations! I do have to say from my experience with these kind of "document" Organisations having a good system set-up at the start will save a lot of pain points down the line! Maybe you could introduce tags for each NPC to quickly find specific ones. That way you could even tag a single dialogue with multiple tags such as NPC 1, npc 2 and active quest / situation. That way saving them in folders based on location gives you two search angles so to speak. But in the end it needs to work for you so as long as it does whatever works works! Really like how transparent you are making the whole development process! Keep up the good work :)
Not sure if you have plans for it yet, but I think animations (or at least different portraits for different scenarios per NPC) in the portraits during dialog would be nifty difty. While I haven't done any programming in a number of years, I do remember quite a bit of it. I read much better than I write. I like the programming content in the videos. For the dialog timing, I would try to add a new toggle to the dialog builder box: "Requires user interaction". Enabled would be the standard click to go system, disabled would enable a timer box in ms that you can use to program animation speed. Each dialog would take a bit of manual jiggery pokery, from a short message to a long one; as the total message length will affect the on screen time via the timer. Maybe someone else has a better idea for controlling the text animation speed.
You could additionally implement an feature for dialogue between NPCs and NPCs talking to themselves to make the world seem more alive (or to convey information for quests or lore etc.pp.)
Nice work and thanks for sharing this. I do wonder about the interaction classes and conditions for different dialogue. You only had one if statement and a single Boolean value in this first example. It would work to have however many conditions in the code in a bunch of if statements, maybe even nested or from a function call. But could that get messy to maintain? Have you already thought of how more complicated conditional dialogue might be coded?
You can eliminate the need to make the word coin plural by associating your coins with a symbol ( like ¢ or $). It's just less information you have to process.
I'm curious. If you change a file that already has translations, how does the system handle that? Like say you change the dialog to add an extra sentence in one branch. Did it overwrite the translations with an empty translation file? Can you tell an entry that's changed vs one that hasn't?
In this case I can regenerate the .POT file and distribute that to translators. They can then update their .PO files using the .POT file and continue translating. As far as I can see, the edited strings get updated via their context and flagged as needing review in Poedit.
I foresee a problem with your localization setup. It looks like you are using the default English text as the localization key. This means that if you change any default text, such as to fix spelling, grammar or make something bold, you are changing the lookup key. Now all languages need to be updated with new translations even though they most likely will end up with the same text as before. In localizations systems I have worked with, the lookup key and the default text are always two different strings. The lookup key would only get changed in cases where you intend for it to go through translation again. This way you can decide case by case whether the change warrants new translations. I think you will find that more often than not, changes end up being minor and should not impact translations. This is especially true later in the development cycle when bug fixing and polishing. You don't want to end up in a situation where minor changes to default text have a major cost associated with them.
Hey! I was wondering what you meant by plural support. Does that have to do with how different languages handle plurals differently or something else entirely?
So plural support is to swap out translations based on a number you provide. So for example: if j have a wildcard string “I have {quantity} banana”. If we have 1, we would show “I have 1 banana” but if I have 2 it would show “I have 2 bananas”. gettext (according to my research) can also has support for language specific pluralisation. Some languages (Japanese, for example) don’t have plurals, and so the Japanese translation file won’t have required entries for plurals. But there are other languages that have multiple forms of plurals depending on the quantity. Hope this helps!
@@jembawls thank you! Yes, different languages will require different number of strings when there are placeholders standing in for numbers from (typically 1 to 4). Hungarian for example only needs one because although it does have plural forms, it uses singular noun for all "number" + "noun" structures. It's great that your system will be able to handle more complicated scenarios as well as context help for translators. I have seen it happen that even though the system is ok to handle plurals, the info on which string covers which cases is too general or doesn't go all the way to the people who work on the strings. So if the context strings can help with that, this issue won't happen here.
Hey Jace, how are you finding the C# support in Godot? I use C# in my day job but decided to use GDScript as it looked like C# support was not super polished yet and probably fewer resources online for it
I can see the need to create a custom dialog box, that uses a richtext label. I don't remember using richtext except in Godot 3.x and it was kind of jank. I am not sure if that's true anymore, but I guess I'll find out when I need that type of label again. I am not sure how you are designing your dialog system with other languages in mind. One thing that does come to mind is that some characters are double wide, like for Chinese or other symbolic languages. I assume just make the sizing dynamic, and if the dialog doubles in size, you have made room for it, or cut the dialog into smaller pieces to be displayed. Another issue: I only speak English and bad English. How could I possibly test if my system properly formats and displays all languages? Edit: IMO, I think translations should be done by the OS. There are exeptions for text in images, but otherwise, why do we need special code for multiple languages?
"there are other things in my code that make the context obvious" please do a dev stream some time after release where you read your code and see if you can figure out how it works? 😅🤔
Good video. I managed with Godot 4.2.2 to localize 2 languages. In options when you click on Spanish or English you put one language or the other. The problem comes when I close the game. It doesn't save the language I selected. could you help me?
Why not used named constants for dialog identifiers instead of auto incrementing numerical IDs? If the IDs change (due to an insert instead of an append to the list), then all of the code needs to change. If they are named constants, those have e very low chance of changing.
The IDs being numerical come with other advantages (such as pushing ranges) while at the same time not actually needing to be ordered (unless pushing ranges). They also don’t require coming up with a named constant every time I create or insert a new dialog string, or when meaningfully editing a string such that it’s constant is no longer contextually accurate. The nature of using the containers also means that unordered dialog (or re-ordering dialog) in an event is not very hard to do and won’t have knock on effects elsewhere so it’s not that error prone - EXCEPT when I opt to use location-based containers (where different events may share the same container) in which case I’ll need to be careful, or just split up the events into their own containers in a sub-folder. I can maybe see some value for potentially adding an optional tag value to the dialog strings that I can look up and increment from (or something like “add dialog up to a tag”) but I won’t embark on that journey unless I really feel the workflow calling for it. But a few people have suggested named constants so I’ll keep it in mind as I work with the system and consider it as a solution if (WHEN! 😂) I run into issues. Thanks for your comment!
The plugin is DEEPLY tied to the rest of my system which I don't think is suitable as a general-purpose dialog/localization system, so I don't think I will unfortunately. Maybe as I work on the game and develop more features for system it might become more worthy of generalising and distributing.
Did you choose to keep using C# because it's the industry standard and you're more comfortable with it or do you dislike GDScript? (or both?) As a web services dev (backend and frontend) I tend to prefer GDScript, but I can see it's more because I'm not a game developer
Congrats on your first sponsorship! Have you considered other video platforms like Nebula? I would love to actually pay for your videos and support you that way. I also think you videos would fit quite well there.
Editor here, I'm sorry you had that reaction :( I even put an audio filter on the background music so that the higher-pitched elements weren't as loud. I'll consider music with less high-pitched melodies in the future. I find it annoying as well, so I try to make it blend as well as possible.
To try everything Brilliant has to offer-free-for a full 30 days, visit brilliant.org/JaceVarlet . The first 200 of you will get 20% off Brilliant’s annual premium subscription.
Dialogue history is such a must feature for any game that has a lot of dialogue and has puzzles/interactions based on those past dialogues. I wish my 90s adventure games had it xD
Really cool video! As a programmer/CS student who had only very little contact with programming games (and even less with localization), this video had really nice pacing and depth. It was interesting, well structured and (for me) very understandable, both why and how you did things - even without knowing anything about the engine. I of course can't speak for people who don't have any knowledge about programming, but I feel like the different levels of explaining, showing demos, file structures etc. really had something for everyone to understand without it feeling either repetitive or too complex. So really outstanding work on that video! Also happy to see you getting some sponsorship and looking forward to more videos like this one!
thank you! I'm glad you enjoyed the depth and pacing. Worked pretty hard on that!
I've always wondered how localization was generally implemented. Definitely interesting to get a peek under the hood of some popular localization components and a walkthrough. Thanks Jace, helps a POT.
Smart system. Awesome! And congratulations for your first sponsorship.
that was a very smooth sponser plug at 17:00, I think it works so well cos its a subject/topic you would bring up naturally even without a sponsor
it's very cool that you've got the dialog system working with most of the features you wanted :) thanks for talking us through it
i worry a little that the code for showing dialog is separated from where you're writing dialog. and is that gonna be hard to manage in the future when there's a lot more of it?
i dunno. i've been trying to build a dialog system for a fair while, and i can't get it straight in my head how things should best be organised. your solution is at least easy to understand!
The decision to separate the dialog data and the dialog functionality was a very deliberate one as I don't really want dialog in my code. I also considered a system that could just output the dialog as they are ordered in the container which would mean I wouldn't need to look up and push indices. The downside with that is that not all dialog is sequential. However, I did create a PUSH_DIALOG_RANGE function (can't remember if i briefly mentioned it in the video or not) which can easily push a range of uninterrupted dialog that was ordered in the container.
As for what will happen when there is more dialog: that is where the containers come in! Since the containers will only have a very small subset of dialog that I will be working with, it makes it really easy to find the relevant dialog and implement it.
A RUclips sponsorship and a Twitch sponsorship all in one week! Congrats Jace, that's huge! Fascinating video as well, thanks for the insight into how the game works.
Great video and congrats on the sponsorship! I realized early on when writing a game that dialog gets out of control pretty quickly especially with many branches and conditions. I ended up writing my own dialog design app (Chat Mapper) but then realized that this would be a full time job by itself and got burnt out. 😅 It’s really interesting to see your thought process and I will be following your progress! Also a huge fan of C# and I try to use it on projects whenever I can.
Really enjoy your content, keep up the good work! Stumbled upon this video in search for inspiration for how to organize my own dialog system.
Really appreciating all the time and attention you put into these videos and explaining everything!
Thank you. This was extremely interesting and very useful in the conceptual and, uuum, ?system analysis/design? aspects. I am not a professional programmer, just a self-teaching hobbyist so I found your coverage of, and suggestions about, the broader aspects (even things like getting the localisation in place up front) very helpful. Thanks again.
Undertale is really good example! Sans slowly losing with you is so eerie
I love the insider knowledge on your takes and reasoning behind what you're doing with your game. I've seen how difficult translations can be from fans trying to translate games and it's taken several years. Glad to see this being thought of so early.
Also, happy to see the sponsor, congrats :)
Excellent work with this! It's great that you put it in a plugin, having the gui interfaces added in to the Godot editor is perfect for the tool dev process that frees you to be creative ❤
I love the condensed overview of your Twitch stream progress. Unfortunately I can't make some of your streams and fall behind on the VODs so it's nice to get a summary or check-in. I hope to be able to make my own plugins as well one day, so this video has been very inspirational!
I'm currently working on my own game and I have been terrified of the dialogue process since I'm not the best programmer and I'm also new to game development, animation, art, etc. The project is turning out to be a monumental task, but I've done a lot of learning and it's getting smoother each day. I've recently figured out exactly how I want to do my animations and kind of settled on an art style as well. I think it's finally time to tackle dialogue...
Best of luck! I'm rooting for you!
This was interesting and engaging while also being fairly easy to understand. I think you did a great job explaining what you've been working on and how it works.
I do have some rudimentary understanding of C++, but I wouldn't call myself a programmer at all.
very interesting!
three points come to mind:
since you showed an PO editor - will the translators get to see the portrait? for example if you choose irony/jesting portraits maybe that context gets lost to them unless you describe it in more words.
second point: how long did it take to setup? feels like a 200h task. i'm constantly underestimating the stuff i want to implement so would be curious.
and last: maybe you also need a system for non-modal dialogs? e.g. vendors screaming without stopping your movement / chase sequences / etc
First point: No, actually! And this is a great point and a big oversight on my part - thank you! It would be great to embed some information regarding that. Perhaps I can hook the portrait/emote name into the translator comment that might help a bunch.
Second point: The system itself took a couple weeks worth of work to design and set up but i was also learning the engine at the same time so a lot of time was spent lost on that. The plugin was about a week or so to learn and implement.
Third point: non-modal is definitely something that is also on the to do list and i also forgot to mention in the video. I'm still undecided if I want all dialog to be in a big, traditional dialog box or floating over people's heads. But if I go with the traditional box, I would still like some overhead stuff for emotes or short phrases that don't interrupt the game flow.
Thanks for the feedback and also great questions!
I would definitely suggest giving your dialogue entries names/identifiers, rather than referring to them by number. This decouples them from "whatever order you happened to create them in" and would allow you to rearrange them without needing to worry about indexes!
I considered this too but I feel that it just moves the goal posts around. Right now the indices don't *have* to be in order so re-ordering isn't completely necessary and can handle last minute additions by just appending them at the end of the container but add them earlier in the code. For blocks of dialog (not interrupted by pauses or actions) I can use PUSH_DIALOG_RANGE to push a range of dialog regardless of order and so inserting new dialog and extending the range works great here.
Using entry names would involve coming up with meaningful names, spelling them correctly, and if the content of the dialog changes then i might need to change the name and then change the code anyway. So names does solve the re-ordering issue, but currently I'm not seeing how it would meaningfully improve the workflow overall.
Thanks for the feedback!
@@jembawls Fair enough! There are pros and cons to both. In particular my own system doesn't work too well with adding more lines of dialogue into a cutscene block - it certainly works but not as easily as yours. Your video has given me some ideas!
I guess one benefit would be to easily see the dialog contents directly in the editor, if there were meaningful names. Could be useful when you a couple of months later edit the files.
Could a plugin give annotations in the editor? Show the actual content. Would be really cool.
Creating a que for a system I found is necessary sometimes. In my game I have a roll up scoring system. I had to do a bit of debugging to find out why it never counted accurately when it scores really fast. My solution was to put the scores into a que (in my case an array), and let a timer handle the displaying of those scores, using a tween and a function. That way the scoring can occur even when the roll up display is still going with a previous score.
WOOOOO GAME DEV LOG
Well done on the spon, upwards and onwards Jace :)
Being able to watch these insights in the code of a skilled Game Dev, and so well explained, it's an amazing learning source for aspiring game devs, thanks a lot for sharing this!
Great video! Localization is much harder to do as an afterthought so building it into the framework early on is smart.
so related to dialog history, i’d love to see some sort of way to attach info to who characters are, past notable events, etc. it’s something i often run into with long news stories with a bunch of people, remembering who tf jones is after no mention for 8 paragraphs.
On the accessibility part with bigger font sizes, something I've not seen done in games that have heavy dialog is TTS for dialog menus. There's voices now for all kinds of PDA type smart speakers, etc., and we're no longer limited to Microsoft Sam-era TTS.
If you can't find a free TTS voice pack, maybe crowdfund it to pay a VA to provide a sampling that you can use to train a voice model for your game. Or just have them read out the lines I guess and extend the reusable dialog box to accept a sound string.
edit: oof, the editor UI gonna need some work. Depending on the size of your game scope, I think you'll have a tedious time with the dialog later. :)
edit 2: As a programmer, your dialog should probably dynamically load the scene featuring the dialog and populate it from a json or other type of file. Something like,
bool Dialog.Show(string title, string text, Character source) where "title" and "text" can be key lookups in your localization, and "source" is a simple info struct containing character state.
I liked the detail presented in the video.
If possible, it would be good if you could have the plugin generate a file for you that contains the container names as constant strings. Then you could use those in your code and have less worries about misspelling something or issues from renaming things. Magic strings are more descriptive than magic numbers but it's still nice to have the compiler check that kind of stuff for you.
If you want to improve the system further, have you considered using signals? You could simply have one dialog_trigger signal with an attached string parameter, and then you could easily set things up by just writing the string id of an event, like "walked_behind_counter", in your dialog plugin, and then sending the same string id in your code, without having to worry about hardcoding the specific dialogue location
You could also use something like an enum but this is a bit more flexible, so e.g. you can configure the dialogue first before adding the actual triggering code
You could also have a dialog_advanced signal that attaches the data of the previous dialogue, which again you could then just configure all that entirely within the plugin
I noticed that you are doing two separate dialogs for the multi choice options depending on whether the multiple choice includes the amongus or not. That means there are two separate translations for that multiple choice that are duplicated. If there are a lot of places where the actions of the player might affect the number of choices to a dialog but not the dialog itself, then all the duplication might cause issues in the future.
This is a really nice system! The only difference, honestly, between what you made and what a "professional" plugin would look like is probably the actual UI for adding dialogue, but you'll do well with what you've got.
Honestly the only change I would make is making the dialogue in a container and the multiple choice options have string IDs instead of numbers, since the performance implications are negligible but it basically forces you to write in the code which dialogue option you meant to use. Comments are nice, but forcing you to write what you would put in a comment in the code is nicer.
Thanks! Glad you like it!
As for the string IDs: The short answer is I don’t think I like the workflow of coming up with string IDs every time I create or meaningfully edit a dialog string is appealing to me. It’s true that having the string ID would add more context to the code, but the code is already filled with context (dialog container, function/event/cutscene name, comments) so I’m not sure if the trade away from indices is worth it.
But my mind is open, and I’ll keep it in consideration as I continue to work on the game in case i can see more value in it.
Thanks for the comment!
Dunno how much experience you have with l10n, so feel free to ignore if you're good there. You mentioned you have to work on the portion that handles wildcard substitution - whatever system CS uses for this, if possible use named substitution rather than positional - this allows translations to swap the order of wildcards if needed by the language. I while ago I had to go through my code-base (Python) and change all of my translated strings from positional (ex: 'Put the %s in the %s' vs 'Put the {item_name} in the {container_name}').
Indeed! The current system does allow for the wildcards to be repositioned when localized.
Congrats on the sponsor!
I think tracking dialoge by Npc instead of location for your game might make finding things even easier!
Maybe then add a "scene" subfolder or similar to really be able to tweak and find everything easier should you have a couple thousend lines of code.
This is what I had initially intended actually but very quickly realised when it came time to implement dialog that tracking only by NPC doesn't really make a lot of sense. First of all: which NPC should the chips on the shelf be allocated under? 😂
Secondly, if you have 2 NPC's talking to each other in 1 cutscene - which NPC should the dialog be under? Their respective text could of course remain under their own NPC containers, but that then means loading 2 containers and trying to find the right dialog in 2 different locations, which may be very tricky because NPC's will have different amounts of dialog and their dialog will be paced differently.
I find it makes the most sense for the containers to represent ~~whatever makes sense~~. So containers can be a location, an NPC, or a cutscene - whatever makes organising and executing that text easiest.
@@jembawls You are absolutely right! I did not consider those situations!
I do have to say from my experience with these kind of "document" Organisations having a good system set-up at the start will save a lot of pain points down the line!
Maybe you could introduce tags for each NPC to quickly find specific ones. That way you could even tag a single dialogue with multiple tags such as NPC 1, npc 2 and active quest / situation. That way saving them in folders based on location gives you two search angles so to speak.
But in the end it needs to work for you so as long as it does whatever works works!
Really like how transparent you are making the whole development process!
Keep up the good work :)
Not sure if you have plans for it yet, but I think animations (or at least different portraits for different scenarios per NPC) in the portraits during dialog would be nifty difty. While I haven't done any programming in a number of years, I do remember quite a bit of it. I read much better than I write. I like the programming content in the videos. For the dialog timing, I would try to add a new toggle to the dialog builder box: "Requires user interaction". Enabled would be the standard click to go system, disabled would enable a timer box in ms that you can use to program animation speed. Each dialog would take a bit of manual jiggery pokery, from a short message to a long one; as the total message length will affect the on screen time via the timer. Maybe someone else has a better idea for controlling the text animation speed.
I love the character's hat!!!
You could additionally implement an feature for dialogue between NPCs and NPCs talking to themselves to make the world seem more alive (or to convey information for quests or lore etc.pp.)
Add support for custom backgrounds and adjust the scrolling a bit because I feel some dialogues didn't look scrollable at first
Nice work and thanks for sharing this. I do wonder about the interaction classes and conditions for different dialogue. You only had one if statement and a single Boolean value in this first example.
It would work to have however many conditions in the code in a bunch of if statements, maybe even nested or from a function call.
But could that get messy to maintain? Have you already thought of how more complicated conditional dialogue might be coded?
You can eliminate the need to make the word coin plural by associating your coins with a symbol ( like ¢ or $). It's just less information you have to process.
🙏🏾🙏🏾🙏🏾🙏🏾 awesome video!!!
Any info on the .po/.pot file formats so that other tools and parsers could be found and/or written to work with those files?
I'm curious. If you change a file that already has translations, how does the system handle that? Like say you change the dialog to add an extra sentence in one branch. Did it overwrite the translations with an empty translation file? Can you tell an entry that's changed vs one that hasn't?
In this case I can regenerate the .POT file and distribute that to translators. They can then update their .PO files using the .POT file and continue translating. As far as I can see, the edited strings get updated via their context and flagged as needing review in Poedit.
have you played CrossCode? your dialog system reminds me a lot of theirs, which is a good thing!
I love CrossCode!
I foresee a problem with your localization setup. It looks like you are using the default English text as the localization key. This means that if you change any default text, such as to fix spelling, grammar or make something bold, you are changing the lookup key. Now all languages need to be updated with new translations even though they most likely will end up with the same text as before. In localizations systems I have worked with, the lookup key and the default text are always two different strings. The lookup key would only get changed in cases where you intend for it to go through translation again. This way you can decide case by case whether the change warrants new translations. I think you will find that more often than not, changes end up being minor and should not impact translations. This is especially true later in the development cycle when bug fixing and polishing.
You don't want to end up in a situation where minor changes to default text have a major cost associated with them.
Hey! I was wondering what you meant by plural support. Does that have to do with how different languages handle plurals differently or something else entirely?
So plural support is to swap out translations based on a number you provide.
So for example: if j have a wildcard string “I have {quantity} banana”. If we have 1, we would show “I have 1 banana” but if I have 2 it would show “I have 2 bananas”. gettext (according to my research) can also has support for language specific pluralisation. Some languages (Japanese, for example) don’t have plurals, and so the Japanese translation file won’t have required entries for plurals. But there are other languages that have multiple forms of plurals depending on the quantity.
Hope this helps!
@@jembawls thank you! Yes, different languages will require different number of strings when there are placeholders standing in for numbers from (typically 1 to 4). Hungarian for example only needs one because although it does have plural forms, it uses singular noun for all "number" + "noun" structures. It's great that your system will be able to handle more complicated scenarios as well as context help for translators. I have seen it happen that even though the system is ok to handle plurals, the info on which string covers which cases is too general or doesn't go all the way to the people who work on the strings. So if the context strings can help with that, this issue won't happen here.
it took 10 secs to get hooked lmao
Hmmmmmm,So many flavours of chips. I dunno which to choose!
Hey Jace, how are you finding the C# support in Godot? I use C# in my day job but decided to use GDScript as it looked like C# support was not super polished yet and probably fewer resources online for it
I can see the need to create a custom dialog box, that uses a richtext label. I don't remember using richtext except in Godot 3.x and it was kind of jank. I am not sure if that's true anymore, but I guess I'll find out when I need that type of label again. I am not sure how you are designing your dialog system with other languages in mind. One thing that does come to mind is that some characters are double wide, like for Chinese or other symbolic languages. I assume just make the sizing dynamic, and if the dialog doubles in size, you have made room for it, or cut the dialog into smaller pieces to be displayed. Another issue: I only speak English and bad English. How could I possibly test if my system properly formats and displays all languages?
Edit: IMO, I think translations should be done by the OS. There are exeptions for text in images, but otherwise, why do we need special code for multiple languages?
"there are other things in my code that make the context obvious"
please do a dev stream some time after release where you read your code and see if you can figure out how it works? 😅🤔
Good video. I managed with Godot 4.2.2 to localize 2 languages. In options when you click on Spanish or English you put one language or the other.
The problem comes when I close the game. It doesn't save the language I selected. could you help me?
Why not used named constants for dialog identifiers instead of auto incrementing numerical IDs? If the IDs change (due to an insert instead of an append to the list), then all of the code needs to change. If they are named constants, those have e very low chance of changing.
The IDs being numerical come with other advantages (such as pushing ranges) while at the same time not actually needing to be ordered (unless pushing ranges). They also don’t require coming up with a named constant every time I create or insert a new dialog string, or when meaningfully editing a string such that it’s constant is no longer contextually accurate.
The nature of using the containers also means that unordered dialog (or re-ordering dialog) in an event is not very hard to do and won’t have knock on effects elsewhere so it’s not that error prone - EXCEPT when I opt to use location-based containers (where different events may share the same container) in which case I’ll need to be careful, or just split up the events into their own containers in a sub-folder.
I can maybe see some value for potentially adding an optional tag value to the dialog strings that I can look up and increment from (or something like “add dialog up to a tag”) but I won’t embark on that journey unless I really feel the workflow calling for it.
But a few people have suggested named constants so I’ll keep it in mind as I work with the system and consider it as a solution if (WHEN! 😂) I run into issues.
Thanks for your comment!
Will you be uploading your plug-in to the Godot asset store ( or if it already there, I didn't get a chance to look yet)
The plugin is DEEPLY tied to the rest of my system which I don't think is suitable as a general-purpose dialog/localization system, so I don't think I will unfortunately. Maybe as I work on the game and develop more features for system it might become more worthy of generalising and distributing.
Where can I find the plugin?
This plugin is not publicly available. It's just my personal tool I use for my game.
Jace or Chat,
Is this plugin available, if so where at?
It is not available. It is a plugin I've custom built for my project.
Did you choose to keep using C# because it's the industry standard and you're more comfortable with it or do you dislike GDScript? (or both?)
As a web services dev (backend and frontend) I tend to prefer GDScript, but I can see it's more because I'm not a game developer
*I see the among us reference there*
Jace, if I do the uwu :3 translations for the game, would you put it in as a supported language?
Congrats on your first sponsorship! Have you considered other video platforms like Nebula? I would love to actually pay for your videos and support you that way. I also think you videos would fit quite well there.
WHY IS YOUR CODE SCREAMING ALL THE TIME? ;)
PUSH_DIALOG( "Because I'm using all caps functions help distinguish global helper functions from regular code");
hi bro
REALLY ANNOYING background sounds in the intro. I felt like there was something ringing in the next room. Quite anxiety inducing 🫨
Editor here, I'm sorry you had that reaction :( I even put an audio filter on the background music so that the higher-pitched elements weren't as loud. I'll consider music with less high-pitched melodies in the future. I find it annoying as well, so I try to make it blend as well as possible.
@@MasonzeroDigitalWorks thanks 🙂. It wasn't so bad once I realized it.