Article

Gamza 2011. 5. 19. 12:25

요즘 게임은 더 좋은 사양의 컴퓨터를 사용할때 더 좋은 장면을 보여주기위해 프레임율에 의존적이지 않게 루프를 작성하게 됩니다. 간단하게 말하자면 이전 프레임과 현재 프레임의 시간차(dt)를 이용하는거죠. 

다만 이렇게 하면 약간 신경쓰이는것들이 생기게되고, 확실히 프레임 스키핑을 이용한 고정 프레임율을 사용하는것이 편할때가 있습니다. 

그래서 가변프레임율 과 고정 프레임율을 동시에 적용하는 방법을 생각해보았습니다. 

기존에 '프레임 스키핑'이란 기법은 낮은 사양의 컴퓨터에서 고정프레임율을 보장하기 위한 기법으로, 메인루프가 설정해놓은 최대 프레임율보다 더 빨리돌수 없다는 한계를 가졌다고 알려져 있었습니다.

여기서는 특정코드들은 프레임스키핑을 사용해서 컴퓨터의 사양에 상관없이 고정프레임율을 가지고 동작하면서도, 기타 다른 코드들은 가능한한 빠르게 동작하도록 하는 코드를 소개합니다.

다음은 VC++6 에서 컴파일 테스트 해본 예제입니다.

void   Main( HWND hWnd, float dt )
{
    //******************************************************************
    //   프레임율 비의존적인 코드
    //      dt에 의해 진행되기때문에 가능한한 빨리도는 코드들.
    //      시스템이 좋을수록 더 자주 호출된다.
    //******************************************************************
    // Animation( dt );
    
    if( fs.Update(dt) )
    {
        //******************************************************************
        //   프레임율 의존적인 코드
        //      이부분은 frame skip에 의해 고정 프레임율을 갖고 동작한다.
        //******************************************************************
        static long counter=0;
        counter++;
        if( counter%100 == 0 ) 
        printf( "static frame rate code : %d\n", counter ); 
    } 
    
    if( ! fs.IsFrameSkip() ) 
    { 
        //****************************************************************** 
        //   경우에 따라 호출되지 않아도 되는 코드 
        //      이부분은 frame skip에 의해 skip될수 있다. 
        //      렌더링따위의 시간을 많이 잡아먹는 코드를 넣는다. 
        //      역시 시스템이 좋을수록 더 자주 호출된다. 
        //      숫자는 제시스템에서 나온 fps입니다. 
        //****************************************************************** 
        Sleep( 0.8f*1000 );   // 10fps 
        //Sleep( 0.2f*1000 );   // 10fps 
        //Sleep( 0.05f*1000 );   // 20fps 
        //Sleep( 0.02f*1000 );   // 50fps 
        //Sleep( 0.002f*1000 );   // 100fps 
    } 
    
    // printf frame / sec 
    PrintFPS( dt ); 
}
테스트는 프레임 스키핑을 10fps로 설정해 놓고 Sleep을 이용해서 전체적인 프레임율을 조절해보았습니다. 주석에도 써있다시피 Sleep값을 키워줘도 프레임 스키핑에 의해 10fps는 유지되고, 빨리돌수 있을때는 [프레임율 비의존적인 코드]와 [경우에 따라 호출되지 않아도 되는 코드]가 가능한한 자주 호출되게 됩니다. 

물론 [프레임율 의존적인 코드] 는 메인루프가 빠르던,느리던 상관없이 10fps로 돌아갑니다. 

아래는 가변 프레임율이 지원되는 FrameSkip 클래스 입니다. 

class FrameSkip 
{ 
public: 
    void Clear() 
    { 
        SetFramePerSec( 60.0f ); 
        m_Timer = 0.0f; 
    } 
    void SetFramePerSec( float fps ) 
    { 
        m_SecPerFrame = 1.0f/fps; 
    } 
    
    // 원하는 프레임보다 너무 빠르면, 
    // false를 반환해서 코드를 동작시키지 않도록 한다. 
    // dt는 '초'단위 (밀리초 아님!!!) 
    bool Update( float dt ) 
    { 
        m_Timer += dt;       
        if( m_Timer<0 ) return false;       
        // 한프레임에 해당하는 시간을 뺀다. 
        m_Timer -= m_SecPerFrame; 
        return true; 
    } 
    
    // Update후에 호출해서 frame skip을 수행해야 하는지 검사한다. 
    // frame skip이 일어나야 한다면 true를 반환한다. 
    bool IsFrameSkip() 
    { 
        return m_Timer >= 0; 
    } 
    
    // 멤버변수와 생성/소멸자. 
    FrameSkip(){ Clear(); } 
    virtual ~FrameSkip(){} 
protected: 
    float m_Timer; 
    float m_SecPerFrame; 
}; 
이 클래스를 이용하면 사용자 입력은 10fps로 동작시키면서, AI는 30fps로... 카메라이동은 가능한한 부드럽게 하는식으로.....
이런게 간단하게 구현됩니다.

FrameSkip fs[2]; 
... 
fs[0].SetFramePerSec( 10 ); 
fs[1].SetFramePerSec( 30 ); 
... 
게임루프( float dt ) 
{ 
    카메라이동( dt ); // 가능한한 빨리동작하는 부분 
    
    if( fs[0].Update(dt) ) 사용자 입력처리; // 10fps로 동작 하는 부분 
    
    if( fs[1].Update(dt) ) AI; // 30fps로 동작 하는 부분 
    
    if( ! ( fs[0].IsFrameSkip() || fs[1].IsFrameSkip() ) ) 
    { 
        렌더링따위의 코드..... 
    } 
} 
♡달링 알랍♡


블로그에 참조했습니다. 좋은 정보 감사합니다.
오 좋은 생각이군요.
좋은 정보 감사합니다.