Scripting tutorial

Here’s a basic setup tutorial that will help you get started and understand how VIDE works code-wise. Some Unity and scripting knowledge is required, but I’ll try to make it as noob-proof as possible.


VIDE doesn’t include any fancy, built-in UI Manager. It includes multiple demos, but, ideally, the plugin is designed to be flexible, so that you can create your own manager covering your own project needs and have freedom over the handling of data you set in the VIDE Editor. If you’re new to programming or Unity or both, you might find yourself scratching your head as you try to understand how to actually use the plugin. If that’s so, please read ahead.

Setting things up

I will assume that you already know how to use the VIDE Editor since that’s the easy part and it is covered in the Documentation. For this tutorial, I’ve created my short custom dialogue with both Player and NPC-formatted nodes:

diag1
Notice my Start Node ID is set to 0 because I want my dialogue to begin at node 0.

So, my short test dialogue is ready. What now?

We are going to create a simple UI interface in an empty scene. Later, we’re going to begin the dialogue when we press a key.

Before we get to coding, we need to setup the UI objects in our scene that our script will be referencing. You can absolutely use the GUI class and keep yourself from using the UI system, but for this tutorial, we will setup everything inside our Canvas using the UI system.

To quickly add the game objects with UI components, right-click in the empty space inside the Hierarchy window and select, for example, Text. A Canvas and Event System will be created along with it. 

For the Player text, let’s create 3 Button game objects and put them inside an empty game object named “Container_Player”. For the NPC text, let’s create a Text game object, then put it inside an empty game object I named “Container_NPC”. You can resize, format, and reposition them as desired.

setup1

Good; now we can send our node data somewhere. Next, let’s add a new empty game object that will handle dialogues. I named it “UIManager”, but you can name it whatever you want.

For demo purposes, we will add a VIDE_Assign component to the game object. With it, you can set per-component settings for the assigned dialogue. This component will then be used to begin the dialogue. Let’s assign the dialogue we created.

assign
If you were to have many NPCs to interact with, each one would have their own VIDE_Assign.

Lastly, let’s add a script that will handle our node data. Click ‘Add Component’ > New Script. We’ll use C# for this tutorial. Name it “UIManager”.

scr

All good to go, for now.

Programming our UIManager

The VIDE_Data namespace encapsulates two classes: VD & VD2. Either class will load, manage, and pack the node data retrieved from the dialogues. They own various methods and variables that will let you obtain node data in an orderly fashion. While VD has static members, VD2 doesn’t and can be used for instancing. In this tutorial, we’ll be using the VD class that can be easily accessed and used.

Open the newly created UIManager script!

Let’s add VIDE_Data namespace and UnityEngine.UI namespace to gain easy access to their classes (I suggest you type stuff yourself rather than copy-pasting):

code1
using VIDE_Data and UnityEngine.UI

Let’s add the references to the UI game objects we created earlier:

code2
All we need for now

Save your script, go back to Unity, and drag and drop the corresponding game objects to their slot:

code3

So, we have our variables pointing to our UI. We now need to pass data to these variables. The first thing we would like to do for this tutorial is to begin the dialogue when you press a key if there isn’t one already active. A dialogue becomes active when you call VD.BeginDialogue(). To check if there’s an active dialogue, you can use the variable VD.isActive.

So, let’s make it so that the script calls a custom Begin() method when the Return key is pressed, but only if there isn’t a dialogue active. The input check should happen inside the Update void.

code3

When implementing VIDE, you need to have a grasp on the main 3 methods you’ll find inside the VD and VD2 classes:

VD.BeginDialogue() – Activates the dialogue. Loads up the node data of the start node.
VD.Next() – Gets the node data of the next node in the chain. If we’re currently on an NPC node, then we’ll advance through the comments before going to the next connected node. For Player nodes, where it goes next will depend on a variable.
VD.EndDialogue() – Cleans all temp data and deactivates the dialogue.

BeginDialogue and Next will be constantly updating a variable called nodeData inside VD and VD2. VD.nodeData is a variable of type NodeData which contains.. you guessed it: node data.

code4

These are the variables you’ll be using to decorate your UI! All you have to do is check the current contents of VD.nodeData or get the return value from the above methods, which is a NodeData variable.

What we want, then, is to get the node data of our first node, so let’s call BeginDialogue first. In order for VD to know what dialogue we want to use, we send the VIDE_Assign when calling the method:

code5

To make it easier for you to handle node data changes, VD & VD2 offer a few events like OnNodeChange and OnEnd. These two will help you take action when the node data changes or when we have reached the end of the conversation. The events require your methods to have one parameter of type VD.NodeData; a variable of said type will be sent when the events fire.

