XNA / MonoGame: получение кадров в секунду

Я пытаюсь получить текущий 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?

5 ответов


вот класс счетчика 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 вашей игры и нарисуйте метку, как вам нравится..

    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
    }

наслаждайтесь! :)


вы можете получить текущую частоту кадров в любой момент, используя эту формулу:

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

вызовите метод обновления в любом месте, которое будет запускаться каждый кадр:

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 выше.

Спасибо за чтение, я надеюсь, что это помогает кто-то.


используйте double для измерения 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;
}

Я не тестировал этот код, но вы должны уловить идею.


вы, вероятно, пытаетесь обновить FPS в каждой операции рисования. Это будет очень нестабильное значение, так как иногда нужно больше, иногда меньше. Чтобы сделать значение более стабильным-возьмите среднее из многих значений.

самый простой способ подсчета кадров в секунду, вероятно, подсчет чертежей. И есть функция timer-bases, которая принимает это значение, позволяет сказать каждую секунду, рассчитать новый fps и очистить счетчик. Сделайте этот таймер длиннее (например, 2 сек) или просто возьмите последние 10 значений fps и отобразите средний.

в некоторых играх я видел, что они также считают максимальное время, необходимое для перерисовки сцены. Это тоже может представлять интерес. Для этого вы можете измерить, сколько времени занимает функция draw: зарегистрируйте время в начале и в конце, разница будет временем.

обратите внимание, если вы включаете синхронизацию, вы откладываете операцию рисования до следующей синхронизации, возможно, это причина странных результатов?


простой метод, который обновляет каждый Draw () будет:

frameRate = 1 / gameTime.ElapsedGameTime.TotalSeconds;

вы также можете удалить это в методе Update (), чтобы увидеть, как часто это срабатывает, если хотите.

Если вы хотите замедлить, как быстро он обновляется...

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