1. Frame rate란?
- 1초에 메인 루프가 몇 번 도는지를 가리켜 프레임 레이트(Frame rate)라고 부른다. 예를 들어 '프레임
레이트는 60이다'라고 표현하고 더 줄여서 '60프레임이다'라고도 한다. 영어로는 60 Frame per second
라고 하고 줄여서 fps라고도 한다. 앵간하면 게임에선 60프레임 고정을 목표로 하자.
2. 입력 페이스 조절.
- 여기서 고민해 볼 것은 게임 입력키로 자주 사용하는 GetAsyncKeyState()로 입력을 받아서 물체를 이
동 시킨다고 치면, 그 게임의 Frame ate에 따라 입력되는 횟수가 틀려질 것이다.
이것을 해결하기 위한 방법은 기본 적으로 3가지가 있다.
1) 누른 시간에 비례해서 움직인다.
2) 이전 프레임에서 눌리지 않았을 때만 반응한다.
3) 일정 시간 이상 눌려야 반응한다.
1)의 방법은 액션 게임에서는 일반적으로 사용되는 방법으로 한 칸 단위가 아니라 정밀하게 움직이는
게임에서 사용한다. 예를 들면 1프레임동안 눌렀다면 0.2만큼 이동해라.
2)의 방법은 이전 프레임 상태를 저장해 두고 이번 프레임에서 처음 눌렀을 때만 반응하는 방법이다.
이 방법 같은 경우에는 이동키에는 사용하기 힘들고, 격투 게임에서 펀치 버튼에서 사용할 수 있겠다.
3)의 방법은 그다지 사용되는 빈도는 높지 않지만 일단은 알아두자. 키를 눌렀을 때 바로 반응하면 곤
란하거나 잠시 누르고 있을 대 계속 반응한 채로 두고 싶을 때 사용된다.
3. Frame rate 계산.
- 이 계산에도 다양한 방법이 있을 것이다. 여기선 단순하게 하자. 결국 Frame rate는 MainLoop를 1초에
몇번하는가 이다. 그러면 MainLoop가 1회당 몇초가 걸리는지 생각해서 그 역수를 Frame rate로 하는
것이다. 하지만 고정된 수로 하는 것은 위험하다. 언제 변수가 생길지 모르니까...
그래서 MainLoop의 걸리는 시간을 10개 정도 저장을 해서 평균을 내는 것이 좋다.
unsigned PreviousTime[10];
MainLoop()
{
unsigned currentTime = time();
unsigned frameTimeAve = currentTime - PreviousTime[0];
for( int i = 0; i < 10 - 1; ++i)
{
PreviousTime[i] = PreviousTime[i + 1];
}
PreviousTime[10 - 1] = currentTime;
unsigned frameRate = 1000 * 10 / frameTimeAve; // 프레임 레이트 계산.
// 여기서 업데이트 및 렌더 처리
}
10프레임일 때 기록을 저장해 두고 지금 측정한 시간에서 제일 오래된 기록을 빼면 '최근 10 프레임에
걸린 시간'을 알 수 있다. 다시 10으로 나무면 1프레임당 걸린 시간으로 고칠 수 있고 여기부터는
Frame rate도 계산 할 수 있다.
4. timeGetTime()을 이용한 fps 구하기.
- 이 방법은 timeGetTime()을 이용하여 간단하게 fps를 측정하는 코드이다.
#include <timeapi.h>
#pragma comment(lib, "Winmm.lib")
UpdateFPS()
{
static DWORD frameCount = 0; //프레임 카운트수
static float timeElapsed = 0.0f; //흐른 시간
static DWORD lastTime = timeGetTime(); //마지막 시간(temp변수)
DWORD curTime = timeGetTime(); //현재 시간
float timeDelta = (curTime - lastTime)*0.001f; //timeDelta(1번생성후 흐른 시간) 1초단위로 바꿔준다.
timeElapsed += timeDelta;
/////////////////////////////////////////////// 함수 실행!!!!
frameCount++;
if (timeElapsed >= 1.0f) //흐른시간이 1초이상이면 내가 하고싶은것 처리
{
wchar_t pMessage[300] = { 0, };
float fps = (float)frameCount / timeElapsed;
swprintf_s(pMessage, sizeof(pMessage), TEXT("[D3D PusthToChannel] FPS: %f \r\n"), fps);
OutputDebugStringW(pMessage);
frameCount = 0;
timeElapsed = 0.0f;
}
lastTime = curTime;
}
5. Frame rate를 고정하자.
- 위에서 계산한 프레임은 성능이 같은 컴퓨터 끼리는 같은 결과가 나오겠지만 성능이 다른 컴퓨터 끼
리는 다른 결과가 나오게 된다. 때문에 Frame rate를 고정할 필요가 있다.
이 고정하는 방법은 대충 2가지의 방법이 있다. 첫 번째는 Frame rate를 일정하게 고정하는 방법이고,
두 번째는 게임 처리를 Frame rate에 맞춰 변경하는 것이다. 예를 들어 Frame rate가 60일 때에 1 픽셀
씩 이동하고 Frame rate가 3일 때에 2 픽셀씩 이동하는 방식이다.
1) 프레임 레이트를 고정하는 방법.
- 프레임 레이트를 고정해서 게임을 제작하는 방법은 고정 프레임 레이트라고 부른다.
unsigend EndTime = PreviousTime + 16 // 예정 시간. 16은 프레임으로 치면 62.5 정도....
while( 1)
{
unsigned CurrentTime = time() - PreviouseTime;
if( CurrentTime < 0) CurrentTime += 0x100000000; // 현재 시간이 음수가 되면 안되기 때문에
if( CurrentTime >= EndTime)
{
break;
}
}
이렇게 하면 이전 프레임 시각에서 16밀리 초 이상 경과하지 않으면 다음으로 넘어가지 않는다. 아
주 간단히 고정 프레임 레이트를 구현할 수 있지만, 남는 시간을 while문으로 계속 돌리는 것은 좋
지 않다. 그냥 sleep()으로 재워두자.
while( time() < EndTime())
{
sleep( 1);
}
2) 고정 프레임의 문제점.
- 현재 빠른 것을 느리게 한다는 가정으로 고정 프레임을 만든 것인데, 느린 것을 빠르게 만드는 방
법은 없다. 이것이 가장 큰 결점이다. 그래서 프레임 레이트는 가장 느릴 때에 맞춰 정해야만 한다.
만약 정해진 프레임 레이트에 맞지 않게 된다면 이것은 처리 지연이라고 한다.
3) 프레임 레이트에 맞게 게임 속도를 바꾸는 방법.
- 프레임 레이트에 맞춰 게임 처리를 변경하는 방법으로 가변 프레임 레이트라고도 한다.
이 방식의 이점은 크게 두가지로 볼 수 있는데, 첫 번째는 항상 컴퓨터 성능을 극한까지 끌어낼 수
있다는 것이고 두 번째는 처리 지연이 발생하지 않는다는 것이다. 프로그램이 쉬는 시간이 전혀 없
으므로 항상 컴퓨터를 최대한으로 사용한다.
하지만 단점도 있다. 프레임 간격이 계속 변한다는 것을 전제로 게임 처리를 작성해야 하므로 코드
가 복잡해지고, 재현성 문제도 있어서 가변 프레임 레이트 게임에서 같은 상태를 재현하기는 어렵
다. 때문에 디버그가 상당히 어려워진다.