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.

Recap lesson 3

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.

Content lesson 4

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.

Linear test dialogue in articy:draft

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).

The Dialogue Widget

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.

Creating the reference for the dialogueButton

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.

Going through the linear test dialogue

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;

Creating the reference for the 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.

Close button to end dialogue

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.

Close button covers Continue button

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.

Setting dialogueBranch and dialogueBranchClose objects to inactive

Only relevant button is shown in dialogue UI

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.

Recap lesson 4

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.

What's next?

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);
        }
    }
}

GO TO LESSON 5

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.