You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity中该FPS计数器实现方案是否符合精准平滑的需求?

关于FPS统计代码的有效性疑问

我编写了一段用于计算平均FPS的代码,设置每0.4秒更新FPS文本显示,每2秒重置平均FPS统计。我需要实现精准且平滑的FPS统计(消除临时帧率波动),请问这段代码是否能满足我的需求?

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

代码分析与结论

这段代码基本能满足平滑FPS统计的需求,但在精准性上有几个细节可以优化:

  • 平滑性达标:通过2秒窗口内的FPS平均值过滤临时波动,每0.4秒更新一次显示,不会出现频繁跳变,符合平滑要求。
  • 精准性的潜在问题:
    1. 计时与统计基准不一致:统计FPS用Time.unscaledDeltaTime(不受游戏时间缩放影响),但计时用Time.deltaTime,如果游戏开启慢动作或暂停,重置和更新时机就会出错。
    2. 统计窗口错位:当前用_timeSinceFpsReset > _averageValueCollectionTime触发重置,会导致实际统计窗口略长于设定的2秒,比如可能到2.1秒才重置,统计的是过去2.1秒的FPS。
    3. 除以0风险:AverageFloat的Average属性在_count为0时会触发除以0错误,虽然实际运行中概率极低,但仍需规避。

优化后的代码

[RequireComponent(typeof(TMP_Text))]
public class FpsCounter : MonoBehaviour
{
    private struct AverageFloat
    {
        // 增加_count为0的判断,避免除以0
        public float Average => _count == 0 ? 0f : _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()
    {
        float unscaledDelta = Time.unscaledDeltaTime;

        // 统一用unscaledDeltaTime计时,避免时间缩放影响
        _timeSinceFpsReset += unscaledDelta;
        // 用>=判断,并且保留超出的时间,让统计窗口精准对齐
        if (_timeSinceFpsReset >= _averageValueCollectionTime)
        {
            _timeSinceFpsReset -= _averageValueCollectionTime;
            _fpsValue.Reset();
        }

        _fpsValue.Add(1f / unscaledDelta);

        _timeSinceUpdate += unscaledDelta;
        if (_timeSinceUpdate < _refreshFrequency)
        {
            return;
        }

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

        _timeSinceUpdate = 0f;
    }
}

优化后的代码解决了上述问题,能精准控制统计周期,不受游戏时间缩放影响,同时保留了原有的平滑效果,完全满足你对精准且平滑FPS统计的需求。


内容的提问来源于stack exchange,提问作者purepelmen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 09:45:42