質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.96%

while文の中のWaitForEndOfFrameの理解

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 2,447

GeeChiki

score 10

イメージ説明unity公式の2Dshootingチュートリアルの敵集団をwaveを作る仕組みの中で、下記スクリプトが出てきました。

using UnityEngine;
using System.Collections;

public class Emitter : MonoBehaviour
{
    // Waveプレハブを格納する
    public GameObject[] waves;

    // 現在のWave
    private int currentWave;

    IEnumerator Start ()
    {

        // Waveが存在しなければコルーチンを終了する
        if (waves.Length == 0) {
            yield break;
        }

        while (true) {

            // Waveを作成する
            GameObject wave = (GameObject)Instantiate (waves [currentWave], transform.position, Quaternion.identity);

            // WaveをEmitterの子要素にする
            wave.transform.parent = transform;

            // Waveの子要素のEnemyが全て削除されるまで待機する
            while (wave.transform.childCount != 0) {
                yield return new WaitForEndOfFrame ();
            }

            // Waveの削除
            Destroy (wave);

            // 格納されているWaveを全て実行したらcurrentWaveを0にする(最初から -> ループ)
            if (waves.Length <= ++currentWave) {
                currentWave = 0;
            }

        }
    }
}

この中の、

while (wave.transform.childCount != 0) {
                yield return new WaitForEndOfFrame ();
            }


の部分ですが、wave.transform.childCount != 0がfalseになった時に次のDestroy (wave);まで進むことができるという機能になっているかと思います。

しかし、このwhile文でなぜそうなるのか、ということが理解できません。

WaitForEndOfFrame のドキュメントの説明を見ると、「スクリーン上のレンダリングが完了するまで待ちます。」とあります。なぜレンダリングの話がでてくるのでしょうか。

また、wave.transform.childCount != 0がfalseになった時にDestroy (wave);に移るということは、もしかするとtrueの間はyield return new WaitForEndOfFrame ();はcontinueのような使われ方で、ループの先頭に戻る役割を担っているのでしょうか。つまり、falseになった瞬間にcontinueしなくなったので、次に進めたということかな、と。(だとしてもドキュメントの説明とつながらないように思えます)

何卒ご教授頂けると嬉しいです。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+3

質問主さんの仰る通り、「IEnumerator Start()」がコルーチンです。

「WaitForEndOfFrame」はレンダリング完了までの時間を指しますが、
このレンダリングというのは、いわゆる「フレーム」とほぼ同義です。
60fpsなら約0.016秒、30fpsなら0.033秒となります。

「yield return」は「一旦処理を中断し、returnの後ろに書かれた時間だけ待機し、その後次の行から再開する(=待機する)」命令です。

もしyield returnを挟まない場合、ずっと処理が続く為、それが完了するまで入力を受け付けることも出来ず画面が固まることになります。
(Unityの仕様上1つのプログラムの動作中は基本的に他のプログラムは動作せず、画面再描画(=レンダリング)もされない為)

なので実際の挙動は
「(wave.transform.childCount != 0)を判定→trueだった→yield returnで処理中断→レンダリングや入力を待つ→レンダリング完了(=1フレーム経過)→(wave.transform.childCount != 0)を再判定→trueだった→処理中断→レンダリングを待つ→……」
という繰り返しになり、結果的に
「Waveの子要素のEnemyが全て削除されるまで待機する」という処理になります。

こちらも参考になるかと思います。
Unity - マニュアル: イベント関数の実行順

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/10/24 14:19 編集

    非常にわかりやすいご回答、ありがとうございます!
    私がちゃんと理解できているか、図を添付してみました。
    (※1)が、1回目のyield returnの再開タイミング(レンタリング終了のタイミング)。その後、同じ0フレーム内で2回目のwhile (wave.transform.childCount != 0) {};に突入し、(※2)のタイミングで、次のレンダリング終了まで中断する。
    という流れあっていますでしょうか。
    もし正しいとすると、理解しづらかった理由は、1回目の中断はフレームをまたがず、2回目以降の中断ではフレームをまたいでいる点でした。yield return new WaitForEndOfFrame ();がフレーム単位で見た時に1回目も2回目も同じような処理になると勝手に思い込んでいたためです。

    キャンセル

  • 2017/10/25 11:25 編集

    私も正確に理解している保証は無い前提でお答えしますと……

    「1フレーム」を「回答内のリンク先で言うPhysics〜Pausingまで」とするならば、図の解釈で合っています。

    フレーム0:ゲーム起動(Editor→Initializationと進行)
    フレーム0:Start()内で1回目のWhile判定、yield return開始
    フレーム0:Initialization終了

    フレーム1:フレーム開始(Physics→Input events→……と進行)
    フレーム1:End of frame到達時にyield return終了、2回目のWhile判定、yield return開始
    フレーム1:Pausing完了、フレーム終了

    フレーム2:フレーム開始(Physics→Input events→……と進行)
    フレーム2:End of frame到達時にyield return終了、3回目のWhile判定、yield return開始
    フレーム2:Pausing完了、フレーム終了

    (以降繰り返し)

    「1回目のWhile判定はそもそもフレームのカウントが開始する前に行われる(=便宜上フレーム0としたが本当はフレームと呼べない=なのでフレームをまたぐこともない)」
    「yield WaitForEndOfFrameは『常にEnd of frameタイミングまで待つ』(=1回目も2回目も同じ処理である)」
    どちらも正しい認識だと思います。

    キャンセル

  • 2017/10/25 14:49

    なるほど!とても分かりやすいです。ありがとうございます。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.96%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る