| |

A Simple Dialog Quest System For Unity

In this tutorial you will learn how to create a simple and extensible dialog quest system for Unity. We will also create a simple dialog box UI to display the quest to the player. The system will use XML files to store dialog data and allow for different dialogs to be loaded on the fly.

Difficulty: Medium, you should have a intermediate understanding of C#, data structures, the Unity Scene Editor, Game Objects, and Prefabs.

Note: Updated Tutorial should be easier to follow now, github repository added for tutorial files!

dialog

Resource Setup

Since we will be storing our data in XML files, we will start by creating a sample quest to work with.

<Quest>
    <QuestTitle></QuestTitle>
    <Nodes>
        <Node>
            <NodeTitle></NodeTitle>
            <NodeText></NodeText>
            <NodeImage></NodeImage>
        </Node>
    </Nodes>
</Quest>

This is the format that I will use for the tutorial. Each quest will be stored in it’s own XML file, allowing for the following information to be saved. In addition to having a quest title, the quest line will also store an array of nodes, which represent each part of the dialog.

Each node can have a title, body text, and an image. Each of these elements are optional, as you will see later, the UI will organize the dialog accordingly.

Next let’s setup our asset folder structure to keep everything organized. Create the following folders and files.

 /Resources/Quests
 /Resources/Icons
 /Scripts

Now that we have all the preliminaries setup, we will get right into coding. Since the quests are being stored as XML files, we need to load them into Unity before we can use them. We also want to be able to reference them in our game world without having to reload them each time.

Quest Dialog System Scripts

QuestNode.cs – Represents a node within a quest.

using System.Xml.Serialization;

public class QuestNode
{
    [XmlElementAttribute("NodeTitle")]
    public string NodeTitle;

    [XmlElementAttribute("NodeText")]
    public string NodeText;

    [XmlElementAttribute("NodeImage")]
    public string NodeImage;

    public override string ToString()
    {
        return string.Format("[NodeTitle: {0} NodeText: {1} NodeImage: {2}",
            NodeTitle, NodeText, NodeImage);
    }
}

Quest.cs – Represents a quest, holds reference to a list of nodes in the quest, as well as current progression in the quest.

using System.Collections.Generic;
using System.Xml.Serialization;
using System.IO;

public class Quest
{
    [XmlArray("Nodes")] [XmlArrayItem("Node")]
    public List Nodes;

    [XmlAttribute("QuestTitle")]
    public string QuestTitle;

    [System.NonSerialized()]
    private QuestNode _currentNode;

    [System.NonSerialized]
    private int _position;

    /**
     * Returns the current node progress in quest and the moves to next node if possible.
     **/
    public QuestNode ProgressQuest()
    {
        if (HasNextQuestNode())
        {
            _currentNode = Nodes[_position++];
            return _currentNode;
        }
        return null;
    }

    private bool HasNextQuestNode()
    {
        return Nodes != null && _position < Nodes.Count;
    }

    /**
     * Loads quest from XML file.
     **/
    public static Quest LoadQuest(string fileName)
    {
        var serializer = new XmlSerializer(typeof(Quest));
        var stream = new FileStream(fileName, FileMode.Open);
        var quest = serializer.Deserialize(stream) as Quest;
        stream.Close();

        return quest;
    }
}

These two classes Quest and QNode represent our quest and node information from the XML file we created before. LoadQuest(string name) is a static method used to load a quest. Given a XML file, it uses XMLSerializer to de-serialize our xml data into the Quest and QNode objects.

QuestManager – Used to control and facilitate interaction between quest data and the UI. Also holds reference to node icons.

using UnityEngine;
using System.Collections.Generic;

public class QuestManager : MonoBehaviour
{
    private Dictionary<string, Sprite> _iconDict;

    [SerializeField]
    private Sprite[] _icons;

    [SerializeField]
    private GameObject _questDialog;

    void Start()
    {
        _questDialog.SetActive(false);
        LoadIconsFromSprites();
    }

    private void LoadIconsFromSprites()
    {
        _iconDict = new Dictionary<string, Sprite>();
        foreach (Sprite sprite in _icons)
        {
            _iconDict.Add(sprite.name, sprite);
        }
    }
    
    /**
     * Triggers the start of a quest
     * questFilePath: supply file path to the xml file
     **/
    public void StartQuest(string questFilePath)
    {
        _questDialog.SetActive(true);
        _questDialog.GetComponent()
            .Initialize(Quest.LoadQuest(questFilePath));      //Uses the Dialog UI and initializes the quest onto the display
    }

    public Sprite GetIcon(string iconName)
    {
        if (_iconDict[iconName] != null)
            return _iconDict[iconName];
        else
            return null;
    }
}

I chose to use a Dictionary<string, Sprite> so that icons could be looked up quickly. This is especially helpful when your game has a large number of icons. I was hoping to not have to use Sprite[] array, however it does not seem you can add references to the dictionary from the Component Inspector, so I used a work around, throw everything in the Sprite[] icons array, then on Start(), the script will populate the dictionary. If anyone has a better method of looking up the icons, please let me know!

QuestDisplay.cs – Add this as a component to your quest dialog. Used to update the dialog UI as the quest dialog progresses. Also hides the UI again when the quest is complete.

using UnityEngine;
using UnityEngine.UI;

public class QuestDisplayComponent : MonoBehaviour
{
    public Quest Quest;

    [SerializeField]
    private Image _nodeImage;
    [SerializeField]
    private Text _nodeTitle;
    [SerializeField]
    private Text _nodeText;
    [SerializeField]
    private QuestManager _questManager;

