Hello everybody. I have made myself this simple fps counter script that display's the current frame count in a simple ui text. So my question is kind of weird, but does this script display's accurate frame rate? Or are there any better ways checking the current frame rate? I just want to make sure I'm not seeing false thing on my screen. Cause sometimes I see pretty high fps and I just want to make sure it's actually true or not. Thank you. The Script: Code (CSharp): using UnityEngine; using UnityEngine.UI; public class FPSDisplay : MonoBehaviour { public int avgFrameRate; public Text display_Text; public void Update () { float current = 0; current = Time.frameCount / Time.time; avgFrameRate = (int)current; display_Text.text = avgFrameRate.ToString() + " FPS"; } }
This should do it. Code (csharp): current = (int)(1f / Time.unscaledDeltaTime); Here's a full script. Just drag in your own Text (Legacy) component. https://gist.github.com/st4rdog/80057b406bfd00f44c8ec8796a071a13 Code (csharp): using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class FPSCounter : MonoBehaviour { public Text Text; private Dictionary<int, string> CachedNumberStrings = new(); private int[] _frameRateSamples; private int _cacheNumbersAmount = 300; private int _averageFromAmount = 30; private int _averageCounter = 0; private int _currentAveraged; void Awake() { // Cache strings and create array { for (int i = 0; i < _cacheNumbersAmount; i++) { CachedNumberStrings[i] = i.ToString(); } _frameRateSamples = new int[_averageFromAmount]; } } void Update() { // Sample { var currentFrame = (int)Math.Round(1f / Time.smoothDeltaTime); // If your game modifies Time.timeScale, use unscaledDeltaTime and smooth manually (or not). _frameRateSamples[_averageCounter] = currentFrame; } // Average { var average = 0f; foreach (var frameRate in _frameRateSamples) { average += frameRate; } _currentAveraged = (int)Math.Round(average / _averageFromAmount); _averageCounter = (_averageCounter + 1) % _averageFromAmount; } // Assign to UI { Text.text = _currentAveraged switch { var x when x >= 0 && x < _cacheNumbersAmount => CachedNumberStrings[x], var x when x >= _cacheNumbersAmount => $"> {_cacheNumbersAmount}", var x when x < 0 => "< 0", _ => "?" }; } } }
This will display the FPS in UI.Text updated once per second. Tweak the HudRefreshRate to refresh the FPS text in a different rate: Code (CSharp): using UnityEngine; using UnityEngine.UI; public class FpsCounter : MonoBehaviour { [SerializeField] private Text _fpsText; [SerializeField] private float _hudRefreshRate = 1f; private float _timer; private void Update() { if (Time.unscaledTime > _timer) { int fps = (int)(1f / Time.unscaledDeltaTime); _fpsText.text = "FPS: " + fps; _timer = Time.unscaledTime + _hudRefreshRate; } } }
This one's similar to the others with a minor difference in format. I feel like any of these work really well, lots of great coders out there! This one in particular allows an easy edit to string format, refresh rate, and time-lapse lets you switch between deltaTime, smoothDeltaTime of fixedDeltaTime to see the difference. It's not any better than other examples, just another fun option with more to play around with. Code (CSharp): public class FPSCounter : MonoBehaviour { public float timer, refresh, avgFramerate; string display = "{0} FPS"; private Text m_Text; private void Start() { m_Text = GetComponent<Text>(); } private void Update() { //Change smoothDeltaTime to deltaTime or fixedDeltaTime to see the difference float timelapse = Time.smoothDeltaTime; timer = timer <= 0 ? refresh : timer -= timelapse; if(timer <= 0) avgFramerate = (int) (1f / timelapse); m_Text.text = string.Format(display,avgFramerate.ToString()); } }
Code (CSharp): //Below is slightly more solid (Just being nitpicky, sorry) InvokeRepeating(nameof(GetFPS), 1,1);
Hi, thanks for share your codes, i did it but the editor shows 150 fps while the script is showing 90 fps, so, is this script accurate?
Editor shows "fake" FPS because it subtracts editor overhead time, this script shows actual FPS in editor window. But your FPS in build will be closer to the stats window FPS.
this is a way to make a good performance gor your game guys int fps fps = bla bla bla Time.timeScale = _fps / 60; if (Time.timeScale > 1) { Time.timeScale = 1; } //thats mean if fps > 60 the speed if the game will be 1 wich mean normal speed and if fps < 60 the speed will be fps count / 60 and that let the cpu and gpu do his process in a easy way
maybe yes but in some situations its really help on performance maybe not your project it could be any project
It doesn't improve performance at all, unless you're doing a lot of FixedUpdate calculations. https://forum.unity.com/threads/timescale-affects-performance.18049/
Since InvokeRepeating is generally not recommended, you can use IEnumertor Start() instead Code (CSharp): using System.Collections; using UnityEngine; namespace Tools { public class FpsCounter : MonoBehaviour { public int Fps { get; private set; } [SerializeField] private float fpsRefreshTime = 1f; private WaitForSecondsRealtime _waitForSecondsRealtime; private void OnValidate() { SetWaitForSecondsRealtime(); } private IEnumerator Start() { SetWaitForSecondsRealtime(); while (true) { fps = (int)(1 / Time.unscaledDeltaTime); yield return _waitForSecondsRealtime; } } private void SetWaitForSecondsRealtime() { _waitForSecondsRealtime = new WaitForSecondsRealtime(fpsRefreshTime); } } }
This script shows it accurately, it works on mobile, it uses the GUI. It divides the framecount by the time passed, it can be configure to update at any interval. Code (CSharp): using System.Collections; using System.Collections.Generic; using UnityEngine; public class ShowFPSMobile : MonoBehaviour { // provided by Niter88 private string fps = ""; private WaitForSecondsRealtime waitForFrequency; GUIStyle style = new GUIStyle(); Rect rect; bool isInicialized = false; private void Awake() { //float fraction = 0.5f; // Render at half the resolution of current screen //float fraction = 0.8f; //float fraction = 1f; //Screen.SetResolution((int)(Screen.currentResolution.width * fraction), (int)(Screen.currentResolution.height * fraction), true); //don't use vsync on mobile, limit fps instead // Sync framerate to monitors refresh rate //Use 'Don't Sync' (0) to not wait for VSync. Value must be 0, 1, 2, 3, or 4 QualitySettings.vSyncCount = 0; // Disable screen dimming Screen.sleepTimeout = SleepTimeout.NeverSleep; //Inicialize(true); //use for testing on editor } private IEnumerator FPS() { int lastFrameCount; float lastTime; float timeSpan; int frameCount; for (; ; ) { // Capture frame-per-second lastFrameCount = Time.frameCount; lastTime = Time.realtimeSinceStartup; yield return waitForFrequency; timeSpan = Time.realtimeSinceStartup - lastTime; frameCount = Time.frameCount - lastFrameCount; fps = string.Format("FPS: {0}", Mathf.RoundToInt(frameCount / timeSpan)); } } void OnGUI() { // Display GUI.Label(rect, fps, style); //GUI.Label(new Rect(Screen.width - 110, 5, 0, Screen.height * 2 / 100), fps, style); //GUI.Label(new Rect(10, 10, Screen.width, Screen.height * 2 / 100), fps, style); //GUI.Label(new Rect(Screen.width - 100, 10, 150, 20), fps, style); } private void Inicialize(bool showFps) { isInicialized = true; style.alignment = TextAnchor.UpperLeft; style.fontSize = Screen.height * 3 / 100; style.normal.textColor = new Color32(0, 200, 0, 255); rect = new Rect(Screen.width * 90 / 100, 5, 0, Screen.height * 2 / 100); if(showFps) StartCoroutine(FPS()); } public void SetNewConfig(GraphicSettingsMB gSettings) { Application.targetFrameRate = gSettings.targetFrameRate; waitForFrequency = new WaitForSecondsRealtime(gSettings.testFpsFrequency); if (!isInicialized) Inicialize(gSettings.showFps); if (!gSettings.showFps) Destroy(this.gameObject); } } [SerializeField] public class GraphicSettingsMB { public byte targetFrameRate = 30; public byte testFpsFrequency = 1; public bool showFps = false; }
just run this function in Update() and you have an fps counter: void fpsCounter() { if (timer < 1) { timer += Time.deltaTime; frameRate += 1; } else { Debug.Log("FPS: " + frameRate.ToString()); timer = 0; frameRate = 0; } }
How do i acutally use any of those scripts? I created a new one but what component of my scene should i add the script to? Or does this work differently?
Here's the script from the top, but i modified the text to work with TextMeshPro. Just add the script to an empty gameobject or whatever you'd like honestly, then drag a UI text from your canvas ( edit: I added an additional text indicator which will now read as, "FPS: (however many frames)" ) ( another edit: I added a space to "FPS: " ... I missed that somehow)" ) Code (CSharp): using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using TMPro; public class FPSCounter : MonoBehaviour { private TextMeshProUGUI Text; public TextMeshProUGUI FPS_Text; private Dictionary<int, string> CachedNumberStrings = new(); private int[] _frameRateSamples; private int _cacheNumbersAmount = 300; private int _averageFromAmount = 30; private int _averageCounter = 0; private int _currentAveraged; void Awake() { // Cache strings and create array { for (int i = 0; i < _cacheNumbersAmount; i++) { CachedNumberStrings[i] = i.ToString(); } _frameRateSamples = new int[_averageFromAmount]; } } void Update() { // Sample { var currentFrame = (int)Math.Round(1f / Time.smoothDeltaTime); // If your game modifies Time.timeScale, use unscaledDeltaTime and smooth manually (or not). _frameRateSamples[_averageCounter] = currentFrame; } // Average { var average = 0f; foreach (var frameRate in _frameRateSamples) { average += frameRate; } _currentAveraged = (int)Math.Round(average / _averageFromAmount); _averageCounter = (_averageCounter + 1) % _averageFromAmount; } // Assign to Private Text value { Text.text = _currentAveraged < _cacheNumbersAmount && _currentAveraged > 0 ? CachedNumberStrings[_currentAveraged] : _currentAveraged < 0 ? "< 0" : _currentAveraged > _cacheNumbersAmount ? $"> {_cacheNumbersAmount}" : "-1"; } // Assign to UI with additional text indicator { FPS_Text.text = "FPS: " + Text.text; } } } }
This is my version which takes an average over 60 frames Code (CSharp): public class FPSDisplay : MonoBehaviour { public TextMeshProUGUI fpsText; private int _frameCount; private int _totalFPS; void Update() { _frameCount++; _totalFPS += (int)Math.Round(1f / Time.unscaledDeltaTime); if (_frameCount % 60 == 0) { fpsText.text = "FPS : " + _totalFPS / _frameCount; _totalFPS = 0; _frameCount = 0; } } }
I'll throw my method in here, too. It captures the frame duration every frame but smoothes the result with recent frames having a higher weighting. Code (CSharp): public class FPSExample : MonoBehaviour { // The decay needs to be above 1.0. // The larger this value is, the faster the influence from the past will decay. const float decayVal = 1.5f; float accumWt = 0.0f; float accum = 0.0f; void OnGUI() { float wtAvgDT = accum/accumWt; GUILayout.Label((1.0f/ wtAvgDT).ToString()); } private void Update() { // Decay the accumulations. // The ratio of this.accum/this.accumWt doesn't change from this - but the // values are now smaller compared to the new values we're going to add // in - and thus have a smaller weighting. this.accum /= decayVal; this.accumWt /= decayVal; this.accum += Time.unscaledDeltaTime; this.accumWt += 1.0f; } } @Kartik_R You should probably subtract 1 from timer instead of setting it to zero, or else you're throwing away fractions of a second.
For the minimalists with good eyesight: Code (CSharp): void OnGUI() { GUI.Label(new Rect(10, 10, 100, 20), (1/Time.smoothDeltaTime).ToString("f0")); }