I'm fairly new to coding (been learning for like the past 4 months) and I've been making a 2D point and click game with an RPG-like dialogue system in Unity, and the dialogue works well for the most part except for one thing: whenever I go from the dialogue of one object to another, if I change too fast between them, strange characters start appearing and the text is butchered.
Here is an example of what I mean: https://media.giphy.com/media/4QsoxLSInXN0vKciaW/giphy.gif. .The text is in Spanish, but I think anyone can see where the text isn't behaving like it should (there's a weird amalgamation of symbols and letters at random). The first time I opened both dialogues (the first dialogue is in the drawer object and the second is in the lamp object) they were fine, but the second time, I opened the dialogue in the lamp object too fast and made the text look weird.
The dialogue system for my code is based in the one displayed in the youtube channel Brackeys: https://www.youtube.com/watch?v=_nRzoTzeyxU. I modified this script so it could adapt to my game, but it was mostly so I could trigger the dialogue with UI buttons and to add a Close button (this is why some of the public variables may seem unnecessary but I use them in other UI buttons).
Here is the main code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class DialogueManager : MonoBehaviour
{
public Text nameText;
public Text dialogueText;
public GameObject gameObject;
public float delay = 0.5f;
public PlayerController playerController;
public Toggle myToggle;
public GameObject endObject;
private Queue<string> sentences;
// Start is called before the first frame update
void Start()
{
sentences = new Queue<string>();
}
public void StartDialogue(Dialogue dialogue){
playerController.enabled = false;
nameText.text = dialogue.name;
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 new WaitForSeconds(delay);
}
}
void EndDialogue(){
myToggle.isOn = false;
endObject.SetActive(false);
playerController.enabled = true;
gameObject.SetActive(false);
}
}
It uses a public class that contains a Dialogue named "dialogue":
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
[System.Serializable]
public class Dialogue
{
public string name;
[TextArea(3, 10)]
public string[] sentences;
}
It also uses a Trigger function that is called with a button.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DialogueTrigger : MonoBehaviour
{
public Dialogue dialogue;
public DialogueManager dialogueManager;
public void TriggerDialogue(){
dialogueManager.StartDialogue(dialogue);
}
}
I'm really sorry if I'm not explaining myself well, like I said I'm kind of new to this and still learning, so if you don't understand what I'm asking or you want me to clarify something just ask me so we can understand each other better. Thanks in advance to anyone that is willing to help! :)
CodePudding user response:
StopCoroutine is rather unreliable, you can make a boolean check to see if the dialogue is running for example private bool DialogueRunning
and set it to true inside
IEnumerator TypeSentence(string sentence){
dialogueText.text = "";
DialogueRunning = true;
foreach(char letter in sentence.ToCharArray()){
dialogueText.text = letter;
yield return new WaitForSeconds(delay);
}
DialogueRunning = false;
}
Then when you try to EndDialogue() check first if the dialogue is completed and return if it isnt. Otherwise you can set your delay
to 0 to instantly type out the text and then end the dialogue. This will ensure the coroutine doesn't get called multiple times.
void EndDialogue(){
if (DialogueRunning) return;
myToggle.isOn = false;
endObject.SetActive(false);
playerController.enabled = true;
gameObject.SetActive(false);
}
The problem is you have multiple objects accessing your text box
, but only one text box to pass the values in. When a coroutine is started whilst the other isn't finished, both of the Coroutines
pass values to the text box and you get this messy gibberish instead of the sentences.
Otherwise you can also use async to await the task completion, which makes it a bit cleaner than coroutines