Project KURUKURU

ゲームプログラミングについて、興味を持ったものを雑多に取り上げるブログです

C#で1ms以下の計測、そして1000FPSキープ

はじめに

こんにちはこんばんは。繰繰廻(くるくる・めぐる)です。ECSの記事をのんびり書いていたら続き書く前に飽きたので、今回は別の小ネタです。今回Unityで作っているプロジェクトでUDP通信の処理を書く必要が出てきたのですが、通信のスレッドを1000FPSに固定する必要があったので、そこの処理の備忘録です。

 

 

失敗したこと

とりあえず「C# 時間計測」でググったら、Stopwatchクラスなるものが出てきた。

Stopwatch クラス (System.Diagnostics)

ので、それを使ってざっくり書いてみた

while (isRunning)
{
    System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
    sw.Start();
    
    //処理
    
    sw.Stop();

    while ((float)sw.ElapsedMilliseconds < 1.0f)
    {
        sw.Start();
        sw.Stop();
    }
}

 結果、ダメでした

そりゃそうですよ。Stopwatch.ElapsedMillisecondsの戻り値、long型なんだから、1ms単位のもの測ろうとしてもうまく行きませんでした。

Stopwatch.ElapsedMilliseconds プロパティ (System.Diagnostics)

結局どうしたか

C++で60FPS固定するときに使ったような、CPUの周期で時間測るやつ使いました。

using System.Runtime.InteropServices;

public class UDPSender : MonoBehaviour
{
    //1000Hzにするための高精度カウンター
    [DllImport("kernel32.dll")]
    extern static int QueryPerformanceCounter(ref long x);

    [DllImport("kernel32.dll")]
    extern static int QueryPerformanceFrequency(ref long x);

    //ここは別スレッド
    void SendFunc()
    {
        long freq, before, after;
        freq = before = after = 0;
        QueryPerformanceFrequency(ref freq);
        while (isRunning)
        {
            before = after = 0;
            QueryPerformanceCounter(ref before);

            //処理
            
            QueryPerformanceCounter(ref after);

            while ((after - before) * 1000 / (float)freq < 1.0f)
            {
                QueryPerformanceCounter(ref after);
            }
        }
    }
}

だいぶざっくりと処理を書きましたがこんな感じ。処理の間に経過したクロック数を測っておいて、それを周波数で割り算すると経過秒が出てきて、それを1000倍することでミリ秒の計測。

そして1ms超えるまでwhileで処理を止めることで1000FPSの完成。

DllImportとかは今回初めて使いました。なんで奥まったところに処理隠されちゃったんだろう。