Home > Software design >  Is this a correct way to implement FPS counter in Unity?
Is this a correct way to implement FPS counter in Unity?

Time:02-03

My code calculates average FPS. Every 0.4s it updates the FPS text. Every 2s it resets the number of average FPS. I need to calculate accurate and smooth FPS (so any temporary spikes will be eliminated). So is this FPS counter code will follow my requirements?

[RequireComponent(typeof(TMP_Text))]
public class FpsCounter : MonoBehaviour
{
    private struct AverageFloat
    {
        public float Average => _value / _count;

        private float _value;
        private int _count;

        public void Add(float number)
        {
            _value  = number;
            _count  ;
        }

        public void Reset()
        {
            _value = 0f;
            _count = 0;
        }
    }

    [SerializeField] private float _averageValueCollectionTime = 2f;
    [SerializeField] private float _refreshFrequency = 0.4f;

    private float _timeSinceFpsReset = 0f;
    private float _timeSinceUpdate = 0f;
    private AverageFloat _fpsValue;

    private TMP_Text _text;

    private void Start()
    {
        _text = GetComponent<TMP_Text>();
    }

    private void Update()
    {
        if (_timeSinceFpsReset > _averageValueCollectionTime)
        {
            _timeSinceFpsReset = 0;
            _fpsValue.Reset();
        }

        _fpsValue.Add(1f / Time.unscaledDeltaTime);
        _timeSinceFpsReset  = Time.deltaTime;

        if (_timeSinceUpdate < _refreshFrequency)
        {
            _timeSinceUpdate  = Time.deltaTime;
            return;
        }

        int fps = Mathf.RoundToInt(_fpsValue.Average);
        _text.text = fps.ToString();

        _timeSinceUpdate = 0f;
    }
}

CodePudding user response:

You could use InvokeRepeating in order to reset / refresh your number of average FPS, in addition to Time.frameCount to get the number of frames.

CodePudding user response:

So... It's better to calculate FPS with exponentially weighted moving average. This solution gives more accurate and smooth results.

[RequireComponent(typeof(TMP_Text))]
public class FpsCounter : MonoBehaviour
{
    [SerializeField] [Range(0f, 1f)] private float _expSmoothingFactor = 0.9f;
    [SerializeField] private float _refreshFrequency = 0.4f;

    private float _timeSinceUpdate = 0f;
    private float _averageFps = 1f;

    private TMP_Text _text;

    private void Start()
    {
        _text = GetComponent<TMP_Text>();
    }

    private void Update()
    {
        // Exponentially weighted moving average (EWMA)
        _averageFps = _expSmoothingFactor * _averageFps   (1f - _expSmoothingFactor) * 1f / Time.unscaledDeltaTime;

        if (_timeSinceUpdate < _refreshFrequency)
        {
            _timeSinceUpdate  = Time.deltaTime;
            return;
        }

        int fps = Mathf.RoundToInt(_averageFps);
        _text.text = fps.ToString();

        _timeSinceUpdate = 0f;
    }
}

_expSmoothingFactor must be set from 0 to 1. It determines how much smoothing will be applied. 0.9 value is most optimal (higher value = more smoothing).

  • Related