In most games, a system for displaying dialogue is essential, whether for NPC conversations, tutorials, or other in-game messages. Here we display a system that helps you create these dialogues easily
You can create a new dialogue by going to Create/Essentials/Dialogue/Dialogue. Here we can configure a couple of settings.
The actual dialogue data is what's called the Header and the Description. To create a full conversation we can create new Branches. A Branch is a link to another dialogue instance. For example, if we want to transition from dialogue A to B, we create a new Branch and assign dialogue B as the Next dialogue.
For the dialogue to understand which and when it should transition to another branch, we use Conditions. A condition can be for example; Done (whenever dialogue has finished), Skip (if a user presses a button) etc. You can create and locate new conditions under the Create/Essentials/Dialogue/Conditions.
Conditions
To create a custom condition, we do the following:
// This creates a ScriptableObject that is located wherever all the other conditions are
[CreateAssetMenu(fileName = "Test [CONDITION]", menuName = "Essentials/Dialogue/Conditions/Test", order = 0)]
public class TestCondition : Condition // we must extend Condition
{
// if Evaluate() is NOT overriden, it will return a protected variable called isSatisfied.
// we can change this variable wherever we want directly, or call SetTrue() or SetFalse().
// we don't NEED to override this, only if we want some initilization code
public override void Init(DialogueManager manager)
{
base.Init(manager); // make sure to call the base.Init()
// do some initialize code
}
// we don't NEED to override this, only if we want some dispose code
public override void Dispose()
{
base.Init(manager); // usually, we call base.Init() here.
// do some dispose code
}
// we don't NEED to override this, only if we want to return something else, rather than isSatisfied
public override bool Evaluate()
{
return // return some code to evaluate.
}
}
For example, to create a skip condition we write:
// This creates a ScriptableObject that is located wherever all the other conditions are
[CreateAssetMenu(fileName = "Skip [CONDITION]", menuName = "Essentials/Dialogue/Conditions/Skip", order = 0)]
public class SkipCondition : Condition // we must extend Condition
{
public override bool Evaluate()
{
return Input.GetKeyDown(KeyCode.Space);
}
}
Or, if we just want to create a condition that auto-selects the branch when the dialogue is finished:
// This creates a ScriptableObject that is located wherever all the other conditions are
[CreateAssetMenu(fileName = "Done [CONDITION]", menuName = "Essentials/Dialogue/Conditions/Done", order = 0)]
public class DoneCondition : Condition // we must extend Condition
{
public override void Init(DialogueManager manager)
{
base.Init(manager);
manager.onDialogueFinished += SetTrue; // we subscribes to the dialogue's finish event to set this condition true.
}
public override void Dispose()
{
base.Dispose();
manager.onDialogueFinished -= SetTrue; // we MUST also remember to unsubscribe to avoid leak over dialogues.
}
}
Initializing dialogues
Dialogues can appear visually in many different scenarios. This is why we created an abstractDialogueManager that you can extend wherever you need a dialogue to appear.
For example; if we want to show the dialogue in a UI we could create a dialogue manager like this:
public class DialogueUI : DialogueManager // extend the dialogue manager
{
public TextMeshProUGUI headerText, descriptionText;
// this must exist in any class that extends DialogueManager
// it is called whenever the dialogue has finished.
protected override void Hide()
{
headerText.text = ""; // clear the header
descriptionText.text = ""; // clear the description
}
// this must exist in any class that extends DialogueManager
// it is called whenever a new dialogue is supposed to show
protected override void Show(Dialogue dialogue)
{
headerText.text = dialogue.Header; // we set the header to the dialogue's header
descriptionText.text = dialogue.Description; // we set the description to the dialogue's description
StartCoroutine(DelayFinish()); // we delay the finish a bit to allow the player to see the dialogue before it finishes.
// alt. we can use the Thread.Delay(this, Finish, 1) that comes with the package as a utility function.
}
private IEnumerator DelayFinish()
{
yield return new WaitForSeconds(5); // waits 5 seconds.
Finish(); // this must always be called somewhere in a DialogueManager whenever the dialogue is supposed to be finished.
}
}
For more examples, we can see the dialogue demo.
To later start a dialogue, we can simply call:
public DialogueManager manager; // some reference to any manager (could be a UI)
public Dialogue dialogue; // some reference to the dialogue to show
manager.Run(dialogue); // run the new dialogue (could be placed in a Start() or any other desired place.