yield return new WaitForSeconds(2) destroys functi

2019-07-15 10:51发布

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?

3条回答
乱世女痞
2楼-- · 2019-07-15 11:37

You need to start the coroutine by using StartCoroutine:

StartCoroutine(FlipCardFaceUp(card));

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 use yield, too:

Debug.Log("Call FlipCard");
yield return StartCoroutine(FlipCardFaceUp(card));
Debug.Log("Returned from FlipCard");

Note that you can't use yield from within Update or FixedUpdate.

EDIT: Seeing that you are trying to do this in the OnGUI method, a better way might be to just use a variable of type Card there so that you can remember which card to flip. Then, in Update, you can check that variable, and if it says that a card needs to be flipped, you can start the coroutine.

查看更多
老娘就宠你
3楼-- · 2019-07-15 11:38

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:

foreach(var obj in FlipCardFaceUp(card)) {}

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:

// the following is COMPLETE GUESSWORK - I've never coded unity
// update: wrong: see Botz3000's answer, and `StartCoroutine`
Card card = grid[i, j];

if (GUILayout.Button(new GUIContent((Texture) Resources.Load(card.getImg()), ""), GUILayout.Width(cardWidth))) {
  foreach(var step in FlipCardFaceUp(card)) yield return step;
}
查看更多
相关推荐>>
4楼-- · 2019-07-15 11:45

You can only use yield return new WaitForSeconds(2) if you're calling your method through StartCoroutine. 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).

查看更多
登录 后发表回答