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; } } 22 Answers
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.
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).