I recently started work on a new RPG project, for which I knew I’d need to work on a dialogue system. After implementing this in the form of a JSON / internal database, I settled on creating my handler for quick implementation and ‘scriptability’™.
One requirement of this system was to have variable responses, blocked either by prerequisites or due to certain events being triggered.
ScriptableObjects are a wonderful way of storing bits of data that you may not necessarily want to be easily editable in your game. The downside of this method over having a large database is that every conversation is its instance, meaning that instead of one file that can easily be passed off to a localiser, for example, each object must be selected and changed manually in the editor.
Setting Up our Dialogue Class
For my project, I wanted to know both the NPC’s ID and name, this could easily be adapted to hold an ID for looking up a sprite in the case of wanting a profile picture during conversations.
I also wanted two additional fields for my responses, these being pre-requisites and triggers. Pre-reqs allow for certain responses to only display if those conditions are met, triggers send out an event to the object that triggered the dialogue (either the player or an NPC).
The ‘next’ field notes which response to show next, I’d recommend using -1 as your default ‘exit’ value.
[System.Serializable]
public class Dialogue : ScriptableObject {
public int npcID;
public string npcName;
public Message[] messages;
}
[System.Serializable]
public class Message {
public string text;
public Response[] responses;
}
[System.Serializable]
public class Response {
public int next;
public string reply;
public string prereq;
public string trigger;
}
Creating Instances
Now for a slightly awkward tidbit; if you want to create an instance of your lovely new class, we can simply right-click the script, and we’re done! have to create a brand new class and add a new ‘create’ option in our editor. To do this, just add this either as its script or inside the dialogue script:
[CreateAssetMenu(fileName = “New Dialogue”, menuName =”Dialogue/New Dialogue”)]
public class DialogueData : Dialogue { }
Beautiful! Once the instance is created, I’d recommend folders for each of your rooms to help keep track of conversations. In this example, I’ve created a folder for all conversations held in the living room.
Once you have your conversations, you can now start writing your dialogue. Note that you can still implement your markup for these pieces of text as if they were done in a DB format.
Reading the Text
There are many implementations and areas of customisation for reading in the text, so I’ll just be covering the bare bones here.
Create a class called TextHandler and add a new public function called LoadDialogue, passing through the dialogue object so that we can access the data and the sender object itself. (This is useful to us if we want to send events to that object)Additionally, create a variable at the top of the script where we can cache a reference to the dialogue object for use in other functions.
Create another function called LoadText; this will take the index of our ‘next’ dialogue option. We’ll want to call LoadText(0) inside out LoadDialogue option to start each interaction at its first index.
Here you can access the object's content by accessing the reference created earlier and using ‘next’ as the index.
Now that we have our text-complete dialogue objects and TextHandler, we need a way to assign them to each object.
Create a class called Interactable and create a reference for both the TextHandler and Dialogue options. In my case, I added a little floating speech bubble that shows the object is interactable. You’re all done! You can now start writing dialogues in seconds and customising them to your heart’s content <3