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

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

新規登録して質問してみよう
ただいま回答率
85.50%
Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

Q&A

解決済

1回答

5869閲覧

タイマーの作り方。

退会済みユーザー

退会済みユーザー

総合スコア0

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

1グッド

2クリップ

投稿2016/10/10 03:39

編集2016/10/10 06:57

###前提・実現したいこと
タイマーでコルーチンを使ってはいけないのでしょうか?
この記事の、「1. 簡単なタイマー機能にyieldやcoroutineを使用しないこと」では、
タイマーにコルーチンを使ってはいけないと書かれているようですが、その理由が書かれていない為、
使ってはいけない理由がわかりません。コルーチンを使うと何か不具合が生じるのでしょうか?

この記事の末尾の『「何秒後にこういうことをしたい」といったように、
1フレームを超えた処理でコルーチンを使用しないといけないところで自分の理解が止まっていて...』の、
「1フレームを超えた処理でコルーチンを使用しないといけないところ」の文章が理解できません。
単純に、「フレームをまたぐ処理はコルーチンを使用しないといけない」と言っているのでしょうか?

本題ですが、下記のような仕様のタイマーを作りたいと思っています。

①指定キーを押したタイミングでタイマーがスタート。 ②毎回、時間を表示する。整数でなく、少なくとも小数第2位までは表示する(今回はDebug.Logを出力)。 ③指定時間間隔ごとに定期処理が実行される(今回はDebug.Logを出力)。 ④指定した時間経過後、タイマーを終了として、終了処理を行う(今回はDebug.Logを出力)。 ⑤上記の処理をなるべく、メソッド化。指定時間間隔と終了時間を引数で渡す。

冒頭のコルーチンを使ってはいけないらしい情報があった為、
上記仕様のタイマーをUpdate()で作ってみました。
しかし、③、④の処理が、時間ピッタリに行うことができません(詳細は試したことのコードのコメント)。

上記仕様のタイマーの作り方のご教授をお願いいたします。

また、コルーチンを使って作る場合、冒頭の件に関しても教えていただきたいです。

###試したこと

C#

1bool timerflag = false; 2float timercount = 0; 3 4 5 void Update() 6 { 7 if (Input.GetKey(KeyCode.T) & !(timerflag)) { 8 timercount = 0; 9 timerflag = true; 10 } 11 if (timerflag) { 12 timerMethod (0.5f, 10); 13 } 14 } 15 16 void timerMethod(float interval, int limit){ 17 timercount += Time.deltaTime; 18 //毎回時間を表示する。少なくとも小数第2位までは表示する。 19 Debug.Log (timercount); 20 21 //この条件では、引っ掛からない可能性が高い。 22 //また、if文内をMathf.Floorを使って、timercountを0.5xxx...から0.5に書き換えても、 23 //複数回引っかかる可能性がある。例:0.5123のとき、0.5とみなされ引っかかり、 24 //0.52431でも0.5とみなされ引っかかる。 25 //そもそも、0.5123のとき、0.5とみなすのではなく、 26 //0.5000...0という時間ピッタリのときに引っ掛けたい。 27 if (timercount % interval == 0) { 28 //定期実行処理。 29 Debug.Log ("定期実行処理" + timercount); 30 } 31 32 //この条件では、指定時間ピッタリに終了させることはできない可能性が高い。 33 //10.000...0で引っかからない。 34 //Mathf.Floorを使えば、10になるが、そもそもそれでは本当の10.000...0の時間に 35 //引っ掛かるわけではない。 36 if (timercount == limit) { 37 Debug.Log ("タイマー終了"); 38 timerflag = false; 39 } 40 }
mpyw👍を押しています

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

■何故コルーチンを使ってはいけないか
元記事の方にもありますが、

正しく記載することを忘れてしまうことで大混乱に陥ったり、そもそも何の目的で使うか見えなくなりがち

これが理由です。
よく理解しないまま使うとコルーチンが無限増殖したり、永遠に回り続けることで負荷を上昇させます。それを防ぐ為、簡単な処理はUpdateで済ませましょう、或いは別の処理を使いましょうということです。

■時間ぴったりに何かを実行させる
厳密には不可能です。
コルーチンと元記事で紹介されているInvokeも試してみましたが、0.04秒程のラグが発生しました。
そもそもfloat自体にも誤差がありますし、0.01秒以下の単位での時間合わせは難しいものと思います。


コルーチン・Invokeを使用する場合、「コルーチンかInvokeで開始・終了フラグを管理する」「Updateで時間表示機能を作る(開始・終了フラグを監視する。終了時は端数になるので無理矢理きっちりした数字にしてしまう)」の2つを組み合わせれば出来るかと思います。

ただ、改めて考えると描画はフレームレートのタイミングでしか更新されないので、全てUpdateで行っても時間の精度で言えば大した違いは出ないように思います。
(例えば5秒後きっちりにコルーチンを止めたとしても、画面上にそれが表示されるのは「次のフレーム」なので、そこでも微細な誤差が出ます)
一応全てUpdateで処理したバージョンのスクリプトを置いておきます。

C#

1bool timerflag = false; 2float timercount = 0.0f; 3float totalTimerCount = 0.0f; 4 5 6 void Update() 7 { 8 if (Input.GetKey(KeyCode.T) & !(timerflag)) { 9 timercount = 0.0f; 10 totalTimerCount = 0.0f; 11 timerflag = true; 12 } 13 if (timerflag) { 14 timerMethod (5.0f, 10.0f); 15 } 16 } 17 18 void timerMethod(float limit, float totalLimit){ 19 timercount += Time.deltaTime; 20 totalTimerCount += Time.deltaTime; 21 22 //毎回時間を表示する。少なくとも小数第2位までは表示する。 23 Debug.Log (timercount); 24 Debug.Log (totalTimerCount); 25 26 if (timercount >= limit) { 27 //定期実行処理。 28 Debug.Log ("定期実行処理" + timercount); 29 timercount = 0.0f; 30 } 31 32 if (totalTimerCount >= totalLimit) { 33 Debug.Log ("タイマー終了"+ totalTimerCount); 34 //終了時のtotalTimerCountはきっちりした値ではないので、 35 //表示する際はきっちりした値(totalLimit)を表示して、 36 //さもきっちり終わったかのように見せかけるとよい 37 timerflag = false; 38 } 39 40 //誤差は60fpsなら0.01666…秒、30fpsなら0.333…秒(実機動作時の現在のfpsの影響を受けます) 41 }

投稿2016/10/10 08:07

編集2016/10/10 09:42
sakura_hana

総合スコア11425

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2016/10/10 08:56

ご回答ありがとうございます。コルーチンを使ってはいけない理由、とてもよくわかりました。可能であれば、質問の仕様のタイマーのコードを、コルーチンとInvokeとで掲載お願いできないでしょうか。コルーチンやInvokeで書く場合、毎回、時間表示を行いつつ、途中で定期実行も行うという組み合わせのコードが作れなくて困っています。
退会済みユーザー

退会済みユーザー

2016/10/10 13:25

ご回答ありがとうございます。コルーチン・Invokeを使用する場合でも、Updateを併用することで解決できることが勉強になりました。また、誤差に関しても丁寧にご教授くださりありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問