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

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

ただいまの
回答率

88.13%

画面を一定時間停止後に画面遷移させる処理がうまくいかない

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 4,086

score 11

【やりたいこと】
アクションゲームのゲームオーバー処理で、画面全体を一定時間停止させた後、
後続の処理を再開させたい。(ここでは画面遷移)

【困っていること】
下記コードのように、Time.timeScale = 0.0f; で停止させた後、
コルーチン内でWait処理を実行させた後、
Time.timeScale = 1.0f; で画面更新を戻すロジックを作りましたが、
下記コメント記述のように、うまくいかず、
Time.timeScale = 1.0f; を付ける適切な場所がわかりません。

Time.timeScale = 1.0f; をつけた状態だと、画面が一時停止しなくなります。

ちなみに、コルーチンを使ったやり方については、いろいろと情報サイトを調べて試してみましたが、うまくいっていません。

どなたかご教示お願い致します。m(_ _)m

記載の不備等ありましたら、ご質問頂ければ、補足説明します。

【Unityのバージョン】
unity2017_1.0f3

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

using UnityEngine.UI;
using UnityEngine.SceneManagement;

public class GameManager : MonoBehaviour {

    // … 中略 …

    // ゲームオーバー処理
    public void GameOver () {

        // … 中略 …

        Time.timeScale = 0.0f;

        StartCoroutine("PauseMinites", 2.0f);

        Invoke("GoBackStageSelect", 2.0f);

        // ↓ これを付けると、シーンが一時停止しなくなる。
        // ↓ 削除すると、シーンが停止したままとなる。
        Time.timeScale = 1.0f;
    }

    // ステージ選択シーンに戻る
    void GoBackStageSelect () {
        SceneManager.LoadScene ("StageSelectScene");
    }

    // 一定時間停止処理
    IEnumerator PauseMinites(float sec) {
        Debug.Log("PauseMinites呼び出し");

        yield return new WaitForSeconds(sec);

        Debug.Log("Wait後、timeScaleを1に設定。");

        Time.timeScale = 1.0f;
    }
}
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 2

checkベストアンサー

+2

Time.timeScaleは単なる一時停止ではなく、「Unity内の時間の流れ設定(倍率)」です。
つまりTime.timeScale = 0.0f;は「時間の流れを0倍にする=時間を止める」ということなので、WaitForSecondsやInvokeの時間測定は止まります。
(時間の流れが止まっているので「○秒後」も来ることが無い)

Coroutine自体が止まっているわけではないので、
yield return new WaitForSeconds(sec);の部分を
for (int i = 0; i < 100; i++) { yield return null; }
とかにすれば一応計測は可能です。
yield return null;は1フレーム待つのと同じ処理。フレームの更新自体は行われている為)
なお、Updateも止まりませんが、Time.deltaTimeは0になるので、時間計測時はTime.realtimeSinceStartupなどを使ってください。

Unityの一時停止で止まるもの、止まらないもの【Time.timeScale】
Time.timeScale に左右されない時間を使う【Tips】

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/08/26 19:08

    ご教示頂き、ありがとうございました。
    とりあえずではありますが、下記のように自己解決しました。
    シーンの一時停止処理の実装については、個人的には今後の課題とさせていただきます。

    キャンセル

0

上記回答頂いたように、次の通りに実装して試してみましたが、
停止した状態のまま、再開できませんでした。

///GameManager.cs
public class GameManager : MonoBehaviour {

    // 中略

    // ゲームオーバー処理
    public void GameOver () {
        textGameOver.SetActive (true);
        PauseMinites(3.0f);
        Invoke("GoBackStageSelect", 0.0f);
    }

    // ステージセレクトシーンに戻る
    public void GoBackStageSelect () {
        SceneManager.LoadScene ("StageSelectScene");
    }

    // 一定時間停止処理
    void PauseMinites(float sec) {
        Debug.Log("PauseMinites呼び出し");
        Time.timeScale = 0.0f;

        for (lastRealTime = 0.0f, realDeltaTime = 0.0f; sec > realDeltaTime; ) {
            Debug.Log("realtimeSinceStartup呼び出しループ内");
            // 【???】このループ処理から脱出できていないと思われる。
            CalcRealDeltaTime();
        }
        Time.timeScale = 1.0f;
        Debug.Log("Wait後、timeScaleを1に設定。");
    }

    //現実時間基準で経過時間を計算する
    void CalcRealDeltaTime()
    {
        if (lastRealTime == 0)
        {
            lastRealTime = Time.realtimeSinceStartup;
        }
        realDeltaTime = Time.realtimeSinceStartup - lastRealTime;
        lastRealTime = Time.realtimeSinceStartup;
    }
}

