Queue not running through all items?

Hi everyone!

I am working on creating a simple dialogue system for a game, using some tutorials as guidance. I currently have a Queue system built for showing text, but for some reason all the sentences don’t get run through, just the first one. I’m not sure where the problem lies as I am not 100% well-versed in Queues (just started learning about them). If anyone can help that would be great!

Here is the DialogManager,

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class DialogManager : MonoBehaviour
{

    public Text nameText;
    public Text dialogText;
    //create a queue to load sentences in queue order (FIFO)
    private Queue<string> sentences;
   
    // Start is called before the first frame update
    void Start()
    {
        sentences = new Queue<string>();
    }

    public void StartDialog (Dialog dialog)
    {
        nameText.text = dialog.name;
       
        sentences.Clear(); //clear any sentences in the queue

        foreach (string sentence in dialog.sentences) //for each sentence, enqueue it
        {
            sentences.Enqueue(sentence);
        }

        DisplayNextSentence(); //then run the display method

    }

    public void DisplayNextSentence ()
    {
        if(sentences.Count ==0) //if no more sentences
        {
            EndDialog(); //end the dialog
            return;
        }

        string sentence = sentences.Dequeue(); //otherwise, dequeue the sentences (FIFO order)
        dialogText.text = sentence;
    }

    void EndDialog()
    {

    }
}

Here is the Dialog class:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[System.Serializable]
public class Dialog
{
    //this passes to DIalogManager whenever we start a new dialog
    //has all info we need for a dialog

    public string name;
   
    [TextArea(1, 10)] //specifies minimum and maximum amount of lines the text uses
    public string[] sentences;

}

And here is the Trigger script. The eventual idea is to have a player enter a trigger collider, hit space, and see the text, though I haven’t created the collider or anything yet and was simply testing just activating via spacebar when I discovered it not working correctly.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class DialogTrigger : MonoBehaviour
{
    // Put this on an object that will trigger dialog (NPC, button, etc)
    public Dialog dialog;
    private bool canActivate = true;


    private void Update()
    {
       if(canActivate && Input.GetKeyDown("space"))
        {
            TriggerDialog();
        }

    }
    public void TriggerDialog()
    {
        FindObjectOfType<DialogManager>().StartDialog(dialog);
    }
}

Please let me know if I need to provide any more information!

I only see you calling DisplayNextSentence() once, in StartDialog(). Is there somewhere else you’re calling it to try to go through all the sentences? Otherwise it seems like you’re just calling StartDialog() every time you hit space, which will restart the dialog from the beginning each time.

1 Like

No, it’s not being called anywhere else.

So if space is simply starting the dialog over each time, how can I get the script to run through each sentence in the dialog? For example, if I have three sentences that need to be run through before it ends?

In the tutorial I followed for most of this from Brackeys (

), he creates a button that can progress the text. However, since I am coding a keycode, that is probably where I am not clear on how the interaction needs to be working.

If you want to use the same button for starting the dialog as continuing the already started dialog dialog, you will need to add some logic when the user hits the space bar. Something like this:

  • If there is no dialog currently in progress, call StartDialog()
  • If there is a dialog currently in progress, call DisplayNextSentence()
1 Like

Hm, okay that makes sense! Thank you for the insight. So I accomplished this by changing the DialogTrigger to the following code and it works!

public class DialogTrigger : MonoBehaviour
{
    // Put this on an object that will trigger dialog (NPC, button, etc)
    public Dialog dialog;
    private bool canActivate = true;
    private bool dialogStarted = false;


    private void Update()
    {
       if(canActivate && Input.GetKeyDown("space"))
            if(dialogStarted)
            {
                ContinueDialog();
            }
            else
            {
                BeginDialog();
            }
    }
    public void BeginDialog()
    {
        dialogStarted = true;
        FindObjectOfType<DialogManager>().StartDialog(dialog);
    }

    public void ContinueDialog()
    {
       
        FindObjectOfType<DialogManager>().DisplayNextSentence();
    }
}

Does this seem like the best way to make this work or is there a more streamlined way I could use?

1 Like

That seems pretty good but you should think about what happens when the dialog is over. Do you want the player to be able to run back through the dialog again? They won’t be able to with the current code because dialogStarted, once set to true, will never be set back to false.

1 Like

Ah of course, that makes total sense. So I created

public DialogTrigger dialogTrigger;

in the DialogManager and then dragged my gameobject to it. Then I added to the DisplayNextSentence so the text can begin again:

public void DisplayNextSentence ()
    {
        if(sentences.Count ==0) //if no more sentences
        {
            dialogTrigger.dialogStarted = false;
            EndDialog(); //end the dialog
            return;
        }

        string sentence = sentences.Dequeue(); //otherwise, dequeue the sentences (FIFO order)
        dialogText.text = sentence;
    }

It seems to be working! Does this seem like the correct approach?

This will work but I’m not sure it’s the best because now both objects need references to each other. What if you made DisplayNextSentence return a bool variable indicating if the dialog was finished or not? Then you could check that return value in DialogTrigger and set dialogStarted to false if the dialog was finished.

1 Like

Hm, okay so I see what you are saying. That way, we only are referencing from one script to another instead of to each other. However, here is what the scripts look like now: (I have added animations since starting this thread, so ignore that unless its relevant)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class DialogManager : MonoBehaviour
{

    public bool dialogFinished;
    public Text nameText;
    public Text dialogText;

    public Animator animator;
    //create a queue to load sentences in queue order (FIFO)
    private Queue<string> sentences;
   
    // Start is called before the first frame update
    void Start()
    {
        sentences = new Queue<string>();
    }

    public void StartDialog (Dialog dialog)
    {
        animator.SetBool("isOpen", true);
       
        nameText.text = dialog.name;
       
        sentences.Clear(); //clear any sentences in the queue

        foreach (string sentence in dialog.sentences) //for each sentence, enqueue it
        {
            sentences.Enqueue(sentence);
        }

        DisplayNextSentence(); //then run the display method

    }

    public void DisplayNextSentence ()
    {
        if(sentences.Count ==0) //if no more sentences
        {
            dialogFinished = true;
            EndDialog(); //end the dialog
            return;
        }

        string sentence = sentences.Dequeue(); //otherwise, dequeue the sentences (FIFO order)
        dialogText.text = sentence;
    }

    void EndDialog()
    {
        animator.SetBool("isOpen", false);
    }
}

and:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class DialogTrigger : MonoBehaviour
{
    // Put this on an object that will trigger dialog (NPC, button, etc)
    public DialogManager dialogManager;
    public bool canActivate = true;
    public bool dialogStarted = false;


    private void Update()
    {
       if(canActivate && Input.GetKeyDown("space"))
            if(!dialogManager.dialogFinished)
            {
                ContinueDialog();
            }
            else
            {
                BeginDialog();
            }
    }
    public void BeginDialog()
    {
        dialogManager.dialogFinished = false;
        FindObjectOfType<DialogManager>().StartDialog(dialog);
    }

    public void ContinueDialog()
    {
       
        FindObjectOfType<DialogManager>().DisplayNextSentence();
    }
}

However, I now get the error: Assets\Scripts\DialogTrigger.cs(28,55): error CS0103: The name ‘dialog’ does not exist in the current context

I’m not sure why this is happening, when it was fine before. Did I mess something up without realizing it?