13

現在のFPSを表示するコンポーネントを作成しました。
その最も重要な部分は次のとおりです。

    public override void Update(GameTime gameTime)
    {
        elapseTime += (float)gameTime.ElapsedRealTime.TotalSeconds;
        frameCounter++;

        if (elapseTime > 1)
        {
            FPS = frameCounter;
            frameCounter = 0;
            elapseTime = 0;
        }
        base.Update(gameTime);
    }


    public override void Draw(GameTime gameTime)
    {
        spriteBatch.Begin();

        spriteBatch.DrawString(font, "FPS " + ((int)FPS).ToString(), position, color, 0, origin, scale, SpriteEffects.None, 0);

        spriteBatch.End();

        base.Draw(gameTime);
    }

ほとんどの場合は問題なく動作しますが、最近問題が発生しました。
次のコードをゲームのUpdateメソッドに入れると、奇妙なことが起こり始めます。

       if (threadPath == null || threadPath.ThreadState != ThreadState.Running)
        {
            ThreadStart ts = new ThreadStart(current.PathFinder.FindPaths);
            threadPath = new Thread(ts);
            threadPath.Priority = ThreadPriority.Highest;
            threadPath.Start();
        }

このコードの主なアイデアは、常に異なるスレッドでpathFindingアルゴリズムを実行することです。

奇妙なことに、FPSが大幅に低下することがあることは明らかですが、表示されるFPSは1秒に1回よりも頻繁に変化します。このコードを理解していれば、FPSは1秒に1回以上頻繁に変更することはできません。

誰かが私に何が起こっているのか説明できますか?

2010年3月26日編集
Drawメソッドのコードも掲載しました。

2010年3月31日編集Venesectrixの質問への回答
1)固定または可変の時間ステップで実行していますか?
IsFixedTimeStepおよびSynchronizeWithVerticalRetraceがtrueに設定されています。
2)これが発生したときにXNAウィンドウを移動していましたか?
いいえ
3)XNAウィンドウにフォーカスがありましたか?
はい
4)それはどれほど目立ちましたか(つまり、更新が速すぎて読めない、または更新が1秒以上かろうじて)?
私は更新を読むことができました、FPSは1秒間に約3回更新していました。
5)そして、これはすべて、そこにあるスレッドコードでのみ発生しますか?
はい

4

6 に答える 6

12

Shawn Hargreaves は、これについて素晴らしい投稿をしています。彼のコードとあなたのコードとの最初の違いは、elapseTime を毎回 0 にリセットすることで、時間が少し失われるという事実です。Shawn は、経過時間から 1 秒を差し引くだけです。また、Shawn は ElapsedRealTime の代わりに ElapsedGameTime を使用します。また、Update 関数ではなく、Draw 関数で frameCounter を更新します。

彼が ElapsedRealTime を使用する理由については、投稿後のコメントで次のように説明しています。

> きっと 1 / gameTime.ElapsedRealTime.TotalSeconds

> したがって、現在のフレームレートが得られます。

前回の Update 呼び出しからの経過時間がわかりますが、それはフレームレートとは異なります。

a) ゲームがフレームをドロップしている場合、追いつくために Update がより頻繁に呼び出されます。これらの余分なキャッチアップ ロジック フレームだけでなく、実際に行われている描画の回数を計りたいと考えています。

b) 1 回の更新の時間は大きく変動する可能性があるため、そこから得られる数値はちらつきすぎて簡単に読み取ることができません。

私は彼のコンポーネントを試してみて、それがあなたのために働くかどうかを確認します. 投稿はかなり古いので、別のコメントが指摘しているように、LoadGraphicsContent を LoadContent に、UnloadGraphicsContent を UnloadContent に変更する必要があると思います。

于 2010-03-25T00:08:17.110 に答える
2

