Hello and welcome to lesson 6 of the articy:draft Importer for Unity tutorial series.

Recap lesson 5

In the previous lesson we have started to set up branching functionality for the dialogue navigation buttons. We can now instantiate and show the correct number of buttons required for the amount of following branches in the dialogue UI.

Recap lesson 5

This lesson

In this lesson we will add text to buttons if there is more than one option for the player to choose from. Furthermore, buttons will get their functionality back. At the end of the lesson we should be able to start, properly display, and conclude dialogues.

Lesson 6 content

We start by creating a new C# script called BranchChoice and add it to the dialogueBranch prefab. This script will be responsible for the UI control of a single option in a multiple choice dialogue.

Creating BranchChoice C# script

Adding BranchChoice script to dialogueBranch prefab

First thing we can do in the script is to remove Start and Update and add a couple of namespaces: Articy.Unity, Articy.Unity.Interfaces, and UnityEngine.UI. Then we create two public methods. The first one we call AssignBranch. This method will be called each time a button is created to represent a single branch in the flow. The second method’s name is going to be OnBranchSelected. Within this method we will control what happens when a button is clicked.

using UnityEngine.UI;
using Articy.Unity;
using Articy.Unity.Interfaces;

public class BranchChoice : MonoBehaviour
{
    	public void AssignBranch()
   	{

   	}

   	public void OnBranchSelected()
   	{

    	}
}

Next we declare some private variables: one of type Branch, we are going to call branch, one of type ArticyFlowPlayer called flowPlayer, and one of type Text called buttonText, which we open to the editor via the SerializeField attribute.

private Branch branch;
private ArticyFlowPlayer flowPlayer;
[SerializeField]
Text buttonText;

We switch to the Unity editor, open the dialogueBranch prefab and select the Text child object of dialogueBranch as the target for the buttonText reference we just created. Then we can get back to the BranchChoice script.

Filling reference field

First we add two parameters to AssignBranch: ArticyFlowPlayer aFlowPlayer, and Branch aBranch. Within the method we assign branch the value of aBranch and flowPlayer the value of aFlowPlayer. Then we declare a variable target of type IFlowObject and initialize it to the value of aBranch.Target. Followed by assigning buttonText.text the value of an empty string.

public void AssignBranch(ArticyFlowPlayer aFlowPlayer, Branch aBranch)
{
       	branch = aBranch;
	flowPlayer = aFlowPlayer;
       	IFlowObject target = aBranch.Target;
	buttonText.text = string.Empty;
}

If we look at the dialogue in articy, we can see that for player choices menu text was used. This menu text is supposed to be displayed as the button text. For one it is usually phrased shorter than the actual response, so the risk of overlapping text or cut-offs in the UI is lower. And secondly, I feel it is kind of redundant to show the entire dialogue line already in the button and then show it once again when selected, maybe even in combination with voice over. So this is more of a design decision. To show menu text where it is used in branching situations, and for a linear conversation instead of the text we just display an arrow or something as a signal for the player to click to continue.

Menu text used in Dialogue Fragments

For our script that means we first check if we have any menu text that we could display. We do this by declaring a variable objectWithMenuText and initializing it with target cast to IObjectWithMenuText. Next we check if this object contains the menu text property. If the statement returns true, then we assign buttonText.text the value of objectWithMenuText.MenuText.

var objectwithMenuText = target as IObjectWithMenuText;

if (objectWithMenuText != null)
{
       	buttonText.text = objectwithMenuText.MenuText;
}

Okay, so if we have menu text then we display it. Now we have to cover the case if there is no menu text. Let’s say you wanted to display the entire dialogue line if there’s no menu text. In that case, we start with another if-statement checking if the buttonText.text string currently is empty or null. Then we declare a variable objectWithText and initialize it with target cast to IObjectWithText. Next comes our usual safety check to make sure the object is not null. If it is not null, so if there is any text property, we assign buttonText.text the value of objectwithText.Text.

if (string.IsNullOrEmpty(buttonText.text))
{
	var objectWithText = target as IObjectWithText;
	if (objectWithText != null)
        {
            	buttonText.text = objectWithText.Text;
        }
}

However, like I mentioned before, for this project I just want to display menu text in the dialogue UI, and if there is none, then we display an arrow or something similar, so the player knows they can continue the dialogue. That means I’ll remove the content of the last if-statement we added, and replace it by simply assigning buttonText.text the value of the string we want to display. That’s it.

if (string.IsNullOrEmpty(buttonText.text))
{
	buttonText.text = ">>>";            
	var objectWithText = target as IObjectWithText;
	if (objectWithText != null)
        {
            	buttonText.text = objectWithText.Text;
        }
}

One thing left to do in this script: We need to define what should happen when a button gets clicked. Do you remember what we did before, for our linear dialogue solution?

We told the Flow Player to get going again. Exactly this is what we are going to do within the OnBranchSelected method as well, with one added detail. For the linear dialogue it was absolutely sufficient to start up the Flow Player again with flowPlayer.Play(), without passing any argument. Without an argument the Flow Player always chooses the first branch, which in a linear flow is fine, because there is only one branch. But now we need to tell the Flow Player which route it needs to take, therefore we pass the argument branch.

public void OnBranchSelected()
{
	flowPlayer.Play(branch);
}

We are almost ready to test our solution, just two small steps to go. First we switch into the DialogueManager script. Here we already create the buttons for all current branches, what we still need to do is provide them with the functionality we just added in the BranchChoice script. In the OnBranchesUpdated method within the second foreach loop we add one line of code: We get the component BranchChoice for the button object and call the AssignBranch method from it. As arguments, we pass the flowPlayer and the current branch.

if (!dialogueIsFinished)
{
	foreach (var branch in aBranches)
	{
        	GameObject btn = Instantiate(branchPrefab, branchLayoutPanel);
		btn.GetComponent<BranchChoice>().AssignBranch(flowPlayer, branch);
	}
}

Now we go to the Unity editor and return to the dialogueBranch prefab. In here we go to On Click and select dialogueBranch as the referenced object. In the function drop down select BranchChoice and then choose OnBranchSelected. Now we have enough functionality up and running that we can do another test.

Selecting target for On Click on dialogueBranch prefab

I go straight for the red NPC with the branching dialogue. Very curious what will happen. Buttons display properly and for choices we get presented the menu text as button text. After selecting an option we see the complete dialogue line. To just move the dialogue along, we see the continue symbol we opted for.

Menu text is shown in navigational buttons at branching points

Continue symbol is shown for linear parts in dialogue

Only thing missing is the button to end the dialogue, but it will not be missing for much longer, as we will take care of it right away! In the DialogueManager script we add a GameObject called closePrefab, and open it to the editor via the SerializeField attribute.

[SerializeField]
GameObject closePrefab;

In the Unity editor we fill this reference with the dialogueBranchClose prefab. Again, please make sure you are using the prefab, otherwise it will not work later.

Filling reference with dialogueBranchClose prefab

Back in the DialogueManager script we are basically going to do the same thing as before with the navigational button, only this one gets another listener. In OnBranchesUpdated we add an else clause, we are now covering the case when the dialogue is over. We can copy the code line regarding the button instantiation from above, only thing we need to change is that we instantiate closePrefab instead of branchPrefab. With that we create the close button, now we only need to give it functionality by adding a listener to on click. We declare a variable btnComp and initialize it to getting the Button component of btn. Then we add a listener to on click for btn component and call CloseDialogueBox.

else
{
	GameObject btn = Instantiate(closePrefab, branchLayoutPanel);  
	var btnComp = btn.GetComponent<Button>();
	btnComp.onClick.AddListener(CloseDialogueBox);
}

