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

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

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

WPF

Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです

Q&A

解決済

1回答

2664閲覧

C# 秒間60回の処理を行うタイマーが思ったように動作しません。

gucha

総合スコア55

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

WPF

Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです

0グッド

0クリップ

投稿2018/11/11 12:00

編集2018/11/13 03:04

60fpsの処理を実現させるために「System.Environment.TickCount」を使ったタイマー処理を試したのですが上手く動作しなかったので知恵をお貸しください。
コードはこちらのサイトで紹介されているものとほぼ変わりません。

コード

XAML

1<Grid> 2 <Button Content="start" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click"/> 3</Grid>

C#

1private void Button_Click(object sender, RoutedEventArgs e) 2{ 3 double nextframe = (double)System.Environment.TickCount; 4 float wait = 1000f / 60f; 5 6 // 時間差確認用 7 double diffTime = 0; 8 9 while (true) 10 { 11 if ((double)System.Environment.TickCount >= nextframe) 12 { 13 // 現在の時刻を取得 14 double nowtime = (double)System.Environment.TickCount; 15 // 時間差 16 double diff = nowtime - diffTime; 17 // 更新 18 diffTime = nowtime; 19 nextframe += wait; 20 21 Console.WriteLine("diff:" + diff + " now:" + nowtime + " nextframe:" + nextframe); 22 } 23 } 24}

 
実行した結果の一部
diff:16 now:710521281 nextframe:710521295.333277
diff:15 now:710521296 nextframe:710521311.999943
diff:16 now:710521312 nextframe:710521328.666609
diff:31 now:710521343 nextframe:710521345.333275
diff:16 now:710521359 nextframe:710521361.999941
diff:16 now:710521375 nextframe:710521378.666607
diff:15 now:710521390 nextframe:710521395.333273
diff:16 now:710521406 nextframe:710521411.999939
diff:15 now:710521421 nextframe:710521428.666605
diff:16 now:710521437 nextframe:710521445.333271
diff:16 now:710521453 nextframe:710521461.999937
diff:15 now:710521468 nextframe:710521478.666603
diff:16 now:710521484 nextframe:710521495.333269
diff:16 now:710521500 nextframe:710521511.999935
diff:15 now:710521515 nextframe:710521528.666601
diff:16 now:710521531 nextframe:710521545.333267
diff:15 now:710521546 nextframe:710521561.999933
diff:16 now:710521562 nextframe:710521578.666599
diff:31 now:710521593 nextframe:710521595.333265
diff:16 now:710521609 nextframe:710521611.999931
diff:16 now:710521625 nextframe:710521628.666597

このような結果が得られました。
大体が1/60秒の周期で処理できているのですが、ところどころで1/60秒の倍ほどの間隔になってしまいます。スキップの指定はどれも1000f/60f間隔で加算できているのでこれはコードではなく環境の問題でしょうか?

原因と対処法があれば教えて頂きたいです。


追記

Stopwatchを使用したコードを考えてみました。
上手く機能しているように思いますが間違いや注意点などがあれば教えてください。

C#

1Stopwatch stopwatch = new Stopwatch(); 2private void Button_Click(object sender, RoutedEventArgs e) 3{ 4 // 1/60秒の間隔 5 // コメントにて指摘されたので修正 6 // int interval = 166667; 7 float interval = 1f / 60f * Stopwatch.Frequency; 8 9 while (true) 10 { 11 if (stopwatch.ElapsedTicks >= interval) 12 { 13 // 指定した間隔を超えた時の処理 14 Console.WriteLine(stopwatch.ElapsedMilliseconds); 15 stopwatch.Restart(); 16 } 17 else 18 { 19 // 指定した間隔未満だった時の処理 Stopwatchを再開 20 stopwatch.Start(); 21 } 22 } 23}

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

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

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

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

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

guest

回答1

0

ベストアンサー

TickCount は確かに1msの分解能はありますが、その実、時間的精度はあんましよろしくありません

C# 経過時間取得,タイマー

なので、こういう用途で使用する場合には、タイマを選ぶ必要があります

投稿2018/11/11 12:34

y_waiwai

総合スコア87747

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

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

gucha

2018/11/11 16:50

解答ありがとうございます。 バラつきが出たのはそういうわけだったのですね。参考先も読ませて頂きました。 Thread.Timer、Timers.Timerで確認しましたが書かれていたような結果が得られました。思っていた以上に誤差があったので驚きました・・・。 Stopwatchでループの経過時間を確認する方法を試したところ幾分精度が上がりましたが、Stopwatchは使用したことがないので使い方が正しいのか分かっていません。間違いや注意点などがあれば教えていただきたいです。 コードを追記しておきました。
y_waiwai

2018/11/11 22:25 編集

私もStopwatchは使ったことないのでアレですが、いろいろ読んでるとStop/Restartでは計測を停止/再開させるという記述が気になります。 もしかしたら、ここはStopさせずに動きっぱにさせて、ElapsedMillisecondsやElapsedTicksを読んできて、(最初のコードのように)タイマ値を積算させながら比較していくのがいいかもしれません
y_waiwai

2018/11/11 22:36

んで、気をつけなければいけないのは、PCの規格(?)上、高精度のタイマ値を取得する機能、というのは用意されていません(参照先でよく書かれている55msという数字は、カレンダICのクロック値から来てます) Stopwatch(PerformanceCounter)は、おそらくサウンドカード由来のタイマ値となるので、もしかすれば、PCのデバイス構成によって変わる可能性があります。 まあ、いまどきサウンドデバイスを内蔵していないPCなぞ存在してないでしょうけど、そこらへん考慮しておく必要があるかもしれません
gucha

2018/11/12 03:22

ElapsedMillisecondsやElapsedTicksを読むとその時点でStopwatchが停止してしまうのでStartで再開させています。Restartでリセットして再開です。 停止させずに読む方法が分からないのでこのような形にしました・・・。 PerformanceCounterも環境依存で結構変わってしまう可能性があるのですね。これが最適かは分かりませんが狙っていた通りの動作が確認できました。解答ありがとうございました。
YAmaGNZ

2018/11/12 03:40

intervalに用いる値はStopwatch.Frequencyから計算されたほうがよろしいかと思います。 手元のPC2台で比較したところ値が異なっていました。
gucha

2018/11/13 03:05

教えて頂きありがとうございます。 float interval = 1f / 60f * Stopwatch.Frequency; としました。こちらで問題ないでしょうか?
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問