これが私がそれを行う方法とこの方法です:

  • n フレームの平均
  • 選択した任意の初期化方法で使用できます
  • 読みやすく、従うのが簡単なはずです
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace _60fps
{
public class Game1 : Microsoft.Xna.Framework.Game
{
    GraphicsDeviceManager graphics;
    SpriteBatch spriteBatch;
    SpriteFont OutputFont;
    float Fps = 0f;
    private const int NumberSamples = 50; //Update fps timer based on this number of samples
    int[] Samples = new int[NumberSamples];
    int CurrentSample = 0;
    int TicksAggregate = 0;
    int SecondSinceStart = 0;

    public Game1()
    {
        graphics = new GraphicsDeviceManager(this);
        Content.RootDirectory = "Content";
    }

    protected override void Initialize()
    {
        base.Initialize();
        graphics.SynchronizeWithVerticalRetrace = false;
        int DesiredFrameRate = 60;
        TargetElapsedTime = new TimeSpan(TimeSpan.TicksPerSecond / DesiredFrameRate);
    }

    protected override void LoadContent()
    {
        spriteBatch = new SpriteBatch(GraphicsDevice);
        OutputFont = Content.Load<SpriteFont>("MessageFont");
    }

    protected override void UnloadContent()
    {/* Nothing to do */}

    protected override void Update(GameTime gameTime)
    {
        if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Escape))
            this.Exit();

        base.Update(gameTime);
    }

    private float Sum(int[] Samples)
    {
        float RetVal = 0f;
        for (int i = 0; i < Samples.Length; i++)
        {
            RetVal += (float)Samples[i];
        }
        return RetVal;
    }

    private Color ClearColor = Color.FromNonPremultiplied(20, 20, 40, 255);
    protected override void Draw(GameTime gameTime)
    {
        Samples[CurrentSample++] = (int)gameTime.ElapsedGameTime.Ticks;
        TicksAggregate += (int)gameTime.ElapsedGameTime.Ticks;
        if (TicksAggregate > TimeSpan.TicksPerSecond)
        {
            TicksAggregate -= (int)TimeSpan.TicksPerSecond;
            SecondSinceStart += 1;
        }
        if (CurrentSample == NumberSamples) //We are past the end of the array since the array is 0-based and NumberSamples is 1-based
        {
            float AverageFrameTime = Sum(Samples) / NumberSamples;
            Fps = TimeSpan.TicksPerSecond / AverageFrameTime;
            CurrentSample = 0;
        }

        GraphicsDevice.Clear(ClearColor);
        spriteBatch.Begin();
        if (Fps > 0)
        {
            spriteBatch.DrawString(OutputFont, string.Format("Current FPS: {0}\r\nTime since startup: {1}", Fps.ToString("000"), TimeSpan.FromSeconds(SecondSinceStart).ToString()), new Vector2(10,10), Color.White);
        }
        spriteBatch.End();
        base.Draw(gameTime);
    }
}
}

に関しては:「しかし、表示されるFPSが1秒に1回以上変化する理由はまだ未解決です」

ElapsedGameTime と ElapsedRealTime の違いは、「ElapsedGameTime」は、最後に Update または Draw ステートメントを入力してからの時間です (使用している「gameTime」に応じて、Update からのものか、Draw からのものかによって異なります)。

ElapsedRealTime はゲーム開始からの時間です。このため、ゲームを実行し続けると直線的に増加します。実際、ロジックは次のようになっているため、1 秒後にすべてのフレームが更新されます。

(説明を簡単にするために、4 fps で実行していると仮定します):

  • フレーム 1: ElapsedRealTime: 0.25。現在の累計: 0.25
  • フレーム 2: ElapsedRealTime: 0.5 現在の合計: 0.75
  • フレーム 3: ElapsedRealTime: 0.75 現在の合計: 1.5
  • 累計が 1 より大きい!!! FPSを見せろ!
  • 実行中の合計を 0 に設定
  • フレーム 4: ElapsedRealTime: 1.00 現在の累計: 1.0
  • 累計が 1 より大きい!!! FPSを見せろ!

カウンターを修正したので、Elapsed Game Time の変化は安定した 0.25 だけになるはずです。

  • フレーム 1: ElapsedGameTime: 0.25。現在の累計: 0.25
  • フレーム 2: ElapsedGameTime: 0.25 現在の合計: 0.50
  • フレーム 3: ElapsedGameTime: 0.25 現在の合計: 0.75
  • フレーム 4: ElapsedGameTime: 0.25 現在の合計: 1.00
  • 累計が 1 より大きい!!! FPSを見せろ!
  • 実行中の合計を 0 に設定

  • フレーム 5: ElapsedGameTime: 0.25。現在の累計: 0.25

  • フレーム 6: ElapsedGameTime: 0.25 現在の合計: 0.50
  • フレーム 7: ElapsedGameTime: 0.25 現在の合計: 0.75
  • フレーム 8: ElapsedGameTime: 0.25 現在の合計: 1.00
  • 累計が 1 より大きい!!! FPSを見せろ!
  • 実行中の合計を 0 に設定

これはあなたが期待しているものです。要するに、最初の問題を修正したので、2 番目の問題も修正する必要があります。「理由」は上記で説明されています。

于 2011-10-25T21:48:47.150 に答える
1

余談ですが...スレッドの優先順位を設定することは避けてください。バックグラウンドスレッドであるはずのスレッドに最高のスレッド優先度を割り当てると、スケジューラーがthreadPathに優先度を与えるため、CPU時間のメインスレッドが不足する可能性があります。

于 2010-03-25T01:34:53.063 に答える
0

IsRunningSlowly が変更されているかどうかを積極的に確認していますか? IsFixedTimeStep が true であっても、プログラムが期待するほど多くの Update を実行できない場合は、より頻繁に呼び出されます。

以前にこれを軽減した方法は、自分で追跡するのではなく、直接 ResetElapsedTime() を呼び出すことです。

ただし、それがうまくいくかどうかはわかりません。以前の問題をデバッグしていたときに、おそらくデバッグ時の「機能」である追加の更新が呼び出されないことに気付きました。

于 2010-04-02T18:22:12.707 に答える
0

draw メソッドの下で fps カウンターを実行する必要があります。

于 2013-07-12T10:51:04.743 に答える
0

こんにちは皆さん、実際のフレームレートを表示したい場合は、メソッドにフレームレート カウンターを実装する必要があります。これはDraw、XNA が次のように行うためです。UpdateDrawUpdate

于 2016-01-29T15:38:14.103 に答える