DX라이브러리2014. 12. 31. 15:24

3.14 특정 FPS로 동작하기


지금까지 60Hz의 모니터를 사용하는 환경에서는 1초에 60번 루프한다 라고 배워왔다. 60Hz의 모니터에선 아래와 같은 코딩으로 1초간 60번 루프가 이루어진다.


while( ScreenFlip()==0 && ProcessMessage()==0 && ClearDrawScreen()==0 ){

1더하기();

}


대부분의 모니터가 60Hz로 동작하지만 그보다 크거나 작을 경우도 생각해보아야 한다. 60Hz이외의 환경에서도 같은 처리속도를 보장하기 위한 제어가 필요하다. (단, 보통 60Hz보다 낮은 모니터는 사용하는 경우가 없기 때문에 이보다 큰 경우만 생각한다.) 이론적으론 어렵지 않다. 1프레임마다 FPS에 맞는 계산을 해주어서 대기 시키게 된다. 시작 시간 : 0프레임째를 0이라고 한다면 각 프레임의 시작시간은 아래와 같다.


1프레임 째 "1000[ms] / 60[frame] *1" ms

2프레임 째 "1000[ms] / 60[frame] *2" ms

3프레임 째 "1000[ms] / 60[frame] *3" ms

.

.

.



90Hz의 모니터를 예로 든다면 1프레임 째의 시간은 "1000/90*1"이 되므로 60Hz때보다 빠르게 흘러간다. 때문에 이를 1000/60*1이 될 때 까지 지연 시켜야 한다. 이 지연되야 하는 시간은 각 프레임에서 "int형 지연시간 = 진행되어야 하는 시간 - 실제로 진행된 시간"이 된다. 이를 실제 코딩하게 되면 아래와 같다.


C++의 경우


#include <math.h>

#include "DxLib.h"


class Fps{

int mStartTime;         //측정개시시각

int mCount;             //카운터

float mFps;             //fps

static const int N = 60;//평균을 얻기위한 샘플 값

static const int FPS = 60; //설정하려는 FPS


public:

Fps(){

mStartTime = 0;

mCount = 0;

mFps = 0;

}


bool Update(){

if( mCount == 0 ){ //1프레임이라면 시각을 기억

mStartTime = GetNowCount();

}

if( mCount == N ){ //60프레임이라면 평균을 계산

int t = GetNowCount();

mFps = 1000.f/((t-mStartTime)/(float)N);

mCount = 0;

mStartTime = t;

}

mCount++;

return true;

}


void Draw(){

DrawFormatString(0, 0, GetColor(255,255,255), "%.1f", mFps);

}


void Wait(){

int tookTime = GetNowCount() - mStartTime; //걸린시간

int waitTime = mCount*1000/FPS - tookTime; //지연시켜야 하는 시간

if( waitTime > 0 ){

Sleep(waitTime); //대기

}

}

};


int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){

ChangeWindowMode(TRUE),DxLib_Init(),SetDrawScreen( DX_SCREEN_BACK );


Fps fps;


while( ProcessMessage()==0 && ClearDrawScreen()==0 && CheckHitKey(KEY_INPUT_ESCAPE)==0 ){

fps.Update(); //갱신

fps.Draw(); //묘화

ScreenFlip();

fps.Wait(); //대기

}


DxLib_End();

return 0;

}




C의 경우


#include <math.h>

#include "DxLib.h"


static int mStartTime;      //측정개시시각

static int mCount;          //카운터

static float mFps;          //fps

static const int N = 60; //평균을 얻기위한 샘플 값

static const int FPS = 60; //설정하려는 FPS


bool Update(){

if( mCount == 0 ){ //1프레임이라면 시각을 기억

mStartTime = GetNowCount();

}

if( mCount == N ){ //60프레임이라면 평균을 계산

int t = GetNowCount();

mFps = 1000.f/((t-mStartTime)/(float)N);

mCount = 0;

mStartTime = t;

}

mCount++;

return true;

}


void Draw(){

DrawFormatString(0, 0, GetColor(255,255,255), "%.1f", mFps);

}


void Wait(){

int tookTime = GetNowCount() - mStartTime; //걸린시간

int waitTime = mCount*1000/FPS - tookTime; //지연시켜야 하는 시간

if( waitTime > 0 ){

Sleep(waitTime); //대기

}

}


int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){

ChangeWindowMode(TRUE),DxLib_Init(),SetDrawScreen( DX_SCREEN_BACK );


while( ProcessMessage()==0 && ClearDrawScreen()==0 && CheckHitKey(KEY_INPUT_ESCAPE)==0 ){

Update(); //갱신

Draw(); //묘화

ScreenFlip();

Wait(); //대기

}


DxLib_End();

return 0;

}



실행결과


위 스샷처럼 60프레임으로 동작하고 있다. mFps에 측정된 실제의 FPS수치가 들어가 있고 Draw에서 측정한 FPS값이 출력된다. FPS의 평균을 몇번이나 할까 하는것은 변수 N을 통하여 조정 가능하다. 또, 정의된 FPS값을 변경하면 원하는 FPS로 설정 가능하다. 얘를 들어 다음과 같이 30FPS로 설정 가능하다.


static const int FPS = 30;


실행결과



30프레임으로 동작중이다. PSP같은 경우는 30FPS이며 PSP와 같이 높은 프레임레이트를 필요로 하지 않는 경우에는 이렇게 조정하는것도 좋은 방법이다. 특히 스마트폰 게임과 같은경우는 프레임레이트를 높이게되면 베터리의 소모에도 영향을 줄 수 있기 때문이다.

Posted by 캡슐리어