シーン全体を一時停止する代わりに、停止させたい、(ボールの)オブジェクトのみ停止させる実装方針に変更しました。
具体的には、次のように、RidgeBody2Dの velocity(位置移動)と、Z軸方向の回転(Rotation)を停止させるようにしました。

///GameManager.cs
public class GameManager : MonoBehaviour {

    // 中略

    // ゲームオーバー処理
    public void GameOver () {
        textGameOver.SetActive (true);
        Invoke("GoBackStageSelect", 3.0f);
    }

    // ステージセレクトシーンに戻る
    public void GoBackStageSelect () {
        SceneManager.LoadScene ("StageSelectScene");
    }
}

/// PlayerManager.cs
public class PlayerManager : MonoBehaviour {
    private Rigidbody2D rbody;            // プレイヤー制御用rigidbody2D

    void Start () {
        rbody = GetComponent<Rigidbody2D> ();
    }

    // 衝突処理
    void OnTriggerEnter2D(Collider2D col)
    {
        //停止したBombに衝突時、又は移動範囲外に出た場合
        if ("Bomb".Equals(col.gameObject.tag) || "Trap".Equals(col.gameObject.tag))
        {
            //ボール\停止処理
            StopPlayer();
            gameManager.GetComponent<GameManager>().GameOver();
        }
    }

    //ボール移動・回転停止および重力無効化処理
    void StopPlayer() {
        rbody.velocity = Vector2.zero;
        rbody.freezeRotation = true;
        rbody.isKinematic = true;
    }

}

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/08/27 16:48

    再開出来なかった方のコードですが、
    CalcRealDeltaTime()内の最後の「lastRealTime = Time.realtimeSinceStartup;」のせいで、realDeltaTimeが「このメソッドの前回呼び出しからの経過時間」になっています。
    なのでPauseMinites()のsecよりも大きくなることはありません。

    こいつは上記の「lastRealTime = Time.realtimeSinceStartup;」を削除すれば正常に動作します。

    ちなみにPauseMinites()をvoidにしておくと、forループが1秒間に物凄い回数回ります。
    (PC上で計測したら1回あたり0.0002秒ぐらいで回ります)
    void PauseMinites(float sec) を IEnumerator PauseMinites(float sec)に変えて
    forの中に yield return null; を刺すと1フレーム(0.016秒とか)毎の回転になるので余計な負荷を掛けさせないかと思います。

    キャンセル

  • 2018/08/29 23:39

    コメントが遅くなりました。
    補足説明して頂き、ありがとうございました。
    上記のように、「lastRealTime = Time.realtimeSinceStartup;」を削除して、正常に動作することを確認しました。
    ちなみに、後半の説明のように、IEnumerator と yield return null; を使うと、
    なぜか、Waitせずに画面遷移してしまい、ステージ選択画面から再度同じステージを選択しても、画面が止まったまま動かない、という状況です。

    しかし、回答にありました「Time.realtimeSinceStartup を使う」という重要なヒントと参考URLをご教示頂いたので、最初の回答及び、上記のコメントをベストアンサーとさせて頂きます。
    追伸
    コルーチンと、フレームのUpdate 間の処理の流れが未だによくわかっていません。。。(。。)
    今後も学習して、コルーチンや、画面更新処理をうまく使いこなせるようになりたいと思います。

    キャンセル

  • 2018/08/30 10:05

    >Waitせずに画面遷移してしまい、ステージ選択画面から再度同じステージを選択しても、画面が止まったまま動かない
    書き方悪かったですね。コルーチンに変更しただけだとそういう動きになります。

    void Do () {
    処理1;
    StartCoroutine(コルーチン);
    処理2;
    }

    上記の場合、通常のスクリプトと同様に「処理1実行→コルーチンの呼び出し(※呼び出されるだけで待たない)→処理2実行」のように、待機無しで進みます。(=コルーチン内の処理と、処理2が並行で実行される)
    なので待機したい場合は処理2をコルーチンの内部に入れるか、この一連の処理を行っているメソッドもコルーチン化してyieldを使います。

    IEnumerator Do () {
    処理1;
    yield return コルーチン; //コルーチンが終わるまで待機
    処理2;
    }

    >コルーチンと、フレームのUpdate間の処理の流れ
    「全く別々に動く」と思ってください。両者に関連性は無いです。
    ただフレームを時間軸のベースにしているのは共通です。
    以下を見ると処理の流れが分かりやすいかと思います。ご参考までに。
    https://docs.unity3d.com/ja/current/Manual/ExecutionOrder.html

    キャンセル

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

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

関連した質問

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