How to Create a Dialogue Tree?

Hi. So I have this program set up where I want to display a question and two options, and if you click one option, you either go to another level of the tree or a leaf. I want to use the composite design pattern to make separate level instances, each with different paremeters for one question and two options and add them together into a list. What I’m stuck on is how do I start at the first level and traverse down the tree? It seems like no matter what I do, it only displays the last level parameters added to the list. If anyone could shoot some ideas, I would appreciate it. Thank you.

public class Level : MonoBehaviour {

     bool button1Pressed;
     bool button2Pressed;

    private void Start()
    {
      

        Level Level1 = new Level("Hello", "Hi", "Shut Up");
        Level leaf1 = new Level("Don't be Rude");

        Level Level2 = new Level("What you Doing?", "Not Much", "None of your Business");
        Level leaf2 = new Level("Well Excuuuuse Me");

        Level Level3 = new Level("Can I do that too?", "Sure", "Go Away");
        Level leaf3 = new Level("Fine. Be a Jerk");

        Level Level4 = new Level("This is boring, can we do something else?", "Why not?", "You're boring");
        Level leaf4 = new Level("I'll go be boring somewhere else");

        Level Level5 = new Level("You want ice cream?", "Sounds Good", "I'm allergic");
        Level leaf5 = new Level("ok.......");
        Level leaf = new Level("I Want Chocolate");      

        Level1.add(Level1);
        Level1.add(leaf1);

        Level2.add(Level3);
        Level2.add(leaf2);

        Level3.add(Level4);
        Level3.add(leaf3);

        Level4.add(Level5);
        Level4.add(leaf4);

        Level5.add(leaf5);
        Level5.add(leaf);



    }


  


    public static Text Textbox;
    public static Button Button1;
    public static Button Button2;
 
        public string OptionA;
        public string OptionB;
        public string Question;

        public string Leaf;

        private List<Level> levels;

        public Level(string question, string optionA, string optionB)
        {
            this.Question = question;
            this.OptionA = optionA;
            this.OptionB = optionB;

        GameObject.FindGameObjectWithTag("Level").GetComponentInChildren<Text>().text = Question;
        GameObject.FindGameObjectWithTag("OptionA").GetComponentInChildren<Text>().text = OptionA;
        GameObject.FindGameObjectWithTag("OptionB").GetComponentInChildren<Text>().text = OptionB;

        levels = new List<Level>();

        }

        public Level(string leaf)
        {
            this.Leaf = leaf;
            Textbox.text = leaf;
        }

        public void add(Level lvl)
        {
            levels.Add(lvl);
        }

        public List<Level> getLevels()
        {
            return levels;
        }

    public void Button1Pressed()
    {

    }
    public void Button2Pressed()
    {

    }
}

Well, what I’d do for this type of this, it make some kind of linked list thing, except one node could point to many. Kind of like this?

public class Dialogue {

    public string text;
    public List<string> options = new List<string>();

    public Dialogue previous;
    public List<Dialogue> next = new List<Dialogue>();
    

    public Dialogue(string text = "Default text") {
        this.text = text;
    }

    public void AddOption(string option, Dialogue dialogueIfSelected) {
        if (option != "" && dialogueIfSelected != null) {
            options.Add(option); // Add text option
            next.Add(dialogueIfSelected); // Add destination dialogue
        }
    }

    public void RemoveOption(int index) {
        if (index > 0 && index < next.Count) {
            options.RemoveAt(index); // Remove text option
            next.RemoveAt(index); // Remove destination dialogue
        }
    }

    public void SelectOption(int index) {
        // Validate index
        // ...

        // Grab next dialogue
        Dialogue newDialogue = next[index];

        // Display new dialogue
        // ...
    }

}

You’d then have to initialize a bunch of Dialogue objects and connect them all to form your full dialogue, but after that is done, progressing through it would be easy. Just display the initial Dialogue, and then keep selecting a new option and displaying the new one. If this wasn’t quite what you were going for, sorry, I tried. :stuck_out_tongue:

Interesting question! I don’t have an exact solution, but I will try to direct you in the right direction.

First of all, you must never instantiate a class that derives from MonoBehaviour with new. Unity does internal magic with classes that derive from MonoBehaviour, and if you instantiate it directly with new, it cannot do that and memory management will get messed up. So always use Instantiate() for these classes… or consider an alternative.

The alternatives are either a “regular” class (not deriving from MonoBehaviour), or a class derived from ScriptableObject. In both cases these classes would only hold your conversation data, and you would need a separate class to traverse this data. But this is a good thing, you should always try to separate your data (Model), the display of it (View), and the logic (Control). One way of doing that is the MVC pattern.

So you could have Level as data object, and a ConversationControl class that uses the data from each Level to show the question and the options to the user, then based on the input, go to the next level, and repeat… the display should be a separate class that has the references to the UI elements.

So what’s the difference between using a regular class and a ScriptableObject? The first is easier to set up, but you cannot easily set it values from the Editor, while the second is more complicated, but Unity supports saving them into scriptable object assets, which are basically prefabs for data, easily configurable from the Editor. This is a huge topic in itself, but there are some good videos about it. Check out this (long) talk about why they are great and how to use them, or this short, but less amazing tutorial (probably they work best one after the other).

One more thing about ScriptableObjects: as with MonoBehaviour, you must never instantiate them with new, you must use ScriptableObject.CreateInstance<T>().

I hope it is clear what I mean and will be useful answer to you. Feel free to ask questions in comments, I’ll try my best to answer them!