What is the best way to create a dialogue system?

I first want to make it known that I will not buy or download any assets. I want to learn how to do things myself rather than taking the easy way out.

To reiterate the title of this thread, What is the best way to create a dialogue system? I am not looking for anything fancy with a ton of customization. I just want to know the best way to structure classic JRPG dialogue.

This style generally consists of a picture for each participating character, Dialogue options that can branch out into different dialogue segments, and a sound that plays whenever a characters starts talking(usually a “gasp” or “huh” sound to react to the previous characters dialogue).
Example:
2863277--209651--download.jpg

Just scripting this is half of my battle. I also need a way to allow someone who has never coded in their life to easily create new dialogue trees.

Currently I have 2 ideas for this but I doubt they are efficient or easy to pull off.

  1. Scriptable Objects: I am not quite sure how to go about this but it would make exposing variables easy but also tedious to fill in.
  2. Custom node editor: I know this is a bit ambitious but it would be worth it in the long run. I have very little experience with custom inspectors, but if there is an expert out there who is willing to put up with my idiocy, I believe this would be the best option.

Any help on where to start and/or structure is greatly appreciated.

If you want help with this let me know and we can chat.

I will tell you how I am handling mine:

  • We are using a custom text parser, so if I want bold text for example I have bold text tags, ect. So each character gets its own txt file.
  • The text parser also includes speed indicators and emote tags. As an example.
  • As I said, each character gets its own text file, we also have “parts” in it. So there is an indicator like &1&, that indicates that if we pass along “1” as the dialogue number, it will only read from part 1.
  • I plan on adding a &*r& tag as well, so when the player exhaust a part, and hasn’t moved on to the next part, they will hit that repeat tag in that part.

[e0][e1][s1] Hello!

This means, eyes emotion 0, mouth emotion 1, print speed 1. We can change those any time by dropping those tags in. We have 7 standard emotions for the eyes and mouth, and then three custom additional slots.

We are using using game objects for each letter, and loading a “font” in which is actually a class for slots for each letter. This lets us apply shaders, jiggle, or even use letters as game objects. Just anything we want to do.

Those are just some of the things we do.

1 Like

not really, especially since it’s not a novel approach and there have been plenty of threads on it

I’d probably start with a look at
https://forum.unity3d.com/threads/simple-node-editor.189230/

It’s wise of you to be thinking of this from the start. I have a bit of experience with dialogue systems, and the two things that devs spend the most time on are authoring and UI.

Authoring involves providing an editing environment for non-programmers and a fast, simple workflow to convert their content into a data model that your dialogue system can work. You can get a no-frills custom editor up and running pretty quickly in Unity, but as you add features it can become a large project unto itself. Consider leveraging an existing editor such as Twine or articy:draft. Talk with your writer(s) and ask them if they have any preferred tools. If they’re modders, maybe they’re already comfortable with the Neverwinter Nights or Dragon Age toolsets (which can export to a simple XML format) and can be productive right away.

Then you just need to write a importer that can convert it to your data model. When you’re designing your data model, try to think of features that you’ll need, such as language localization, and how this will impact the author’s workflow. If you end up using translators, you’ll need a way to get content into and out of Unity, since translators generally work with tools like MS Excel, not Unity. Think about workflow here, too. The more you can offload on the writers, the happier everyone will be. The writers will have more creative control, and the project’s workload will be better balanced so everyone’s not constantly waiting on you to write code. Even if you write an editor in Unity, you’ll probably still need a way to get content into and out of Unity.

The UI part is also deceptively complex. Depending on your build platforms, you may need to support joystick navigation, touch input, different resolutions, etc. A lot of JRPGs employ a typewriter-style effect that also allows the writer to specify pauses and other timing codes in their text. (This is more common in visual novels, but still used frequently in JRPGs.) The writers will probably also want control over changing characters’ pictures, either on a permanent basis (e.g., an NPC gets a permanent scar) or temporary (the NPC has an angry expression for part of a conversation).

1 Like

I like this setup, it’s simple but also offers customization. Could you point me in the direction of where to start?

Actually yeah, do you want code, or do you just mean in general? If you’ve never coded, start with just using the UI, and programatically changing the text.

http://answers.unity3d.com/questions/777335/46-ui-changing-the-text-component-via-script.html

