web-dev-qa-db-ja.com

XNA / MonoGame:1秒あたりのフレーム数の取得

ゲームの現在のFPSを取得しようとしていますが、FPS変数を毎秒更新するメソッドしか見つかりません。例えば。 https://github.com/CartBlanche/MonoGame-Samples/blob/master/Draw2D/FPSCounterComponent.cs および http://www.david-amador.com/2009/11/how-to-do-a-xna-fps-counter /

FPSラベルを継続的に更新する方法はありますか?

11
Jeff

これが私が少し前に書いたFPSカウンタークラスです。コードにドロップしてそのまま使用できるはずです。

public class FrameCounter
{
    public FrameCounter()
    {
    }

    public long TotalFrames { get; private set; }
    public float TotalSeconds { get; private set; }
    public float AverageFramesPerSecond { get; private set; }
    public float CurrentFramesPerSecond { get; private set; }

    public const int MAXIMUM_SAMPLES = 100;

    private Queue<float> _sampleBuffer = new Queue<float>();

    public override bool Update(float deltaTime)
    {
        CurrentFramesPerSecond = 1.0f / deltaTime;

        _sampleBuffer.Enqueue(CurrentFramesPerSecond);

        if (_sampleBuffer.Count > MAXIMUM_SAMPLES)
        {
            _sampleBuffer.Dequeue();
            AverageFramesPerSecond = _sampleBuffer.Average(i => i);
        } 
        else
        {
            AverageFramesPerSecond = CurrentFramesPerSecond;
        }

        TotalFrames++;
        TotalSeconds += deltaTime;
        return true;
    }
}

あなたがする必要があるのはあなたのメインのゲームクラスでメンバー変数を作成することです。

    private FrameCounter _frameCounter = new FrameCounter();

そして、ゲームのDrawメソッドでUpdateメソッドを呼び出し、好きなようにラベルを描画します。

    protected override void Draw(GameTime gameTime)
    {
         var deltaTime = (float)gameTime.ElapsedGameTime.TotalSeconds;

         _frameCounter.Update(deltaTime);

         var fps = string.Format("FPS: {0}", _frameCounter.AverageFramesPerSecond);

         _spriteBatch.DrawString(_spriteFont, fps, new Vector2(1, 1), Color.Black);

        // other draw code here
    }

楽しい! :)

27
craftworkgames

次の式を使用して、任意の時点で現在のフレームレートを取得できます。

framerate = (1 / gameTime.ElapsedGameTime.TotalSeconds);

以下に示す他の両方の方法では、フレームレートが変更され、よりスムーズになり、戻り値の変動が少なくなります。

これは、以前のすべてのフレームタイムを対数スケールで重み付けすることによって機能します。フレームドロップが適切に表現されておらず(または、平均が高い場合はまったく)、フレームレートが非常に低い/非常に高い場合、精度のレベルが大きく異なるため、平均を使用してゲームパフォーマンスのメトリックを取得するというアイデアは好きではありませんでした。それらは同じ平均で実行されています。

これを解決するために、私はSmartFramerateクラスを作成しました(ひどい名前、私は知っています)

class SmartFramerate
{
    double currentFrametimes;
    double weight;
    int numerator;

    public double framerate
    {
        get
        {
            return (numerator / currentFrametimes);
        }
    }

    public SmartFramerate(int oldFrameWeight)
    {
        numerator = oldFrameWeight;
        weight = (double)oldFrameWeight / ((double)oldFrameWeight - 1d);
    }

    public void Update(double timeSinceLastFrame)
    {
        currentFrametimes = currentFrametimes / weight;
        currentFrametimes += timeSinceLastFrame;
    }
}

変数を作成するときに重みを設定します:(重みが大きいほど瞬間的なフレームレートに正確であり、重みが小さいほど滑らかです。3〜5が適切なバランスであることがわかります)

SmartFramerate smartFPS = new SmartFramerate(5);

フレームごとに実行される任意の場所でUpdateメソッドを呼び出します。

smartFPS.Update(gameTime.ElapsedGameTime.TotalSeconds);

現在のフレームレートには、次のようにアクセスできます。

smartFPS.framerate

またはそのように印刷されます:

debuginfo.Update("\n\nᴥ" + smartFPS.framerate.ToString("0000"), true);

(私はそれをカスタム印刷クラスに入れているので、構文がファンキーに見える場合はお詫びします)

ただし、特定のフレーム数を単純に平均化する場合は、このクラスが私が思いついた最も効率的な方法です。

class SmoothFramerate
{
    int samples;
    int currentFrame;
    double[] frametimes;
    double currentFrametimes;

    public double framerate
    {
        get
        {
            return (samples / currentFrametimes);
        }
    }

    public SmoothFramerate(int Samples)
    {
        samples = Samples;
        currentFrame = 0;
        frametimes = new double[samples];
    }

    public void Update(double timeSinceLastFrame)
    {
        currentFrame++;
        if (currentFrame >= frametimes.Length) { currentFrame = 0; }

        currentFrametimes -= frametimes[currentFrame];
        frametimes[currentFrame] = timeSinceLastFrame;
        currentFrametimes += frametimes[currentFrame];
    }
}

これを使用するには、SmoothFramerate変数を使用する場所で初期化し、平均化するフレームの量を渡します。

SmoothFramerate smoothFPS = new SmoothFramerate(1000);

上記のSmartFramerateクラスを使用する場合とまったく同じように、現在のフレームレートを更新、アクセス、および印刷します。

読んでくれてありがとう、これが誰かを助けてくれることを願っています。

5
Darius

ダブルを使用してfpsを測定します。

double frameRate = 0.0;

メソッドUpdateを次のように変更します。

public override void Update(GameTime gameTime)
{        
    if(gameTime.ElapsedGameTime.TotalSeconds > 0.0)
    {
        frameRate = (double)frameCounter / gameTime.ElapsedGameTime.TotalSeconds;
    }
    frameCounter = 0;
}

私はコードをテストしませんでしたが、あなたはアイデアを得る必要があります。

1
helb

すべてのDraw()を更新する簡単なメソッドは次のようになります。

frameRate = 1 / gameTime.ElapsedGameTime.TotalSeconds;

必要に応じて、Update()メソッドにドロップして、それが発生する頻度を確認することもできます。

更新速度を遅くしたい場合...

frameRate += (((1 / gameTime.ElapsedGameTime.TotalSeconds) - frameRate) * 0.1);
0
Genzzry

おそらく、すべての描画操作でFPSを更新しようとしています。これは非常に不安定な値になります。必要な場合は多く、場合によっては少なくなるためです。値をより安定させるには、多くの値から平均を取ります。

FPSをカウントする最も簡単な方法は、おそらく図面をカウントすることです。そして、この値を毎秒と言うタイマーベース関数を使用して、新しいfpsを計算し、カウンターをクリアします。このタイマーを長くするか(たとえば2秒)、または単に最後の10fps値を取得して平均を表示します。

一部のゲームでは、シーンの再描画に必要な最大時間もカウントされます。これも興味深い値になる可能性があります。このために、描画関数にかかる時間を測定できます。開始時と終了時に時間を登録すると、差が時間になります。

同期を有効にすると、次の同期まで描画操作が遅れることに注意してください。おそらくこれが奇妙な結果の理由ですか?

0
Sinatr