Let’s add a couple of new custom methods and subscribe them to the events before we call BeginDialogue:

code6
When a node data change occurs, UpdateUI will be called while receiving the new node data. If we reach the end, End will be called.

When subscribing to events, we also need to remember to unsubscribe. We’ll do this inside the End() method. Also, we will call VD.EndDialogues() to clear all node data and deactivate the dialogue:

code7
NodeData.isEnd and VD.OnEnd will only happen when you call Next() on a disconnected comment

Good! Though if something happened to this script and it got destroyed mid-dialogue, we might have to force-end it to prevent errors. Let’s make sure we end the dialogue if this script gets disabled. We’re not really using data so let’s send a null package. Also, let’s call End only if we have no null vars (Variables go null when exiting Play Mode in the Editor, and OnDisable is also called at that time).

code20.JPG

So far, so good. The basic structure is built:

 

UIManager.cs (Click to expand)
using UnityEngine;
using System.Collections;
using VIDE_Data; //Access VD class to retrieve node data
using UnityEngine.UI;

public class UIManager : MonoBehaviour {

    public GameObject container_NPC;
    public GameObject container_PLAYER;
    public Text text_NPC;
    public Text[] text_Choices;

    // Use this for initialization
    void Start () {

	}

	// Update is called once per frame
	void Update () {

        if (Input.GetKeyDown(KeyCode.Return))
        {
            if (!VD.isActive)
            {
                Begin();
            }
        }

	}

    void Begin()
    {
        VD.OnNodeChange += UpdateUI;
        VD.OnEnd += End;
        VD.BeginDialogue(GetComponent<VIDE_Assign>());
    }

    void UpdateUI(VD.NodeData data)
    {

    }

    void End(VD.NodeData data)
    {
        VD.OnNodeChange -= UpdateUI;
        VD.OnEnd -= End;
        VD.EndDialogue();
    }

    void OnDisable()
    {
        if (container_NPC != null)
            End(null);
    }
}

 

Right, so, the moment we call BeginDialogue(), the OnNodeChange event will fire, calling our UpdateUI method. We can either check the current VD.nodeData or simply use the variable we had sent to us along with the event. Said data will belong to that of our start node.

What we want to do is to apply some of the node data to our UI elements, but first, we need to know if the data belongs to an NPC node or a Player node, because we will handle data differently depending on it. How can we know this, then? We use the isPlayer variable inside NodeData:

code9.JPG

Before we apply the text to our UI elements, let’s disable both UI containers. Then, we’ll enable the corresponding one inside our statement:

code10.JPG

Good. Now, let’s go with NPC first. If our newly arrived data does not belong to a Player node, it means we’re on an NPC node. In that case, let’s use the comments array and commentIndex variables inside NodeData to set the text of our corresponding Text element.

code11.JPG

The commentIndex variable updates automatically for NPC nodes. When there are multiple comments in the node, commentIndex will increment every time you call Next(), and it will continue to do so until we reach the last one and move on to the next node.

If we are on a Player node, comments are treated as player choices. If we had only 2 comments, then we would only want to activate 2 out of the 3 buttons we have. To handle such a case, we use a for loop:

code12.JPG

So, we will loop through our text_Choices array which contains references to the Text elements of our buttons. If i is less than our comments array, then we will activate our button and set its text, otherwise, we’ll disable it. Note we are using the Text reference to access the parent game object; we want to disable/enable that one, not the “Text” game object.

By the way, let’s make sure we disable both containers at Start() and at End():

code13.JPG

code14.JPG

Good. You could now save, compile and hit Play, and you would be able to use the Return key to begin the dialogue. The NPC text would then be set to that of the start node’s first comment, but you won’t get any further. We are not yet giving the instruction to continue. Let’s do that.

We have the BeginDialogue() and EndDialogue() doing their job, we’re just missing the Next(). What we want to do is to call Next() if the dialogue is already active. We already have the statement for that, we just need the else:

code15.JPG

Every time Next() is called, NodeData will get updated and OnNodeChange event will fire, calling UpdateUI().

Again: If we are currently on an NPC node with multiple comments, calling Next() will simply increment NodeData.commentIndex up until we are at the last one and jump to the next node. How do we specify what’s our choice when on a Player node? Instead of incrementing commentIndex, Next() will use commentIndex to decide where to head to next. In our setup, setting the correct commentIndex is our UI buttons’ job!

The Button component can call custom methods when pressed. You just have to set them in the Inspector. Let’s first create the method our buttons will be calling when pressed. Let’s only call Next if the buttons are clicked with the left click.

