Вопрос

I am trying to get the current FPS of my game, however I can only find methods that updates the FPS variable every second. E.g. https://github.com/CartBlanche/MonoGame-Samples/blob/master/Draw2D/FPSCounterComponent.cs and http://www.david-amador.com/2009/11/how-to-do-a-xna-fps-counter/

Is there a way to have a continuously updating FPS label?

Это было полезно?

Решение

Here's an FPS counter class I wrote a while ago. You should be able to just drop it in your code and use it as is..

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;
    }
}

All you need to do is create a member variable in your main Game class..

    private FrameCounter _frameCounter = new FrameCounter();

And call the Update method in your Game's Draw method and draw the label however you like..

    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
    }

Enjoy! :)

Другие советы

You can get the current framerate at any given moment using this formula:

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

Both of the other methods presented below give you a modified framerate, intended to be smoother, and have fewer fluctuations in the return value

This one works by weighting all previous frametimes on a logarithmic scale. I didn't like the idea of using an average to get a metric on game performance as framedrops aren't represented well (or at all if you have a high average) and very low/very high framerates have vastly different levels of accuracy if they are running on the same average.

To solve this, I made a SmartFramerate class (terrible name, I know)

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;
    }
}

You set the weight when you create the variable: (a higher weight is more accurate to the instantaneous framerate, a lower weight is smoother. I find that 3-5 is a good balance)

SmartFramerate smartFPS = new SmartFramerate(5);

Call the Update method anywhere that will be run every frame:

smartFPS.Update(gameTime.ElapsedGameTime.TotalSeconds);

The current framerate may be accessed like so:

smartFPS.framerate

or printed like so:

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

(I'm putting it into a custom print class, so I apologize if the syntax looks funky)

However, if you wish to simply average a certain number of frames together, then this class is the most efficient way I have come up with to do so.

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];
    }
}

To use it, simply initialize a SmoothFramerate variable where ever you wish to use it, passing the amount of frames you want averaged:

SmoothFramerate smoothFPS = new SmoothFramerate(1000);

Update, access, and print the current framerate exactly as you would using the SmartFramerate class above.

Thanks for reading, I hope this helps someone out.

A simple method that updates every Draw() would be:

frameRate = 1 / gameTime.ElapsedGameTime.TotalSeconds;

You could also drop that in the Update() method to see how often that fires too, if you wanted.

If you want to slow down how fast it updates...

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

Use a double to measure fps:

double frameRate = 0.0;

Modify the method Update as follows:

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

I didn't test the code but you should get the idea.

You probably trying to update FPS in every draw operation. This will be very unstable value, as sometimes you need more, sometimes less. To make value more stable - take average out of many values.

The easiest way to count FPS is probably count drawings. And have timer-bases function what take this value lets say every second, calculate new fps and clear counter. Make this timer longer (to example 2 sec), or simply take last 10 fps values and display average.

In some games I seen they also count maximum time needed to redraw the scene. This could be an interesting value too. For this you can measure how long draw function takes: register the time at start of it and at the end, difference would be the time.

Take note, if you enable sync, you delay draw operation until next sync, perhaps this is the reason of strange results?

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top