[ 초당 프레임 (퍼포먼스 측정)]
Use physics to create an ever-growing atomic nucleus.
Use the profiler to investigate performance.
Measure and display the frame rate.
Stabilize the frame rate by averaging multiple frames.
Colorize the frame rate display.
[ Building an Atomic Nucleus ]
씬의 중심을 기준으로 atom(sphere)이 모여드는 형태 구현
Neutron(중성자), Proton(양성자)
중성자 만들기
Menu : GameObject >> 3D Object >> Sphere
Name : Neutron
Project Window : Create >> C#
Name : Nucleon
[RequireComponent(typeof(Rigidbody))]
public class Nucleon : MonoBehaviour {
public float attractionForce;
Rigidbody body;
void Awake () {
body = GetComponent<Rigidbody>();
}
void FixedUpdate () {
// 자기 자신의 위치의 반대 방향(음수값)으로 힘들 가하게 되어 결과적으로 (0,0,0)을 기준으로 진자 운동을 하게 된다.
body.AddForce(transform.localPosition * -attractionForce);
}
}
[ RequireComponent ... ]
유니티가 자동으로 사용하려는 Component를 삽입해 줌
Nucleon 드래그 >> (to) Neutron
Script 변수 : Attraction Force >> 5
Neutron Inspector
Rigidbody >> Use Gravity : Uncheck
Project Window : Create >> Material
Name : Neutron
Albedo : RGBA(0, 0, 255, 255)
Neutron(Material) 드래그 >> (to) Neutron(GameObject)
[ Rigidbody ]
양성자 만들기
Neutron(GameObject) 복사(Ctrl+D)
이름 바꾸기 : Proton
Project Window : Create >> Material
Name : Proton
Albedo : RGBA(255, 0, 0, 255)
Proton(Material) 드래그 >> (to) Proton(GameObject)
Proton & Neutron 프리팹 만들기
Nucleon 생성기 만들기
Menu : GameObject >> Create Empty
Name : NucleonSpawner
Project Window : Create >> C#
Name : NucleonSpawner
NucleonSpawner(Script) 드래그 >> (to) NucleonSpawner(GameObject)
Neutorn(prefab) 드래그 >> (to) NucleonSpawner(Script) : NucleonPrefabs >> Element 0
Proton(prefab) 드래그 >> (to) NucleonSpawner(Script) : NucleonPrefabs >> Element 1
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NucleonSpawner : MonoBehaviour {
public float timeBetweenSpawns; // neucleon이 생성되는 시간 간격
public float spawnDistance; // neucleon이 생성되는 거리
public Nucleon[] nucleonPrefabs; // neucleon 프리팹을 연결한 참조 배열
float timeSinceLastSpawn;
void FixedUpdate () {
// timeBetweenSpawns 시간마다 SpawnNucleon()을 실행
timeSinceLastSpawn += Time.deltaTime;
if (timeSinceLastSpawn >= timeBetweenSpawns) {
timeSinceLastSpawn -= timeBetweenSpawns;
SpawnNucleon();
}
}
void SpawnNucleon () {
Nucleon prefab = nucleonPrefabs[Random.Range(0, nucleonPrefabs.Length)];
Nucleon spawn = Instantiate<Nucleon>(prefab);
// 랜덤으로 spawnDistance거리만큼 떨어진 위치를 생성
spawn.transform.localPosition = Random.onUnitSphere * spawnDistance;
}
}
FixedUpdate 시간간격 조정
Menu : Edit >> Project Setting >> Time
Fixed Timestep : Fixedupdate 시간 간격 조정
TimeScale : 1인 경우가 실시간 (0.1인 경우 10배 느려짐)
Rigidbody : Interploate
물리 업데이트 사이에 게임 오브젝트(리지드바디)의 움직임을 보간하는 방법
FixedUpdate의 주기가 길어서 끊기는 현상이 발생할 경우, 이 부분을 통해 부드럽게 움직이는 것을 표현할 수 있음
[ 2 Using the Profiler ]
Profiler
CPU와 그래픽카드 등의 시스템 하드웨어에 어떻게 시간과 작업 부하가 분포되는지 탑다운 방식의 통계를 보여줌
Menu : Window >> Profiler
v-sync (수직 동기화)
컴퓨터 디스플레이에서 그래픽 카드의 프레임 생성과 모니터의 프레임 출력 타이밍을 맞추도록 하는 설정
사용하지 않는 경우 화면 현상의 찌그러짐과 같은 tilt 현상이 발생
유니티에서 v-sync를 Enable하지 않으면, 최상의 포퍼먼스의 Frame rate를 보여줌
Menu : Edit >> Project Settings >> Quality
V Sync Count : Don't Sync
Script에서도 수행이 가능함 ( Application.targetFrameRate = -1)
우리가 작성한 씬을 실행시키면, CPU Usage와 GC Allocated에서 예기치 않은 과부하가 주기적으로 보임
유니티 Editor에 의해서 발생한 현상
Standalone Build의 수행으로 Game View에서만 Profiler를 사용
Menu : Bulid Settings ...
GC Allocated
Garbage Collection 에서 메모리를 할당하고 삭제하는 부분
[ 3 Measuring Frames Per Second ]
Stats에서 보여주는 초당 프레임은 한 프레임당 흐른 시간을 1에서 나눈 값을 표현하는 것
초당 프레임을 구현하기 위해서 1초간 누적된 프레임을 출력할 수 있도록 할 예정
FPSCounter 생성
한 프레임당 걸린 시간을 1에서 나눈 값
Project Window : Create >> C#
Name : FPSCounter
using UnityEngine;
public class FPSCounter : MonoBehaviour {
public int FPS { get; private set; }
/*
int fps;
public int FPS {
get { return fps; }
private set { fps = value; }
}
*/
void Update () {
// FPS = (int)(1f / Time.deltaTime);
FPS = (int)(1f / Time.unscaledDeltaTime);
}
}
[C# property ]
멤버 변수인 것처럼 보여 주도록 하는 메소드(함수)로 사용상 편하게 해주는 역할
Time.unscaledDeltaTime
Time.deltaTime은 한 프레임에서 다음 프레임까지 걸린 시간을 의미하지만, Time Scale에 영향을 받는 값으로 현실의 시간은 아님
Time.unscaledDeltaTime은 실질적인 한 프레임이 실행된 시간을 담고 있음(Time Scale에 영향을 받지 않음)
Unity UI 만들기
FPS를 보여줄 UI 만들기
Menu : GameObject >> UI >> Canvas
EventSystem 이 같이 만들어지지만, 우리는 단지 출력만을 위한 UI를 만들 예정이니, 삭제해도 됨
Canvas Inspector
Pixel Perfect : Check (이미지 픽셀과 1:1 대응)
Menu : GameObject >> UI >> Panel
anchor : 왼쪽 위
pivot (0, 1)
Color (0,0,0,125?)
Menu : GameObject >> UI >> Text
Text : 00
Font Style : Bold Text
Alignment : 가로, 세로 중간
Color : (255,255,255,255)
FPSDisplayer 만들기
Project Window : Create >> C#
Name : FPSDisplayer
using UnityEngine;
using UnityEngine.UI;
[RequireComponent(typeof(FPSCounter))]
public class FPSDisplay : MonoBehaviour {
public Text fpsLabel;
FPSCounter fpsCounter;
void Awake () {
fpsCounter = GetComponent<FPSCounter>();
}
void Update () {
//fpsLabel.text = fpsCounter.FPS.ToString();
// 99를 벗어나면 99로 표기할 수 있도록 한다.
fpsLabel.text = Mathf.Clamp(fpsCounter.FPS, 0, 99).ToString();
}
}
FPSDisplayer 드래그 >> (to) FPS Panel
FPS Label 드래그 >> (to) FPS Panel Inspector >> FPS Display (Script) : FPS Label
Profiler
FPS를 출력하기 위한 임시 텍스트 생성에 따른 동적 메모리 할당(ToString() 함수에 의해서 문자열이 생성되고, Garbage가 됨)
static string[] stringsFrom00To99 = {
"00", "01", "02", "03", "04", "05", "06", "07", "08", "09",
"10", "11", "12", "13", "14", "15", "16", "17", "18", "19",
"20", "21", "22", "23", "24", "25", "26", "27", "28", "29",
"30", "31", "32", "33", "34", "35", "36", "37", "38", "39",
"40", "41", "42", "43", "44", "45", "46", "47", "48", "49",
"50", "51", "52", "53", "54", "55", "56", "57", "58", "59",
"60", "61", "62", "63", "64", "65", "66", "67", "68", "69",
"70", "71", "72", "73", "74", "75", "76", "77", "78", "79",
"80", "81", "82", "83", "84", "85", "86", "87", "88", "89",
"90", "91", "92", "93", "94", "95", "96", "97", "98", "99"
};
void Update () {
fpsLabel.text = stringsFrom00To99[Mathf.Clamp(fpsCounter.FPS, 0, 99)];
}
이미 만들어진 Text를 활용해 사용할 수 있도록 함
[ 4 Averaging Frames Per Second ]
매 프레임마다 업데이트 되는 FPS 보다는 평균적인 값을 업데이트할 수 있도록 하여 가독성을 높임
기존의 FPS명을 AverageFPS로 변경하고, frameRange에 설정한 값(60) 만큼에 대한 FPS의 값들에 대한 평균 값을 구하도록 함
using UnityEngine;
public class FPSCounter : MonoBehaviour {
public int frameRange = 60;
public int AverageFPS { get; private set; }
int[] fpsBuffer;
int fpsBufferIndex;
void Update () {
if (fpsBuffer == null || fpsBuffer.Length != frameRange) {
InitializeBuffer();
}
UpdateBuffer();
CalculateFPS();
}
void InitializeBuffer () {
if (frameRange <= 0) {
frameRange = 1;
}
fpsBuffer = new int[frameRange];
fpsBufferIndex = 0;
}
void UpdateBuffer () {
fpsBuffer[fpsBufferIndex++] = (int)(1f / Time.unscaledDeltaTime);
if (fpsBufferIndex >= frameRange) {
fpsBufferIndex = 0;
}
}
void CalculateFPS () {
int sum = 0;
for (int i = 0; i < frameRange; i++) {
int fps = fpsBuffer[i];
sum += fps;
}
AverageFPS = (int)((float)sum / frameRange);
}
}
FPSDisplay에서 fpsCounter.FPS도 fpsCounter.AverageFPS로 바꿔주어야 함
hightestFPS와 LowestFPS 추가
frameRagne 동안 평균 FPS 이외에 최대, 최소 FPS가 어떻게 되는지도 표현해 보도록 함
using UnityEngine;
public class FPSCounter : MonoBehaviour {
public int frameRange = 60;
public int AverageFPS { get; private set; }
public int HighestFPS { get; private set; }
public int LowestFPS { get; private set; }
int[] fpsBuffer;
int fpsBufferIndex;
void Update () {
if (fpsBuffer == null || fpsBuffer.Length != frameRange) {
InitializeBuffer();
}
UpdateBuffer();
CalculateFPS();
}
void InitializeBuffer () {
if (frameRange <= 0) {
frameRange = 1;
}
fpsBuffer = new int[frameRange];
fpsBufferIndex = 0;
}
void UpdateBuffer () {
fpsBuffer[fpsBufferIndex++] = (int)(1f / Time.unscaledDeltaTime);
if (fpsBufferIndex >= frameRange) {
fpsBufferIndex = 0;
}
}
void CalculateFPS () {
int sum = 0;
int highest = 0;
int lowest = int.MaxValue;
for (int i = 0; i < frameRange; i++) {
int fps = fpsBuffer[i];
sum += fps;
if (fps > highest) {
highest = fps;
}
if (fps < lowest) {
lowest = fps;
}
}
AverageFPS = (int)((float)sum / frameRange);
HighestFPS = highest;
LowestFPS = lowest;
}
}
( Highest/LowestFPS의 추가에 따른) FPSDisplay 수정 사항
public Text highestFPSLabel, averageFPSLabel, lowestFPSLabel;
void Update () {
highestFPSLabel.text =
stringsFrom00To99[Mathf.Clamp(fpsCounter.HighestFPS, 0, 99)];
averageFPSLabel.text =
stringsFrom00To99[Mathf.Clamp(fpsCounter.AverageFPS, 0, 99)];
lowestFPSLabel.text =
stringsFrom00To99[Mathf.Clamp(fpsCounter.LowestFPS, 0, 99)];
}
Higest/Lowest FPS를 위한 UI용 Label도 만들기
만들어진 라벨 드래그 >> (to) FPSDisplay : HightestFPSLabel, AverageFPSLabel, LowestFPSLabel
[ Coloring the Labels ]
Label에 색깔 넣기
FPS의 일정 값에 따라 5단계의 색깔로 숫자 표현하기 ( 60 이상, 45 이상, 30 이상, 15 이상, 0 이상)
[System.Serializable]
private struct FPSColor {
public Color color;
public int minimumFPS;
}
[SerializeField]
private FPSColor[] coloring;
void Display (Text label, int fps) {
label.text = stringsFrom00To99[Mathf.Clamp(fps, 0, 99)];
for (int i = 0; i < coloring.Length; i++) {
if (fps >= coloring[i].minimumFPS) {
label.color = coloring[i].color;
break;
}
}
}
void Update () {
Display(highestFPSLabel, fpsCounter.HighestFPS);
Display(averageFPSLabel, fpsCounter.AverageFPS);
Display(lowestFPSLabel, fpsCounter.LowestFPS);
}
FPSDisplay : Coloring 세팅
FPS Panel에 있는 색상의 Alpha 값을 255로 세팅