Help us understand the problem. What is going on with this article?

コルーチンの止め方と処理間隔について

More than 3 years have passed since last update.

基本的に扱い方は分ってるという前提ですので、ご了承ください。
実際に使ってみて分った事なので、
既にガッツリ使われている人には、あまりメリットがないかもしれません。
ご参考までにという事で。

コルーチンの止め方

StopCoroutine(~~) で止める訳だが、引数には3つ取る事が出来る。

  • String型
  • IEnumerator型
  • Coroutine型

大体の場合に置いて、String型がいいよ、という事になっているわけだが、
それは、メソッド名で動かした時に、止めるときに同じ方法で止まらないからだ。

だが、Stringで渡した時に致命的な欠点があって、
MonoDevelop上から、F2で置き換えた場合に、Stringなので当然一緒に置き変わらないというところだ。
これはコンパイルチェック上から外れてしまうので当然なわけだ。
(そんな迂闊なことしない? まあその通りだが。)

単純にStringが気持ち悪く、
恐らく Coroutine型 で止めるのが一番気持ちいいと思うのだが、
意外と情報がないので記載する事にした。

coroutinStopTest.cs
using UnityEngine;
using System.Collections;

public class coroutinStopTest : MonoBehaviour {

    void Start () {
        Coroutine retC = StartCoroutine (mainLoop());

        StopCoroutine (retC);
    }

    IEnumerator mainLoop(){
        while (true) {
            Debug.Log ("loop Coroutine");
            yield return new WaitForSeconds (0.5f);
        }
    }
}

StartCoroutine の戻り値が Coroutine型なので、
それを引数に StopCoroutine すれば良い。

これもんの、

    Coroutine retC = StartCoroutine (mainLoop());

これということ。

    StopCoroutine (retC);

サンプルソースの場合は、直ぐに停止されるので、ログは出ない。
ちゃんと動いて止まった事を確認したい人は、下の方のソースを見てください。

これがいちばんスマートで直感的だと思うのだが、
検索すると IEnumerator で止める方法が一番最初に上がってくるから不思議だ。

コルーチンの実行タイミングと時間間隔

これは実際に使った人は大体分っているものと思う。

・実行タイミング

これは、描写間隔と同期される。
つまり、WaitTimeを極小にした場合、Update() と同じタイミングで実行される。

・ yield return new WaitForSeconds (0.00001f);

などとしても、毎フレームに抑えられる。

・時間間隔の計測タイミング

上記のような極小タイミングだった場合、1フレームに沢山実行されない事から分るように、
「フレーム毎に、規定時間を過ぎていた場合に実行する」という処理であるといえる。

Time.timescale を少し小さくしてみると、その処理誤差が段々顕著になる。
WaitForSeconds は当然 Time.timescale の影響を受けるが、
毎フレームのタイミングを遅らせるものではない為、
規定時間を経過したと判定され、毎フレーム実行される。

止めるコルーチンに、retC を引数で渡している。

coroutinStopTest2.cs
using UnityEngine;
using System.Collections;

public class coroutinStopTest2 : MonoBehaviour {

    void Start () {
        Time.timeScale = 0.1f;
        Coroutine retC = StartCoroutine (mainLoop());

        StartCoroutine (stopWait(retC));
    }

    IEnumerator mainLoop(){
        yield return new WaitForSeconds (0.2f); //実行時処理の遅延の解消のため

        while (true) {
            yield return new WaitForSeconds (0.0001f);

            Debug.Log ("time : " + Time.deltaTime);
        }
    }

    IEnumerator stopWait(Coroutine retC){
        yield return new WaitForSeconds (2f);

        StopCoroutine (retC);
        Debug.Log ("stop Coroutine");
    }
}

つまり、極小の待機時間にした場合、殆どUpdate()と同じ動きになるという事だ。

で、何が言いたいかというと

Update() と同じ働きに近いが、Time.timescale の影響を受ける。

これを逆に利用して、ポーズ処理の実装が面倒な雑なアプリを作る時には、
Time.timescale = 0 で使えるのではないかという話。

とは言え、ポーズ処理はコンポーネントを無効化するのが一番無難そうですよね。

個人的には、パーティクルシステムが Time.timescale = 0 で動く方法があれば、
大体ビジュアル的にも問題ないですし、これでオッケーなんじゃないかと思ったりするわけですがw

ちなみに、yield return null とした場合は、
時間関数を通らないので、Time.timescale = 0 の影響は受けませんので、
そこは誤解の無きよう。

あと、今回の内容を踏まえてもらうと分ると思うのですが、
通常の使い方でも、WaitForSecondsでループする時は、
それなりに大きい待ち時間じゃないと時間誤差が大きく出るので、あまりよくないです(と思います)。

手抜き上等な方は、こっそり使ってみてはどうでしょうか
(全く推奨しませんがw)

Heart_Of_Logic
ゲーム作り始めました
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした