Reputation: 545
I am trying to program a very simple dialogue system. When a player gets a dialogue box, the box remains until they press a key. Then a second dialogue box appears. Again, they press a key, and so on.
I am realizing, however, that my method of using coroutines doesn't work, because every dialogue box displays at once. Using a simplified version:
IEnumerator Test() {
//Code that displays a message
yield return StartCoroutine(WaitForKey(KeyCode.Space));
}
IEnumerator WaitForKey(KeyCode keyCode)
{
while (!Input.GetKeyDown(keyCode))
yield return null;
}
void start(){
StartCoroutine(Test());
StartCoroutine(Test());
}
The results of the above code is that two messages are displayed. Plus, they display immediately--once a condition is met, the process jumps from that courtine to the initiating, runs the next line of code, and periodically returns to the first coroutine to see if it has finished.
How do I get one coroutine to finish before it continues with the rest of the code following it?
Upvotes: 2
Views: 4535
Reputation: 125305
The easy way to go about this is to creates arrays of Canvas as dialog. Disable all of them in the Editor. With the code below, you can change the array size of the dialogueGameObjectCanvas to how many dialogues you have then assign the parent of each dialogues in order to the dialogueGameObjectCanvas array in the Editor. Cancas must be the parent of each dialogue. Run it.
Pressing the space bar will go to the next dialogue. This is the easiest way.
Now, for the pro way. If the dialogue is generated during run time, you can use List to add or remove dialog(GameObjectUICanvas)instead of using array to do it. You can easily convert array to List and that should work for dynamically generated dialogues.
startDisplayDialogue()
starts the dialogue.
isDialogueDisplaying()
tells you if the dialogue is currently displayed on the screen.
getDialogueCurrentNumberDisplaying()
gets the current array number of dialogue disaplying
stopDisplayDialogue()
kills the whole dialogue and closes them.
Remember that startDisplayDialogue()
is coroutine function and must be started with StartCoroutine (startDisplayDialogue ());
public GameObject[]dialogueGameObjectCanvas;
bool isRunning = false;
int dialogNumberDisplaying = 0;
// Use this for initialization
void Start ()
{
//animationSprite = new GameObject[5]; Uncomment if you want to assign through code, otherwise assign the dialogues from the Editor
StartCoroutine (startDisplayDialogue ());
}
//Display the first dialogue then wait for the space to be pressed then display the next dialue
IEnumerator startDisplayDialogue ()
{
//Make sure there is only one instance of the dialuge functionr running. Break if there is alread one running
if (isRunning) {
yield break;
} else {
isRunning = true;
}
int dialogueNumber = 0; //Starts from 0 to the size of the animationSprite array
dialogNumberDisplaying = dialogueNumber;
while (isRunning) {
//Stop if the dialogNumber is >= number of dialogues to dispaly(dialogue arrays)
if (dialogueNumber >= dialogueGameObjectCanvas.Length) {
isRunning = false;
//dialogueGameObjectCanvas [dialogueNumber].SetActive (false);
Debug.Log ("End of dialogue");
yield break;
}
//Dispaly the current Dialogue based on the array number
dialogueGameObjectCanvas [dialogueNumber].SetActive (true);
//Wait until the Space bar is pressed
while (!Input.GetKeyDown(KeyCode.Space)) {
yield return null; //Must wait here or else Unity would freeze
}
//Space bar has been pressed. Disable the current Dialogue then increement dialogueNumber so that then the next one in the array will display
dialogueGameObjectCanvas [dialogueNumber].SetActive (false);
dialogueNumber++;
dialogNumberDisplaying = dialogueNumber; //For the getDialogueCurrentNumberDisplaying function
//Check if stopDisplayDialogue is called then exit
if (!isRunning) {
dialogueGameObjectCanvas [dialogueNumber].SetActive (false);
yield break;
}
yield return null;
}
}
//Check if a dialogue is currently displaying
bool isDialogueDisplaying ()
{
return isRunning;
}
//Get the current array number of dialogue currently displaying
int getDialogueCurrentNumberDisplaying ()
{
return dialogNumberDisplaying;
}
//Stops the dialogue from displaying
void stopDisplayDialogue ()
{
isRunning = false;
}
Upvotes: 2
Reputation: 976
Your issue is that you are calling StartCoroutine(Test())
twice in the same frame, only when you use
yield return StartCouroutine(Test());
will the next line wait until the coroutine finishes to be executed.
So right now your start function will run the Test()
functions until it gets it first yield return X, and then it will go back to the second line of the Start function and do the same thing. Then in the next frame (or whenever your yield return X finishes) it will continue executing those routines.
To solve your problem you could do a while(true)
in your Test function if i understand what you are doing. However, if you post more detailed code I can give you a better suggestion as to why your approach isnt working the way you expect!
Upvotes: 1