    private QuestNode _currentNode;

    public void Initialize(Quest quest)
    {
        Quest = quest;
        ProgressNode();
    }

    public void ProgressNode()
    {
        _currentNode = Quest.ProgressQuest();

        if (_currentNode == null)
        {
            gameObject.SetActive(false);
            return;
        }

        _nodeImage.sprite = _questManager.GetIcon(_currentNode.NodeImage);

        _nodeTitle.text = _currentNode.NodeTitle;
        _nodeText.text = _currentNode.NodeText;

        if (_nodeImage.sprite == null)
            _nodeImage.gameObject.SetActive(false);

    }
}

Now that we are finished with our scripts, let’s move on to populating our scene.

Scene and UI Setup

First create a UI->Canvas. We will need this to hold all our UI elements.

Create a Empty Object inside Canvas and name it Dialog. Add the QuestDisplay.cs and Event Trigger component. You will also assign the image, title, and text components (which we will create next).

Don't forget to set reference to the QuestManager!
Don’t forget to set reference to the QuestManager!

Inside the Dialog object we just created, add a UI->Panel. Name this DialogPanel. This will be used to layout the rest of the dialog elements. Add a Horizontal Layout Group.

Dialog 02

Now we will add an UI->Image inside the DialogPanel to display node icons. Add a Layout Element with the following properties.

Dialog 03
I have a source image ‘betty-icon’ but this doesn’t matter. It will be populated via code anyway.

Now for the text. Since we have two types of text for our nodes (title & body), we will create another panel to help organize them. Create a UI->Panel and name it DialogTextPanel. This time we will use a Vertical Layout Group. Also add a Layout Element component to it.

Dialog 04

Create two UI->Text objects inside the DialogTextPanel. Name one DialogTitleText and the other DialogText. You may style them to your liking.

Finally create an Empty Object and add the QuestManager.cs script that we wrote before to it. Give it reference to your Dialog object and then add your icons to it.

Dialog 05

Wrapping Up

Your scene hierarchy should look like this.

heirarchy

And here is the final project. I added two buttons as ‘triggers’ to the quests to test. However you can trigger your quests anywhere you want using the QuestManager.cs StartQuest(string questName) method. The quest will progress as you click on the dialog, moving onto the next node until it reaches the end of the quest.

questbutton
Sample quest button that loads a SampleQuest.xml

sample

You can easily expand on this by adding your own functionality, in addition to dialog. Some things that come to mind include panning the camera, presenting the player with choices, and so much more! Check back for part two of the tutorial where I will expand on the event trigger of the quests and add more functionality. Thanks for reading my tutorial! Hope it helps and would love to hear any questions or comments!

If you’re having trouble or just want to check out the tutorial project: Github.

Interested in Unity? Check out my other Unity tutorials.

If you’re looking for a simpler but more extensible quest / objective system, check out my new tutorial on creating a Dynamic Objective System in Unity.

Jonathan

Jonathan

Hey there, I’m Jon! I love landscape photography which is why I travel all the time! On my blog I’ll share all the best spots for epic sunsets and sunrises as well as some photography tips and tricks. I also have a interest in game design so you’ll find the occasional post about that too. Thanks for visiting!

Similar Posts

guest

11 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
B

Hey, the dictionary made the icon not optional. My quick fix that worked was:
public Sprite GetIcon(string iconName)
{
Sprite value;
if (iconDict.TryGetValue(iconName, out value)) {
return value;
} else {
return null;
}
}

Other than that, awesome! Love it!

Jonathan

Thank you so much for this tutorial.
I have been trying a lot of different tutorials
and yours is the best.
Thanks and keep up the good work

Adam

Love this tutorial, even more so for taking the time to do good old fashioned text and images rather than an overlong YouTube video. Looking forward to part II.

Redha Halim

Hello, I have a problem with reading the xml file. I’ve been following the tutorial thoroughly but somehow I can’t call the XML file that i created and the button to test the dialog doea not work.

Jonathan

Hey Redha, thanks for reading my tutorial! I would love to help you solve your problem, could you describe the issue you’re having trouble with?

Ahmet Gurbuz

Hi, can you give me sample xml file, I couldnt run it.

Jonathan

Hi Ahmet, thanks for visiting. Hope I can help you: here is a link to a sample .xml file: https://gist.github.com/xjjon/5117ed74f97fb2f602fb7adb39f577cb

Make sure you are loading it from your Assets/Resources folder.

Let me know if you need any additional help.

Liam Sundström

Where do i put the

My Quest

Node 1
Text for Node 1 of my quest
myImage

code? in c# script??

Jonathan
Dave

I had two problems so far. First was the List declaration in ‘Quest.cs’. I had to change to List #Questnode# Nodes = new List#QuestNode#(); to avoid getting error. Now im stuck in StartQuest method in ‘QuestManager.cs’ when i do GetComponent() I get error saying type cannot be inferred from the usage.

Jonathan

Hi Dave,

Please try the sample located here: https://github.com/xjjon/Simple-Dialog-Quest-System-For-Unity

For your first error – it’s likely that your XML file did not serialize properly, hence the list is empty. Is the format for your quest correct? Take a look at this: https://github.com/xjjon/Simple-Dialog-Quest-System-For-Unity/blob/master/Assets/Resources/Quests/SampleQuest.xml

For the second error, you need to define the type when calling GetComponent. It should be GetComponent();