code21.JPG

Save and go back to Unity. Select each “Button_Choice” and set the method we just created. Since we added an int parameter, the int field will show in the Inspector as a param. What we want is to type 0, 1, and 2 for each button, numbers that refer to comment indexes.

code19.JPG

Awesome! Now hit play and then hit Return to begin and progress through the dialogue. Click the buttons for player choices. It should be working well from start to finish.

ready1.gif

If you want to be able to pick a choice with the keyboard, then make sure you activate the Navigation on the Button components and select Vertical. Next, we want to have a button selected by default when they show up. Let’s do that right after we enable/disable the buttons.

navi.JPG

code18.JPG

The buttons will also call SetPlayerChoice when pressed, but the method will not fire the Next(), because our Update() void is already doing that.

That’s it. Now use the arrow keys and Return to pick a choice.

ready2.gif


That covers the main part of this tutorial. Remember that it is you who decides how to design your code and how to handle the data you receive. Just remember the main BeginDialogue-Next-EndDialogue process.

Some notes…

If you were a character interacting with an NPC, then our Begin() method would most likely be called by another script, and so would Next(). So, for instance, they’d have to be public methods. But, then again, it’s the same process. Example1 scene covers this!

You can optimize and add tons of data handlers to this UIManager script.

Audio: For sounds, just make sure you have an Audio Source component somewhere and pass it the clip found in the NodeData.audios array, use the commentIndex to pick the correct one when on NPC nodes.

Sprites: Similar process, though you can pick from NodeData.sprites, NodeData.sprite, or VIDE_Assign.defaultNPCSprite and VIDEAssign.defaultPlayerSprite. During an active dialogue, you can quickly access the active VIDE_Assign through VD.assigned.

Extra Data: Use it to pass in some extra data. You might even want to split and parse your string.

Extra Variables: Obtain your values by using your keys: extraVars[“myInt”]. The value is going to be of type object, you will have to cast it to the correct format:
int theInt = VD.nodeData.extraVars[“myInt”]; For JS, instead of casting, you call VD.ExtraVars.GetInt/GetBool/GetFloat/GetString. Check out Example 1 scene. 

But there are tons of other methods and variables that will give you much more control over the dialogues during runtime! Check out the Scripting API in the Documentation to know more about them! Make sure you also check out the FAQ!

Here’s the finished script (with added Touch functionality for devices):

UIManager.cs (Click to expand)
using UnityEngine;
using System.Collections;
using VIDE_Data; //Access VD class to retrieve node data
using UnityEngine.UI;

public class UIManager : MonoBehaviour
{
    public GameObject container_NPC;
    public GameObject container_PLAYER;
    public Text text_NPC;
    public Text[] text_Choices;

    // Use this for initialization
    void Start()
    {
        VD.LoadDialogues();
        container_NPC.SetActive(false);
        container_PLAYER.SetActive(false);
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Return) || Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Ended)
        {
            if (!VD.isActive)
            {
                Begin();
            }
            else
            {
                VD.Next();
            }
        }
    }

    void Begin()
    {
        VD.OnNodeChange += UpdateUI;
        VD.OnEnd += End;
        VD.BeginDialogue(GetComponent<VIDE_Assign>());
    }

    void UpdateUI(VD.NodeData data)
    {
        container_NPC.SetActive(false);
        container_PLAYER.SetActive(false);
        if (data.isPlayer)
        {
            container_PLAYER.SetActive(true);
            for (int i = 0; i < text_Choices.Length; i++)
            {
                if (i < data.comments.Length)
                {
                    text_Choices[i].transform.parent.gameObject.SetActive(true);
                    text_Choices[i].text = data.comments[i];
                }
                else
                {
                    text_Choices[i].transform.parent.gameObject.SetActive(false);
                }
            }
            text_Choices[0].transform.parent.GetComponent<Button>().Select();
        }
        else
        {
            container_NPC.SetActive(true);
            text_NPC.text = data.comments[data.commentIndex];
        }
    }

    void End(VD.NodeData data)
    {
        container_NPC.SetActive(false);
        container_PLAYER.SetActive(false);
        VD.OnNodeChange -= UpdateUI;
        VD.OnEnd -= End;
        VD.EndDialogue();
    }

    void OnDisable()
    {
        if (container_NPC != null)
            End(null);
    }

    public void SetPlayerChoice(int choice)
    {
        VD.nodeData.commentIndex = choice;
        if (Input.GetMouseButtonUp(0) && Input.touchCount < 0)
            VD.Next();
    }
}

 

If you have any questions, leave your comment down below!
Thanks for reading. 🙂

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s