Then try to read from a text file and change the text to that.

http://answers.unity3d.com/questions/164460/can-you-use-resourcesload-to-load-a-text-file-into.html

Then start using string parsers to remove tags, and try to create your own bold tags. I use ** ** for mine.

https://forum.unity3d.com/threads/text-parsing.20183/

To clarify, string parsing is when you start working with strings to change things. For example, in this case, I replace ** with a bold text marker and then replace ** with an end bold text marker. This requires me to keep count of start and end markers. You can do it however you want. So when the parser hits [e0][e1] it doesn’t print that. Instead, it remove it from the string, and sets the emotes.

1 Like

Currently I am trying to create a system that will read, format, and print lines of Dialogue from a text file. Everything works until I try to switch to the next line. I keep getting a vague error that wont even point me to the line of code that is causing the problem.

“ArgumentOutOfRangeException: startIndex + length > this.length
Parameter name: length”

Right now I suspect the culprits of this error to be these lines :

if (currentChar + lineLimit >= lineLength) { rawSubstring = allLines[currentLine].Substring(currentChar, currentChar + lineLimit); currentChar = lineLength; }
        else { rawSubstring = allLines[currentLine].Substring(currentChar, currentChar + lineLimit); currentChar += lineLimit; }

I have also noticed that the script does not print only 10 characters per line like it should. I assume that this is also part of the problem.
EDIT I counted and each line displays 10 more characters than the last

Full Script:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.IO;

public class DialogueReader : MonoBehaviour {

    //private List<BaseCharacterAsset> CharacterAssests = new List<BaseCharacterAsset>();

    public TextAsset Dialogue;

    private int currentLine; //Line in text file
    private int currentChar; //current Character in the line

    private int lineLimit = 10; //# of characters in a textbox line before moving to the next line

    void LoadResources()
    {
        //CharacterAssests.Clear();
        //BaseCharacterAsset[] resources = Resources.LoadAll<BaseCharacterAsset>(@"CharacterDialogueAssets");
        //foreach (BaseCharacterAsset characterAsset in resources)
        //{
        //        CharacterAssests.Add(characterAsset);
        //}
    }

    private string[] allLines;//array of each line in the text file

    private int lineLength;//length of the current line

    void ReadLine()
    {
        //Debug.Log("Hello?");
        if (currentChar == lineLength)
        {
            //Debug.Log("Hello?");
            NextLine();
        }
        //Debug.Log("Hello?");
        Debug.Log(lineLength);
        Debug.Log(currentChar);
        if (currentChar + lineLimit >= lineLength) { rawSubstring = allLines[currentLine].Substring(currentChar, currentChar + lineLimit); currentChar = lineLength; }
        else { rawSubstring = allLines[currentLine].Substring(currentChar, currentChar + lineLimit); currentChar += lineLimit; }
        FormatSubString();

        Debug.Log(formattedSubString);
    }

    string rawSubstring;//before formatting
    string formattedSubString;//after formatting
    void FormatSubString()
    {
        if (rawSubstring.StartsWith(" "))
        {
            rawSubstring.Remove(0);
        }

        formattedSubString = rawSubstring;
    }

    void NextLine()
    {
     
        currentChar = 0;
        if (allLines[currentLine + 1] != "")
        {
            Debug.Log("Passes");
            currentLine++;
        }else{
            EndDialogue();
        }
        lineLength = allLines[currentLine].Length;
    }

    void CompileLines()
    {
        currentLine = 0;
        currentChar = 0;

        allLines = File.ReadAllLines(Dialogue.name.ToString() + ".txt"); //Still Trying to figure this part out. put file in first level of project folder
        for (int i = 0; i < allLines.Length; i++)
        { Debug.Log(allLines[i]); }

        lineLength = allLines[currentLine].Length;
    }

    void EndDialogue()
    {
        Debug.Log("End");
    }
    private void Start()
    {
        CompileLines();
    }

    public void Next()
    {
        ReadLine();
    }
}

I have also attached the text document I am using for testing.

As stated before any and all help is very much appreciated.

2864038–209707–New Text Document.txt (305 Bytes)

Try: this.length - 1

It looks like you are out of the bounds of the string array. Strings are just arrays of chars. Sorry if this is way off, I can’t test anything since I at work.

