// 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/

보통 게임을 만들때, 특히 출시까지 고려하게 되면 최적화는 피할 수 없는 문제가 된다.
그런데 내가 처음 게임을 만들때는 이런 기준이 전혀 잡히지 않아서 난감하고 막막했었다.
하지만 물론 게임마다 다르겠지만, 대략적으로 기준을 잡는 방법이 있다
아마 듣고 나면 당연해서 왜 생각 못했지 싶을 것 같다.
아무튼 상당히 명료한 기준이다.
바로 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


