I decided to create a little memory like game, to learn game development with unity3d.
The game should wait 2 seconds after a player clicked to cards, before it flips the cards back.
The yield return new WaitForSeconds(2)
-statement should be perfect for this, but it has the effect, that no line of the function is executed.
Here is my code:
This builds the card grid (with buttons) and calls a function to flip the card, when the card is clicked.
Card card = grid[i, j];
if (GUILayout.Button(new GUIContent((Texture) Resources.Load(card.getImg()), ""), GUILayout.Width(cardWidth))) {
Debug.Log("Call FlipCard");
FlipCardFaceUp(card);
Debug.Log("Returned from FlipCard");
}
This is the flip function:
System.Collections.IEnumerable FlipCardFaceUp(Card card) {
Debug.Log("This isn't shown in the console");
card.isFaceUp = true;
if (!cardsFlipped.Contains(card)) {
cardsFlipped.Add(card);
if (cardsFlipped.Count >= 2) {
playerCanClick = false;
//Waiting 2 seconds before the cards are flipped back or are removed
yield return new WaitForSeconds(2);
if (cardsFlipped[0].id == cardsFlipped[1].id) {
cardsFlipped[0].isMatched = true;
cardsFlipped[1].isMatched = true;
} else {
cardsFlipped[0].isFaceUp = false;
cardsFlipped[1].isFaceUp = false;
}
cardsFlipped.Clear();
playerCanClick = true;
}
}
}
This is the console output, when I test my game and click on a card:
Call FlipCard
Returned from FlipCard
If I remove the stuff that is needed for the yield return
, works everything fine (except that the player can't see the second card, because it is flipped back immediately).
What's wrong with my yield return?
You need to start the coroutine by using
StartCoroutine
:As Marc Gravell said, an iterator block needs to be enumerated to do anything, and that's what
StartCoroutine
does (asynchronously, and probably other stuff needed by unity). The "asynchronously" part is also why"Returned from FlipCard"
is immediately printed before the Coroutine has finished.If you want to wait for your Coroutine to finish before printing
"Return from FlipCard"
, you can useyield
, too:Note that you can't use
yield
from withinUpdate
orFixedUpdate
.EDIT: Seeing that you are trying to do this in the
OnGUI
method, a better way might be to just use a variable of typeCard
there so that you can remember which card to flip. Then, inUpdate
, you can check that variable, and if it says that a card needs to be flipped, you can start the coroutine.By adding
yield return
, you have made it an iterator block. Iterator blocks are only executed when they are iterated - they demonstrate "deferred execution". So for this to work you would need to consume the enumeration returned. At the simplest, this could be:However, there may be some other way that unity expects this to be consumed as a co-routine update: indeed; see Botz3000's answer. In most UIs, blocking in the update code is bad (it prevents the draw), but without unity knowledge I can't advise on the right implementation. For all I know, it might want you to turn the calling code into an iterator block:
You can only use
yield return new WaitForSeconds(2)
if you're calling your method throughStartCoroutine
. Your first block of code will return immediately, but Unity3d will then take care of running the rest of your method (including the 2 second wait and continuing afterward).