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?
Ответы
Ответ 1
Вот класс счетчиков 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();
И вызовите метод Update в методе Game 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
}
Наслаждайтесь!:)
Ответ 2
Вы можете получить текущую частоту кадров в любой момент, используя следующую формулу:
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 выше.
Спасибо за чтение, надеюсь, это поможет кому-то.
Ответ 3
Используйте 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;
}
Я не тестировал код, но вы должны получить эту идею.
Ответ 4
Вероятно, вы пытаетесь обновить FPS в каждой операции ничьей. Это будет очень неустойчивое значение, поскольку иногда вам нужно больше, а иногда и меньше. Чтобы сделать значение более стабильным - выведите среднее значение из многих значений.
Самый простой способ подсчета FPS - это, вероятно, подсчет рисунков. И есть функция тайм-багов, которая принимает это значение, скажем каждую секунду, вычисляет новый fps и очищает счетчик. Сделайте этот таймер более длинным (например, 2 сек) или просто возьмите последние 10 кадров в секунду и отобразите среднее значение.
В некоторых играх я видел, что они также подсчитывают максимальное время, необходимое для перерисовки сцены. Это тоже может быть интересным. Для этого вы можете измерить, как долго выполняется функция draw: зарегистрируйте время при начале и в конце, разница будет временем.
Обратите внимание, что если вы включите синхронизацию, вы отложите операцию рисования до следующей синхронизации, возможно, это причина странных результатов?
Ответ 5
Простым методом, который обновляет каждый Draw(), будет:
frameRate = 1 / gameTime.ElapsedGameTime.TotalSeconds;
Вы также можете отказаться от этого метода Update(), чтобы узнать, как часто это срабатывает, если хотите.
Если вы хотите замедлить скорость обновления...
frameRate += (((1 / gameTime.ElapsedGameTime.TotalSeconds) - frameRate) * 0.1);