Need some help creating a dialogue cutscene

Hello there, I hope I’m putting this in the right place. I’m trying to create a dialogue cutscene for my game and have been having some trouble. Basically I have a dialogue system created that I found on Brackys youtube channel, and added another script to it to serve my goal. What I want is for the character portraits to appear as each character talks eventually having all the characters on screen. For example, the first cutscene has 4 characters talking. The scene starts with no one and as you click through the dialogue each character’s portrait appears on the screen. I’m using the same 4 scripts in each scene however some scenes have more or less than 4 characters. I’m dropping the scripts so you can take a look. Is there another way I can approach this? Any help would be very appreciated thank you!

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

[System.Serializable]
public class Dialogue
{
    public string name;
    public Sprite mugshot;

    [TextArea(3, 10)]
    public string[] sentences;
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class DialogueTrigger : MonoBehaviour
{
    public Dialogue dialogue;

    public void TriggerDialogue()
    {
        FindObjectOfType<DialogueManager>().StartDialogue(dialogue);
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class DialogueManager : MonoBehaviour
{
    public bool sentenceEnded = false;

    public Text nameText;
    public Text dialogueText;

    public Image image1;
    public Image image2;
    public Image image3;
    public Image image4;

    private Queue<string> sentences;

    void Start()
    {
        sentences = new Queue<string>();
    }

    public void StartDialogue(Dialogue dialogue)
    {
        sentenceEnded = false;
        nameText.text = dialogue.name;

        if(image1.sprite == null)
        {
            image1.gameObject.SetActive(true);
            image1.sprite = dialogue.mugshot;
        }
        else if(image2.sprite == null)
        {
            image2.gameObject.SetActive(true);
            image2.sprite = dialogue.mugshot;
        }
        else if (image3.sprite == null)
        {
            image3.gameObject.SetActive(true);
            image3.sprite = dialogue.mugshot;
        }
        else if (image4.sprite == null)
        {
            image4.gameObject.SetActive(true);
            image4.sprite = dialogue.mugshot;
        }

        sentences.Clear();

        foreach(string sentence in dialogue.sentences)
        {
            sentences.Enqueue(sentence);
        }

        DisplayNextSentence();
    }

    public void DisplayNextSentence()
    {
        if(sentences.Count == 0)
        {
            EndDialogue();
            return;
        }

        string sentence = sentences.Dequeue();
        StopAllCoroutines();
        StartCoroutine(TypeSentence(sentence));
    }

    IEnumerator TypeSentence (string sentence)
    {
        dialogueText.text = "";
        foreach (char letter in sentence.ToCharArray())
        {
            dialogueText.text += letter;
            yield return null;
        }
    }

    void EndDialogue()
    {
        sentenceEnded = true;
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

public class PrologueDialogue : MonoBehaviour
{
    public DialogueManager dm;

    public GameObject startDialogue;

    public List<GameObject> dialogue = new List<GameObject>();
    public GameObject activeDialogue;

    public int dialogueNumber = 1;

    static int currentScene = 1;

    void Start()
    {
        startDialogue.GetComponent<DialogueTrigger>().TriggerDialogue();

        GameObject[] allDialogue = GameObject.FindGameObjectsWithTag("Dialogue");
        dialogue.AddRange(allDialogue);

        activeDialogue = dialogue[dialogueNumber];
    }

    void Update()
    {
        if(dialogue.Count > 0)
        {
            activeDialogue = dialogue[0];
        }

        if (dm.sentenceEnded == true)
        {
            activeDialogue.GetComponent<DialogueTrigger>().TriggerDialogue();
            dialogue.RemoveAt(0);   
        }
    }

    public void NextScene()
    {
        if(dialogue.Count == 0)
        {
            currentScene++;
            SceneManager.LoadScene("Cutscene" + (currentScene).ToString());
        }
    }
}

This is one of those kinds of things that looks easy to implement at first, but really isn’t … you have to figure out a way to parse text/character/other events and data, handle input, instantiate objects, move objects, change scenes etc etc.

I guess it depends what your needs are. If you’re making the next best-selling visual novel then I would spend a little bit of time creating some kind of DialogueManager, with functions that easily handle the logic like AddSpeech(string text), AddCharacter(Texture2D charactersprite, Vector2 position), ClearScene() etc (tbh I’m sure someone somewhere probably already has a plugin that does all this already). Then I would create some arrays that hold my speech and events etc.

But if you’re just looking to make something relatively straightforward then I would just make a massive switch() statement for each scene or whatever. E.g:

 void ProgressScene()
        {
            scenepos++;

            switch (scenepos)
            {
                case 1:
                    LoadCharacter(SexyGabeNewell, new Vector2(100f, 500f));
                    AddSpeechBubble("Hi, it's me, Gabe Newell.", new Vector2(120f, 400f));
                    WaitForSeconds(10f); // waiting (pseudocode but u get the idea)
                    break;
                case 2:
                    RemoveAllSpeechBubbles();
                    AddSpeechBubble("Now, I know what you're thinking - Gabe Newell in a Unity game?", new Vector2(120f, 400f));
                    while(!GetKeyDown("Mouse1")) {} // (((pseudocode this is probably not the right function name lol))) wait for a click to progress to next scene ... although this might crash the game a little bit ... maybe you could make this function an IEnumerator and yield return it instead
                    break;
                case 3:
                    RemoveAllSpeechBubbles();
                    LoadCharacter(WorriedGMan, new Vector2(900f, 500f));
                    AddSpeechBubble("The wrong engine in the right place ... can make all the difference in the world.", new Vector2(920f, 400f));
                    break;
                case 4:
                    EndScene();
            }
        }
}

That’s just how I would do it (assuming I didn’t need some insanely flexible and dynamic system with a million features). Simple… inelegant … messy… but effective.

I can’t really comment on whether your code will achieve what you want since I don’t know what kind of thing you’re going for and I’d be here a long time if I went through it line by line. But these are just some ideas :stuck_out_tongue: Personally I think that you don’t really need to split it into four separate code files. Just one should be able to do it for you (unless you’re going the more complicated and more effort route - nothing wrong with that). Note I would change this part slightly:

IEnumerator TypeSentence (string sentence)
    {
        dialogueText.text = "";
        foreach (char letter in sentence.ToCharArray())
        {
            dialogueText.text += letter;
            yield return new WaitForSeconds(0.05f); // CHANGED THIS LINE
        }
    }

That will make it so the text types consistantly slow/fast, rather than every frame (which would probably be too fast).

I haven’t looked at your code for long, but I thought you will do a cutscene and this cutscene is not interactable? So you can produce a video-cutscene and load it, where you need it.

Otherwise, switch/case looks possible, you can use xml for it or you write a prefab where you can throw in your images/videos/texts/sounds/…, but this needs to be planned and maybe isn’t a easy solution. :slight_smile: