[algorithm] 게임에서 초당 프레임 계산
게임에서 초당 프레임을 계산하는 데 좋은 알고리즘은 무엇입니까? 화면 모서리에 숫자로 표시하고 싶습니다. 마지막 프레임을 렌더링하는 데 걸린 시간 만 보면 숫자가 너무 빠르게 변경됩니다.
답변이 각 프레임을 업데이트하고 프레임 속도가 증가 할 때와 감소 할 때 다르게 수렴하지 않으면 보너스 포인트를 얻을 수 있습니다.
답변
평활화 된 평균이 필요합니다. 가장 쉬운 방법은 현재 답변 (마지막 프레임을 그리는 데 걸리는 시간)을 가져와 이전 답변과 결합하는 것입니다.
// eg.
float smoothing = 0.9; // larger=more smoothing
measurement = (measurement * smoothing) + (current * (1.0-smoothing))
0.9 / 0.1 비율을 조정하여 ‘시정 수’를 변경할 수 있습니다. 즉, 숫자가 변화에 얼마나 빠르게 반응 하는지를 나타냅니다. 이전 답변을 선호하는 분수가 클수록 변경이 더 느리게 이루어지고 새 답변을 선호하는 분수가 클수록 값이 더 빠르게 변경됩니다. 분명히 두 가지 요소가 하나에 더해져야합니다!
답변
이것이 제가 많은 게임에서 사용한 것입니다.
#define MAXSAMPLES 100
int tickindex=0;
int ticksum=0;
int ticklist[MAXSAMPLES];
/* need to zero out the ticklist array before starting */
/* average will ramp up until the buffer is full */
/* returns average ticks per frame over the MAXSAMPLES last frames */
double CalcAverageTick(int newtick)
{
ticksum-=ticklist[tickindex]; /* subtract value falling off */
ticksum+=newtick; /* add new value */
ticklist[tickindex]=newtick; /* save new value so it can be subtracted later */
if(++tickindex==MAXSAMPLES) /* inc buffer index */
tickindex=0;
/* return average */
return((double)ticksum/MAXSAMPLES);
}
답변
음, 확실히
frames / sec = 1 / (sec / frame)
그러나 지적했듯이 단일 프레임을 렌더링하는 데 걸리는 시간에는 많은 차이가 있으며 UI 관점에서 프레임 속도로 fps 값을 업데이트하는 것은 전혀 사용할 수 없습니다 (숫자가 매우 안정적이지 않은 경우).
당신이 원하는 것은 아마도 이동 평균이나 일종의 비닝 / 리셋 카운터 일 것입니다.
예를 들어, 마지막 30, 60, 100 또는 What-have-you 프레임 각각에 대한 렌더링 시간을 보유한 큐 데이터 구조를 유지할 수 있습니다 (런타임에 제한을 조정할 수 있도록 설계 할 수도 있음). 적절한 fps 근사치를 결정하기 위해 대기열의 모든 렌더링 시간에서 평균 fps를 결정할 수 있습니다.
fps = # of rendering times in queue / total rendering time
새 프레임 렌더링을 마치면 새 렌더링 시간을 대기열에 넣고 이전 렌더링 시간을 대기열에서 빼게됩니다. 또는 총 렌더링 시간이 미리 설정된 값 (예 : 1 초)을 초과하는 경우에만 대기열에서 제외 할 수 있습니다. “마지막 fps 값”과 마지막으로 업데이트 된 타임 스탬프를 유지하여 원하는 경우 fps 수치를 업데이트 할시기를 트리거 할 수 있습니다. 일관된 포맷을 가지고 있다면 이동 평균을 사용하지만 각 프레임에 “순간 평균”fps를 인쇄하는 것은 아마도 괜찮을 것입니다.
또 다른 방법은 카운터를 재설정하는 것입니다. 정확한 (밀리 초) 타임 스탬프, 프레임 카운터 및 fps 값을 유지합니다. 프레임 렌더링을 마치면 카운터를 증가시킵니다. 카운터가 사전 설정된 제한 (예 : 100 프레임)에 도달하거나 타임 스탬프가 사전 설정된 값 (예 : 1 초)을 통과 한 후 fps를 계산합니다.
fps = # frames / (current time - start time)
그런 다음 카운터를 0으로 재설정하고 타임 스탬프를 현재 시간으로 설정합니다.
답변
화면을 렌더링 할 때마다 카운터를 늘리고 프레임 속도를 측정하려는 일정 시간 간격 동안 해당 카운터를 지 웁니다.
즉. 3 초마다 카운터 / 3를 얻은 다음 카운터를 지 웁니다.
답변
이를 수행하는 방법은 최소한 두 가지입니다.
첫 번째는 다른 사람들이 여기서 언급 한 것입니다. 가장 간단하고 선호하는 방법이라고 생각합니다. 당신은 단지 추적하기 위해
- cn : 렌더링 한 프레임 수 카운터
- time_start : 계산을 시작한 이후의 시간
- time_now : 현재 시간
이 경우 fps를 계산하는 것은 다음 공식을 평가하는 것만 큼 간단합니다.
- FPS = cn / (시간 _ 현재-시간 _ 시작).
그런 다음 언젠가 사용하고 싶은 멋진 방법이 있습니다.
고려해야 할 ‘i’프레임이 있다고 가정 해 보겠습니다. 이 표기법을 사용할 것입니다 : f [0], f [1], …, f [i-1] 프레임 0, 프레임 1, …, 프레임 (i-1 ) 각각.
Example where i = 3
|f[0] |f[1] |f[2] |
+----------+-------------+-------+------> time
그러면 i 프레임 이후 fps의 수학적 정의는 다음과 같습니다.
(1) fps[i] = i / (f[0] + ... + f[i-1])
그리고 동일한 공식이지만 i-1 프레임 만 고려합니다.
(2) fps[i-1] = (i-1) / (f[0] + ... + f[i-2])
이제 여기서 트릭은 공식 (2)의 오른쪽을 포함하고 왼쪽을 대체하는 방식으로 공식 (1)의 오른쪽을 수정하는 것입니다.
이렇게 (종이에 쓰면 더 명확하게 볼 수 있습니다) :
fps[i] = i / (f[0] + ... + f[i-1])
= i / ((f[0] + ... + f[i-2]) + f[i-1])
= (i/(i-1)) / ((f[0] + ... + f[i-2])/(i-1) + f[i-1]/(i-1))
= (i/(i-1)) / (1/fps[i-1] + f[i-1]/(i-1))
= ...
= (i*fps[i-1]) / (f[i-1] * fps[i-1] + i - 1)
따라서이 공식에 따라 (내 수학 도출 기술은 약간 녹슬 었습니다), 새로운 fps를 계산하려면 이전 프레임의 fps, 마지막 프레임을 렌더링하는 데 걸린 시간 및 사용한 프레임 수를 알아야합니다. 렌더링.
답변
이것은 대부분의 사람들에게 과도한 것일 수 있으므로 구현할 때 게시하지 않은 것입니다. 그러나 매우 견고하고 유연합니다.
마지막 프레임 시간과 함께 대기열을 저장하므로 마지막 프레임을 고려하는 것보다 훨씬 더 정확하게 평균 FPS 값을 계산할 수 있습니다.
또한 인위적으로 프레임의 시간을 망칠 것이라는 것을 알고있는 경우 프레임 하나를 무시할 수 있습니다.
또한 실행되는 동안 대기열에 저장할 프레임 수를 변경할 수 있으므로 자신에게 가장 적합한 값을 즉시 테스트 할 수 있습니다.
// Number of past frames to use for FPS smooth calculation - because
// Unity's smoothedDeltaTime, well - it kinda sucks
private int frameTimesSize = 60;
// A Queue is the perfect data structure for the smoothed FPS task;
// new values in, old values out
private Queue<float> frameTimes;
// Not really needed, but used for faster updating then processing
// the entire queue every frame
private float __frameTimesSum = 0;
// Flag to ignore the next frame when performing a heavy one-time operation
// (like changing resolution)
private bool _fpsIgnoreNextFrame = false;
//=============================================================================
// Call this after doing a heavy operation that will screw up with FPS calculation
void FPSIgnoreNextFrame() {
this._fpsIgnoreNextFrame = true;
}
//=============================================================================
// Smoothed FPS counter updating
void Update()
{
if (this._fpsIgnoreNextFrame) {
this._fpsIgnoreNextFrame = false;
return;
}
// While looping here allows the frameTimesSize member to be changed dinamically
while (this.frameTimes.Count >= this.frameTimesSize) {
this.__frameTimesSum -= this.frameTimes.Dequeue();
}
while (this.frameTimes.Count < this.frameTimesSize) {
this.__frameTimesSum += Time.deltaTime;
this.frameTimes.Enqueue(Time.deltaTime);
}
}
//=============================================================================
// Public function to get smoothed FPS values
public int GetSmoothedFPS() {
return (int)(this.frameTimesSize / this.__frameTimesSum * Time.timeScale);
}
답변
여기에 좋은 답변이 있습니다. 그것을 구현하는 방법은 필요한 것에 달려 있습니다. 나는 위의 사람이 “time = time * 0.9 + last_frame * 0.1″러닝 평균을 선호합니다.
그러나 저는 개인적으로 새로운 데이터에 대한 평균 가중치를 더 높이고 싶습니다. 게임에서 SPIKES가 가장 어렵 기 때문에 저에게 가장 관심이 많기 때문입니다. 따라서 .7 \ .3 분할과 같은 것을 사용하면 스파이크가 훨씬 더 빨리 표시됩니다 (효과가 화면에서 더 빨리 떨어집니다. 아래 참조).
RENDERING 시간에 초점을 맞추고 있다면 .9.1 분할이 꽤 잘 작동합니다. b / c가 더 부드럽습니다. 게임 플레이 / AI / 물리 스파이크의 경우 일반적으로 게임이 고르지 않게 보이기 때문에 훨씬 더 큰 문제가됩니다 (20fps 이하로 떨어지지 않는다고 가정 할 때 낮은 프레임 속도보다 종종 더 나쁩니다).
그래서 내가 할 일은 다음과 같은 것을 추가하는 것입니다.
#define ONE_OVER_FPS (1.0f/60.0f)
static float g_SpikeGuardBreakpoint = 3.0f * ONE_OVER_FPS;
if(time > g_SpikeGuardBreakpoint)
DoInternalBreakpoint()
(용납 할 수없는 스파이크라고 판단되는 크기로 3.0f를 채우십시오.) 그러면 프레임이 끝날 때 FPS 문제 를 찾아 해결할 수 있습니다 .