I figured out the problem.
in these lines:

if (currentChar + lineLimit >= lineLength) { rawSubstring = allLines[currentLine].Substring(currentChar, currentChar + lineLimit); currentChar = lineLength; }
        else { rawSubstring = allLines[currentLine].Substring(currentChar, currentChar + lineLimit); currentChar += lineLimit; }

I misread the parameters. the substring method needed a Start position and a length. I thought it said a Start Position and an End Position. Everything works perfectly now.

I changed it to :

if (currentChar + lineLimit >= lineLength) { rawSubstring = allLines[currentLine].Substring(currentChar, lineLength-currentChar); currentChar = lineLength; }
        else { rawSubstring = allLines[currentLine].Substring(currentChar, lineLimit); currentChar += lineLimit; }

Something I learned the hard way, you really want to plan all the features you will need now. Going back and refactoring string manip code is a pita.

currently I have set it up to create 2 different versions of each line. The first is the raw form with with all of my tags. The second is a version stripped of all of the tags. I then have it reading and printing from the stripped version because I do not want the tags to be included in the character count. I now need to figure out how to compare the raw and the stripped lines to format the final product. How would you suggest going about this?

Example:
Raw- This is an example.
Stripped- This is an example.
Formatted- This is an example.

I just noticed that if I mark rich text on the UI text element, unity automatically bolds the text because it uses the tag. Goodbye an hour of wasted work.

If your game’s simple, you could just use the Hierarchy and GameObject nesting to build your dialogue trees visually. Each line of dialogue would be a GameObject with a script on it containing the avatar/emotion/audio variables. They could be nested under “Topic” GameObjects.

The major downside is that the “data” is contained in your scene (unless you reference txt/xml files), so if you changed a class in a major way (changed the name of a variable), the data will get wiped.

Sounds/emotions would just be variables on a MonoBehaviour.

For sounds, I have a sound take [sound=“resources path”] that gets triggered instead. So I don’t need to mess with game objects. Just another thought.

I set up a scriptable object for each of my characters that has an array of Facial expression textures and an array of reaction voices. I then set up my Dialogue reader to check for a tag with the characters ID (EX: PC001) and then check my resources folder for a scriptable object that has the same ID and load it. After that its as simple as using more tags to indicate what expression and reaction voice to use.

I am not sure if this is helpful but here is how I did it:

First I was looking at this project: Simple node editor - Unity Engine - Unity Discussions and used it as a base.

A dialogue at it’s core is a Tree data structure of text, with additional formatting and events that can be triggered. So following the above I feel it’s pretty simple to write enough nodes to handle Dialogue:

I.e I have the following nodes (Which are ScriptableObjects):

  • Text Node - This is just text. It can read in ‘rich text’ and have options in [ ] to link to items\abilities\information in other parts of the game. This is handled by the DialogueReader.
  • Action Node - This will send an event from the DialogueReader with some information on what the action is (I.e Exit Conversation, Change Mood(Player says something the character hates, this will change their face), Start Event and Set Variable. My dialogue has a globally accessible variable storage to handle story elements changing dialogue with simple logic to handle it within the dialogue tree.
  • Branch Node - This will branch based on a variable equaling one or many things.

My API for the reader looks something like this:

someDialogue.OnAction += (action) {
    // Do thing based on action. Open shop.
}

Dialogue dialogue= someDialogue.GetNextText(); // Will branch and call any events if needed and change\add variables.

// Dialogue has the characters text and the players responses.
dialogueView.SetText(dialogue.characterText);
dialogueView.SetReponses(dialogue.playerResponses);

On top of this I have the ability to load the dialogue from a database or JSON file which is easy once the structure is setup.

Here’s a video walkthrough of a partial solution. This shows how to write dialogue in an easy to write JSON format, read it, and display it with a bit of polish.

1 Like

Here are a couple more videos that set up a dialogue system similar to the Fire Emblem style listed above. These emphasize making it easy to build dialogue within Unity in the inspector

Can’t believe no one’s posted up Brackey’s tutorial yet, he also shows you how to do incrementing text.

What I would do is just follow this tutorial and then I would activate my dialogue options based on what buttons were pressed, no need for anything ridiculously complicated.

1 Like