Hello and welcome to lesson 4 of the articy:draft Importer for Unity tutorial series.
Recap lesson 3
In the previous lesson we refactored some code in the PlayerController
script, improving the functionality of the OnTriggerEnter
method. And secondly, we can now display the speaker name in our dialogue UI.
This lesson
In this lesson we will add functionality to the buttons of our dialogue UI, so that we can finally traverse through the entire linear test dialogue.
Adding functionality to navigational buttons
We are able to start a dialogue we imported from articy in our little Unity game project and display the first line of dialogue and its speaker name. But as we can see in articy, the dialogue consists of more than one line of course, so let’s make the rest of the text visible in Unity as well.
First let’s take a quick look at how the dialogue widget is set up. We have an image object called Dialogue Box on which we display the dialogue text (1). The speaker is a separate text object and is positioned so that the speaker name is displayed above the dialogue text (2). Below that we have two buttons. One button for the dialogue choices and one button to end a dialogue (3). They are childed to the Dialogue Branches game object which has a Horizontal Layout Group component, to position the buttons next to one another, using the total width of the dialogue box (4).
Okay, let’s give these buttons some functionality. In the DialogueManager
script we add a variable of type Button
which we call dialogueButton
and open it to the editor via the SerializeField
attribute.
[SerializeField] Button dialogueButton;
Then we switch to Unity, select the Dialogue Manager game object, and drag the dialogueBranch
object to fill the reference we just created.
Back in the DialogueManager
script we now add a listener to a button click within the Start
method: dialogueButton.onClick.AddListener
. And we are going to call ContinueDialogue
on a click. I generate the method and remove the throw command.
What are we going to do? We started our dialogue with the Flow Player. The Flow Player arrived at the first Dialogue Fragment, paused, and provided us with all the data we requested to show dialogue text and speaker name. But it is still pausing, so we need to tell it to get going again. This we do with flowPlayer.Play
.
void Start() { dialogueButton.onClick.AddListener(ContinueDialogue); } public void ContinueDialogue() { flowPlayer.Play();throw new NotImplementedException();}
Let’s see what happens if we test the scene. It works! Finally we can click through the dialogue and behold it in all its glory. Tad too much? Probably. But joking aside, we took another good step to achieve our goal to display branching dialogue imported from articy.
You will notice that at the end of the dialogue we cannot close the window to actually conclude the dialogue. This is what we will take care of next.
In the DialogueManager
script we add another variable of type Button
with the name endDialogueButton
, then we fill this reference field with the dialogueBranchClose
object in Unity.
[SerializeField] Button endDialogueButton;
To implement a button to close the dialogue we first need to check when the dialogue ends. This we will do in the OnBranchesUpdated
method. Here we get passed a list of all branches following the current node. This information we can use in a foreach loop to see if there is still text coming or not.
In OnBranchesUpdated
we declare a bool dialogueIsFinished
and initialize it to true. Next we start the foreach loop, checking each var branch in aBranches
. Because of the way we set up our dialogues in articy, with Dialogue Fragments nested inside of Dialogue nodes, we know that the dialogue is over when no Dialogue Fragment can be found as the following branch target. Therefore we make a check if any branch leads to an IDialogueFragment
target. If this is the case, the current dialogue is apparently not over yet, and we set dialogueIsFinished
to false.
public void OnBranchesUpdated(IList<Branch> aBranches) { bool dialogueIsFinished = true; foreach (var branch in aBranches) { if (branch.Target is IDialogueFragment) { dialogueIsFinished = false; } } }
Okay, so as long as we have Dialogue Fragment following the current node, the dialogue continues. But what is supposed to happen when it finally comes to an end? We want to deactivate the Continue button and activate the End button in its stead. Then we need to add a Listener to the button click to call the CloseDialogueBox
method to properly conclude the current dialogue, which we do in Start
where we already have the listener for the other button.
void Start() { endDialogueButton.onClick.AddListener(CloseDialogueBox); } public void OnBranchesUpdated(IList<Branch> aBranches) { if (dialogIsFinished) { dialogueButton.gameObject.SetActive(false); endDialogueButton.gameObject.SetActive(true); } }
Let’s go over to Unity and run a test. The functionality is there as intended. When we get to the end of the dialogue a working Close button appears. And it even starts to bring out the layout principle I planned for the button appearance. If there is only one choice, only the button in question should be displayed, like with the Close button now. The dialogue is concluded so there is no reason to keep showing the continue button.
We need to make a few more adjustments to remove some issues we still have. For one, while within the dialogue, we don’t want to display the Close button. And currently once we concluded a dialogue and address the NPC a second time, the Close button is still there, preventing us from going through the dialogue again.
Go back to the DialogueManager
script. Here we start by adding one line to the CloseDialogueBox
method. When we hide the dialogue UI, we also want to set the Close button back to an inactive state. There is one more thing we can do while we are at this location in the code: At the moment we show the last object with text and are about to close the dialogue. But on this last object there might be an output pin containing scripts which we want to process as well. This we can do by telling the flowPlayer
to finish the current paused object.
public void CloseDialogueBox() { endDialoguebutton.gameObject.SetActive(DialogueActive); flowPlayer.FinishCurrentPausedObject(); }
One little thing left we need to do and that is to set the dialogue button active again. We hide it in our check in OnBranchesUpdated
, and so far we have no way to get it back. By setting the dialogueButton
game object to active in the StartDialogue
method, we can easily fix this issue.
public void StartDialogue(IArticyObject aObject) { dialogueButton.gameObject.SetActive(DialogueActive); }
Back in Unity we set both dialogueBranch
and dialogueBranchClose
objects to inactive, so we can control via the code when which button appears, then we can run another test.
Recap lesson 4
Beautiful. We made good progress in this lesson. We added functionality to our UI buttons, so that we can now traverse through the entire linear test dialogue and close the dialogue UI at the end. If all you want is to display linear dialogue, you can basically orient yourself at the current project structure and are good to go.
Next lesson
If you want to offer choices to your player, don’t worry, we are going to start tackling that in the next lesson. We will start setting up branching functionality by instantiating buttons for the number of branches the player encounters at any part of the dialogue.
See you there!
Current state of C# scripts we have worked on this lesson:
DialogueManager.cs
using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using Articy.Unity; using Articy.Unity.Interfaces; using Articy.UnityImporterTutorial; using System; public class DialogueManager : MonoBehaviour, IArticyFlowPlayerCallbacks { [Header("UI")] // Reference to Dialog UI [SerializeField] GameObject dialogueWidget; // Reference to dialogue text [SerializeField] Text dialogueText; // Reference to speaker [SerializeField] Text dialogueSpeaker; // Reference to navigation button [SerializeField] Button dialogueButton; // Reference to close button [SerializeField] Button endDialogueButton; // To check if we are currently showing the dialog ui interface public bool DialogueActive { get; set; } private ArticyFlowPlayer flowPlayer; void Start() { flowPlayer = GetComponent<ArticyFlowPlayer>(); dialogueButton.onClick.AddListener(ContinueDialogue); endDialogueButton.onClick.AddListener(CloseDialogueBox); } private void ContinueDialogue() { flowPlayer.Play(); } public void StartDialogue(IArticyObject aObject) { DialogueActive = true; dialogueWidget.SetActive(DialogueActive); flowPlayer.StartOn = aObject; dialogueButton.gameObject.SetActive(DialogueActive); } public void CloseDialogueBox() { DialogueActive = false; dialogueWidget.SetActive(DialogueActive); endDialogueButton.gameObject.SetActive(DialogueActive); // Completely process current object before we end dialogue flowPlayer.FinishCurrentPausedObject(); } // This is called every time the flow player reaches an object of interest public void OnFlowPlayerPaused(IFlowObject aObject) { //Clear data dialogueText.text = string.Empty; dialogueSpeaker.text = string.Empty; // If we paused on an object that has a "Text" property fetch this text and present it var objectWithText = aObject as IObjectWithText; if (objectWithText != null) { dialogueText.text = objectWithText.Text; } // If the object has a "Speaker" property try to fetch the speaker var objectWithSpeaker = aObject as IObjectWithSpeaker; if (objectWithSpeaker != null) { // If the object has a "Speaker" property, fetch the reference // and ensure it is really set to an "Entity" object to get its "DisplayName" var speakerEntity = objectWithSpeaker.Speaker as Entity; if (speakerEntity != null) { dialogueSpeaker.text = speakerEntity.DisplayName; } } } // Called every time the flow player encounters multiple branches, // or is paused on a node and wants to tell us how to continue public void OnBranchesUpdated(IList<Branch> aBranches) { // Check if any branch leads to a DialogueFragment target // If so, the dialogue is not yet finished bool dialogueIsFinished = true; foreach (var branch in aBranches) { if (branch.Target is IDialogueFragment) { dialogueIsFinished = false; } } if (dialogueIsFinished) { // If dialogue is finished hide Continue button and show Close button dialogueButton.gameObject.SetActive(false); endDialogueButton.gameObject.SetActive(true); } } }
Don’t have articy:draft 3 yet? Get the free version now!
Get articy:draft 3 FREE
*No payment information required
Follow us on Twitter, Facebook and LinkedIn, or visit our articy Reddit to keep yourself up to date and informed.