XNA / MonoGame:获取每秒帧数

时间:2013-12-19 07:54:08

标签: c# xna frame-rate monogame

我正在尝试获取我游戏的当前FPS,但是我只能找到每秒更新FPS变量的方法。例如。 https://github.com/CartBlanche/MonoGame-Samples/blob/master/Draw2D/FPSCounterComponent.cshttp://www.david-amador.com/2009/11/how-to-do-a-xna-fps-counter/

有没有办法持续更新FPS标签?

5 个答案:

答案 0 :(得分:26)

这是我刚才写的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;
    }
}

您需要做的就是在主Game类中创建一个成员变量..

    private FrameCounter _frameCounter = new FrameCounter();

然后在Game的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
    }

享受! :)

答案 1 :(得分:5)

您可以使用以下公式在任何给定时刻获取当前帧速率:

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

下面介绍的其他两种方法都会为您提供修改后的帧速率,使其更平滑,并且返回值的波动更少

这个通过在对数刻度上加权所有先前的帧时间来工作。我不喜欢使用平均值来获得游戏性能的指标,因为framedrops没有表现得很好(或者如果你有很高的平均值),非常低/非常高的帧率有很大的不同如果它们以相同的平均值运行,则准确度如何。

为了解决这个问题,我制作了一个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类一样。

感谢阅读,我希望这可以帮助别人。

答案 2 :(得分:1)

使用双倍来衡量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;
}

我没有测试代码,但你应该明白这个想法。

答案 3 :(得分:0)

您可能尝试在每次绘制操作中更新FPS。这将是非常不稳定的价值,因为有时您需要更多,有时更少。为了使价值更稳定 - 从许多价值中取平均值。

计算FPS的最简单方法可能是计算图纸。而且具有定时器功能的每秒可以说这个值,计算新的fps和清除计数器。使这个计时器更长(例如2秒),或者只是取最后10个fps值并显示平均值。

在一些游戏中,我看到他们也计算了重绘场景所需的最长时间。这也可能是一个有趣的价值。为此,您可以测量绘制函数需要多长时间:在开始时记录时间,最后记录时间差异。

注意,如果启用同步,则延迟绘制操作直到下次同步,这可能是奇怪结果的原因?

答案 4 :(得分:0)

更新每个Draw()的简单方法是:

frameRate = 1 / gameTime.ElapsedGameTime.TotalSeconds;

如果您愿意,也可以在Update()方法中删除它以查看触发频率。

如果你想减慢更新的速度......

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