Let’s run a test in Unity. The linear test dialogue is displayed correctly and ends with the deactivation of the dialogue UI. Same for the branching test.

Close button now appears and is fuctional

Recap lesson 6

Awesome! With that we have arrived at the end of this lesson and almost at the end of this tutorial series.We added text to choice buttons and implemented button functionality to navigate and close dialogues. We can now start, display, and end linear and branching dialogues.

Recap lesson 6

Next lesson

In the next lesson, which will be the final one in this series, we will learn what is necessary to handle more complex dialogues, which use some scripting logic and global variables. Then we conclude the series with some tips on how you can continue building upon this foundation we have created for the articy Importer for Unity.

What's next?

See you there!

Current state of C# scripts we have worked on this lesson:

BranchChoice.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Articy.Unity;
using Articy.Unity.Interfaces;
using UnityEngine.UI;

public class BranchChoice : MonoBehaviour
{
    private Branch branch;
    private ArticyFlowPlayer flowPlayer;
    [SerializeField]
    Text buttonText;

    // Called when a button is created to represent a single branch
    public void AssignBranch(ArticyFlowPlayer aFlowPlayer, Branch aBranch)
    {
        branch = aBranch;
        flowPlayer = aFlowPlayer;
        IFlowObject target = aBranch.Target;
        buttonText.text = string.Empty;

        // Check if the object has a "MenuText" property.
        // If yes, set the button text to that "MenuText"
        var objectWithMenuText = target as IObjectWithMenuText;
        if (objectWithMenuText != null)
        {
            buttonText.text = objectWithMenuText.MenuText;
        }

        // If there is no text from a "MenuText" property set,
        // we place ">>>" on the button as a "Continue" symbol 
        if (string.IsNullOrEmpty(buttonText.text))
        {
            buttonText.text = ">>>";
        }
    }

    // What happens when button is clicked  
    public void OnBranchSelected()
    {
        flowPlayer.Play(branch);
    }
}

DialogueManager.cs

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Articy.Unity;
using Articy.Unity.Interfaces;
using Articy.UnityImporterTutorial;

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 button layout
    [SerializeField]
    RectTransform branchLayoutPanel;
    // Reference to navigation button prefab
    [SerializeField]
    GameObject branchPrefab;
    // Reference to close button prefab
    [SerializeField]
    GameObject closePrefab;


    // To check if we are currently showing the dialog ui interface
    public bool DialogueActive { get; set; }

    private ArticyFlowPlayer flowPlayer;

    void Start()
    {
        flowPlayer = GetComponent<ArticyFlowPlayer>();        
    }

    public void StartDialogue(IArticyObject aObject)
    {
        DialogueActive = true;
        dialogueWidget.SetActive(DialogueActive);
        flowPlayer.StartOn = aObject;               
    }

    public void CloseDialogueBox()
    {
        DialogueActive = false;
        dialogueWidget.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)
    {
        // Destroy buttons from previous use, will create new ones here
        ClearAllBranches();

        // 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 we have branches, create a button for each of them 
            foreach (var branch in aBranches)
            {
                // Instantiate a button in the Dialogue UI
                GameObject btn = Instantiate(branchPrefab, branchLayoutPanel);
                // Let the BranchChoice component fill the button content 
                btn.GetComponent<BranchChoice>().AssignBranch(flowPlayer, branch);
            }
        }
        else
        {
            // Dialogue is finished, instantiate a close button
            GameObject btn = Instantiate(closePrefab, branchLayoutPanel);
            // Clicking this button will close the Dialogue UI
            var btnComp = btn.GetComponent<Button>();
            btnComp.onClick.AddListener(CloseDialogueBox);
        }
    }

    // Delete buttons from previous branches
    void ClearAllBranches()
    {
        foreach (Transform child in branchLayoutPanel)
        {
            Destroy(child.gameObject);
        }
    }
}

GO TO LESSON 7

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 to keep yourself up to date and informed.