Unity / FPS Counter Script

// Unity Version : 2021.3.5f1


using System.Collections;
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 = 90;
    public byte testFpsFrequency = 1;
    public bool showFps = false;
}

https://forum.unity.com/threads/fps-counter.505495/

https://pubul.tistory.com/203

보통 게임을 만들때, 특히 출시까지 고려하게 되면 최적화는 피할 수 없는 문제가 된다.

그런데 내가 처음 게임을 만들때는 이런 기준이 전혀 잡히지 않아서 난감하고 막막했었다.

하지만 물론 게임마다 다르겠지만, 대략적으로 기준을 잡는 방법이 있다

아마 듣고 나면 당연해서 왜 생각 못했지 싶을 것 같다.

아무튼 상당히 명료한 기준이다.

바로 fps를 기준으로 잡으면 된다.

뭔소리고 하니, 일반적인 게임은 60fps 혹은 120fps정도로 맞춘다.

물론 그 이상의 프레임을 원하는 유저들도 있지만, 보통은 저정도 프레임률이 나오면 보통은 만족한다.

안드로이드는 60fps, pc는 120fps를 기준으로 잡으면 된다.

fps는 초당 프레임률이기 때문에, fps를 역수 취해주면 프레임당 초가 나온다.

1/120= 0.00833…, 따라서 8ms 이내로 하나의 프레임을 처리 할 수 있으면 된다.

60fps로 잡으면 그 두배인 16ms면 이내로 처리하면 될 것이다.

여기부터는 유니티 기준 얘기다.

프레임단위 처리에 소요시간을 기준으로 판단해야한다는 것을 알았다.

이를 확인하는 방법은 여러가지가 있겠으나,

가장 편리한 방법은 유니티 프로파일러를 이용하는 것이다.

나머지는 해당 글에서 확인


UniTask

https://github.com/Cysharp/UniTask

SingletonMonobehaviour.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SingletonMonobehaviour<T> : MonoBehaviour where T : MonoBehaviour
{
    static T _Instance = null;
    public static T Instance
    {
        get
        {
            if (_Instance == null) {
                _Instance = (T)FindObjectOfType(typeof(T));
                if (_Instance == null) {
                    var _NewGameObject = new GameObject(typeof(T).ToString());
                    _Instance = _NewGameObject.AddComponent<T>();
                }
            }
            return _Instance;
        }
    }

    protected virtual void Awake()
    {
        if (_Instance == null)
            _Instance = this as T;
        
        DontDestroyOnLoad(gameObject);
    }
}

ApplicationManager.cs

using UnityEngine;
using Cysharp.Threading.Tasks;
using System.Threading;

public partial class ApplicationManager : SingletonMonobehaviour<ApplicationManager>
{
    [Header("== :: Initial Settings :: ==")]
    [SerializeField]
    byte TargetFPS;

    protected override void Awake()
    {
        base.Awake();

        ActiveFPSDebugger();
        ChangeTargetFrameRate(TargetFPS);
    }
}

#region == :: FPS Debuger Region :: ==

public partial class ApplicationManager
{
    public float CurFPS_f { get; private set; }
    public string CurFPS_str => string.Format($"FPS : {CurFPS_f:0.0}");
    public bool IsActiveFPSDebuger 
        => FPSTask.Status == UniTaskStatus.Pending;


    byte _FpsFrequency = 1;

    GUIStyle _GUIStyle = new();

    Rect _Rect;

    UniTask FPSTask { get; set; }

    CancellationTokenSource FPSTaskCancelTokenSource { get; set; }


    void OnGUI()
    {
        if (!IsActiveFPSDebuger)
            return;
        GUI.Label(_Rect, CurFPS_str, _GUIStyle);
    }

    public void ChangeTargetFrameRate(byte _targetFrame)
    {
        QualitySettings.vSyncCount = 0;
        Application.targetFrameRate = _targetFrame;
#if (UNITY_EDITOR)
        Debug.LogFormat($"<Color=red>Change the TargetFrame.{_targetFrame}</color>");
#endif
    }
    public void ActiveFPSDebugger()
    {
        if (IsActiveFPSDebuger)
        {
#if (UNITY_EDITOR)
            Debug.LogFormat($"<Color=red>FPS Debugger is already running.</color>");
#endif
            return;
        }
        FPSTaskCancelTokenSource = new CancellationTokenSource();
        FPSTask = FPSDebuggerTask();

        _GUIStyle.alignment = TextAnchor.UpperLeft;
        _GUIStyle.fontSize = Screen.height / 25;
        _GUIStyle.normal.textColor = new(0, 200, 0, 255);
        _GUIStyle.fontStyle = FontStyle.Bold;
        _Rect = new(Screen.width / 20, Screen.height / 20, 100, 100);
    }
    public void DeActiveFPSDebugger()
    {
        if (!IsActiveFPSDebuger)
        {
#if (UNITY_EDITOR)
            Debug.LogFormat($"<Color=red>FPS Debugger is not running.</color>");
#endif
            return;
        }
        FPSTaskCancelTokenSource.Cancel();
    }

    private async UniTask FPSDebuggerTask()
    {
        float _elapsedTime = 0f;
        int _frameCount = 0;

        var _Token = FPSTaskCancelTokenSource.Token;

        while (!_Token.IsCancellationRequested)
        {
            await UniTask.DelayFrame(_FpsFrequency);
            _frameCount++;
            _elapsedTime += Time.deltaTime;

            if (_elapsedTime >= _FpsFrequency)
            {
                CurFPS_f = _frameCount / _elapsedTime;
                _frameCount = 0;
                _elapsedTime = 0f;
            }
        }
    }
}

#endregion

댓글 달기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

Scroll to Top