Making A Tool To Make A Game
First off, this is a continuation of the previous blogpost that talked about the dialogue system for our upcoming game Ronn For Your Life, so if you haven't already, I suggest you read that one first.
Right, so like I concluded in the previous blogpost, I needed to make a tool that would allow for easy and less error prone creation of the content files for my dialogue system. The first and primary goal of the tool would be to handle automated unique IDs for me for every "branch" of every conversation ever generated. But, as I started to make the tool itself, it became apparant that it would also be very beneficial to make it a fully realized visual suite for all my dialogue content creation needs. Before I knew it, I was in the proces of making a full program.
Before we go further, let's take a look at the sort of files I was creating by hand before, and what the primary problem was:
Okay, so let's explain what's going on here.
This is a file containing dialogue instructions for the interpreter.
The first part, "[ID_0]" is the branch identifier - before I changed it into numbers, this was a string, like the next line "ID_DISPLAY_NAME". This caused the now obvious flaws which I discussed before. I still maintained the display ID string, but for just that purpose - display. This is solely to make it more readable for the content creator, since "opengreeting" may make a lot more sense to a human than ID_0.
The next line, "npc=I am NPC" defines the text to display that is currently being spoken. "NPC" is a keyword for the interpreter, and its value is the text that is currently spoken.
Next line, "pPath=ID_1" is the ID of the next branch after this one. This means that, once the user presses a key to advance the conversation, the next branch the interpreter wil choose is whichever has the ID of 1.
"pOptions=-1" means that we have no dialogue options, and the NPC will just keep talking.
Finally, "CONTINUE=1" tells the interpreter ahead that there is more content to come.
The next branch, ID_1, has only three additional unique things; "playerX", "pPathX" and "pOptions". These contain the various dialogue options the player has, as well as the branch path for each option, and finally, one cumulative counter to tell how many total options are available.
This is the most basic structure of a dialogue file for the system. Potentially, it can contain a lot more information, such as if a conversation has wildcard attributes and/or dynamically created dialogue options, and maybe even alternative branch paths for non-existent participating NPCs.
The file you see there is created by hand, which means I typed in all the data manually. Now, keeping track of the two IDs you see is not a big deal - but you quickly get to a point where there's more than 50 or 100 IDs in one conversation, and if you end up editing, moving stuff around, deleting certain branches etc., it gets nearly impossible to keep proper track of which IDs are used and which aren't.
Enter the Dialogue Generator:
The first and most important functionality at work - I can drag in new branches, and the system will automatically assign incremental IDs. To the left side you're seeing the resource tree, which contains all branches, and to the right is the workspace where you can actually see the branches, complete with name and textfields for their values.
The system will always keep going from the highest ID, so even if I delete a branch and then keep adding more branches, the ID that was deleted will never return. This means if I created ID1, ID2 and ID3, then deleted ID2 and created another branch, it would automatically be ID4 no matter what.
I also never have to think about names - since the ID and the name are now two separate values. This means I can easily give a branch a name for my own identification purposes (which will never be seen by the player), but the system won't care, as it will only identify a branch by its unique number. This is also ideal for content creators of different languages, since they can name and rename every new and existing branch whatever they want - the system will never let you change an ID or use the same ID twice.
I can name every branch whatever I want (or not give it a name); it doesn't really matter, it's strictly for my own convenience.
But I also need to be able to add text for the NPC, and dialogue options for the player. This is as simple as entering text into the appropriate fields, as such;
It's almost the same procedure for creating dynamic dialogue options, but there's a little more to it. It's basically using a kind of wildcard within the dialogue fields. For example, if I want to make a dialogue option that only appears when I'm in state 3 (complete) for a certain quest, I would type "pOption_quest_NAMEOFQUEST_state3" followed by the actual option. So, "pOption_" indicates that it's a wildcard for a dynamic quest option, "quest_" indicates that it's based on a quest criterium, then followed by the actual name of the quest (which relates to the quest module), then followed by either "flag" or "state", and then finished by the required number. Then a space, and then the option.
The interpreter will do the rest of the work - it will look to see if the particular state or flag for that particular quest is at its requested number, and if it is, the dialogue option will show up. Otherwise it will just be ignored.
I save my work, and then, once I'm ready to link it all together, there's a visual interface for that as well. For the time being, I simply right-click a textfield - either an NPC textfield or a dialogue option textfield, which will then create a sort of dropdown list with all possible branches. Picking one of these will link the textfield from which it originated to the branch I pick, and indicate the connection with a nice visual line.
Later I would like to add another interface to make the connection, such as simply dragging from the textfield to the branch I want to make the end-connection. I would also like to have all the lines be curved in a way that makes them "dodge" any obstacles, but for now, this falls into the "nice to have" category.
Notice that nice zoom and panning going on in the workspace? <3
One thing you may notice is that there are now red exclamation marks all over the place - until a connection is made. They are present in the resource tree, as well as next to all the textfields.
This is a warning to the content creator, to tell him or her that there are unconnected branches. If you have a branch or a dialogue option that doesn't lead to anywhere, then that is most likely unintended, and the system indicates where any potential errors may occur from this. The resource tree will indicate any branch that has at least one error, while in the branch itself on the workspace you will be able to see in more detail exactly where that / those errors are. As a rule, either the NPC field leads to somewhere (when you have no dialogue options), or you have one or more dialogue options that each leads to somewhere - you would never have the branch and dialogue options lead somewhere at the same time, and the system also accounts for this and tries to remedy any human error; if you've previously linked a branch to somewhere, but then add a dialogue option which you then connect, it will automatically disconnect the previously established branch connection at the same time. Even if you overwrite this by reconnecting the branch while you have one or more dialogue options in the same branch with connections, it will warn you about this as well.
One thing that I constantly had problems with when making the files by hand was forgetting to connect certain branches or options, and when I went to test it, the dialogue would be broken because of this, and I'd have to go back to the file to look for the disconnection. This has cut down basically 100% on those errors, and this will definitely save me a lot of time in the future.
There's also an interface to add those wildcards that I discussed - when you click the little white paper sheet icon on a branch, it will present you with new textfields specifically for this purpose. Here, you can type any wildcard you want, along with its value. In the example below, I am adding the wildcard "gold" to branch ID_3 and setting it to the value 10, which would set the player's gold to 10 (note, not subtract or add, just set it to 10):
As the eagle-eyed reader will discover in the above GIF, I also add a checkmark in ID_2 and ID_3 - specifically the "Player Talking". This will basically automatically add a special wildcard to the saved file, which will tell the interpreter that this specific text is actually spoken by the protagonist instead of an NPC. This will point all EMOTION related wildcards towards the player, and in addition it will point the camera towards him or her, as well as it will generate a sort of "YOU:" or "PLAYER:" or something along those lines to indicate who is talking (where, usually, the NPCs name would be). If this checkbox isn't checked, it will default to the NPC from which the dialogue originated, but it is also possible for me to indicate via a wildcard that another NPC should be the one currently speaking. So really, I can make anyone be the current speaker through custom wildcards - the checkbox for the player is just a quick and convenient way to do this.
Finally, let's add some comfort features to make the UI a little nicer to work with. In addition to panning and zooming, how about the ability to resize the resource tree, minimize and maximize windows, as well as multiselect multiple branches to move them all around? Also, shortcut keys to close, mimimize or maximize one or all currently selected windows. Mmm! Much better already!
After all this, this is the result, which looks a lot like the picture I started off showing - only this time, it's automatically generated by the program, ensuring basically no errors, while also providing a much better and faster workspace for me and any potential future content contributors!
This file is now ready to be fed directly to the game's dialogue system interpreter, which will then automatically handle everything from there.
It will also be very quick and easy to hand the program and all the files to a translator, which would then just translate everything without having to think about IDs, player options, wildcards or any of that stuff. With the game's localization system in place, it's just a matter of changing a simple language setting, which will then make the game point directly to the appropriate version of each dialogue file. So to resume the example from the previous blogpost where you were playing in Spanish, left in the middle of a conversation, and the next day your Italian friend came by to play the game, loaded up and changed language, the conversation would now pick up exactly where you left off in your Spanish version - but in Italian.
The whole process of making this tool has taken around 2 months of on and off work - it's been hard and tedious work! In a way, it almost feels a little ridiculous when I think about the fact that this all spawned from wanting to be able to switch game language in the middle of a saved and loaded conversation, because at the end of the day, how many players are going to be doing just that? But first of all, that doesn't matter - even if no one ever ends up doing that, the functionality just needs to be there.
But more than that, the initial design was inevitably going to cause issues in other areas, so this was a good way to go. It's also resulted in this tool, which has already saved me lots of time, and will save me more time than I can even fathom in the long run - both in terms of all those errors I will avoid, but also in a generally much, much better and faster workflow for this game as well as all other future games utilizing a dialogue system with sometimes fairly complex and dynamic needs.
Furthermore, it provides a solid platform for collaborative content creators as well as modders and translators. There just so many pros, and in the end, it was definitely worth those 2 small months - I can see that already now.
So the lesson here, is that sometimes, even though it may seem overkill at first to make an entire program just for one little part of your game - it can actually make a ton of sense. Try to weigh how much time it will save you in the long run - in my case, it will save both on production time for me and other people, but in addition, it will also save a ton of time on testing, due to significantly reduced errors.