OpenGL에서 게임 루프를 구축하는 좋은 방법


31

나는 현재 학교에서 OpenGL을 배우기 시작했고, 다른 날에 (학교가 아닌 내 자신의) 간단한 게임을 만들기 시작했습니다. 저는 freeglut를 사용하고 있으며 C로 빌드하고 있기 때문에 게임 루프의 경우 glutIdleFunc한 번의 패스로 모든 그림과 물리를 업데이트 하기 위해 전달한 함수를 실제로 사용했습니다 . 프레임 속도에 대해별로 신경 쓰지 않는 간단한 애니메이션에는 좋았지 만 게임은 대부분 물리 기반이기 때문에 업데이트 속도를 묶고 싶습니다.

그래서 첫 번째 시도는 이전 함수 호출 후 경과 한 시간을 추적하고 밀리 초마다 물리 (및 현재 그래픽)를 업데이트하기 위해 함수를 전달하는 것입니다 glutIdleFunc( myIdle()). 나는 이것을 사용 timeGetTime()했다 <windows.h>. 그리고 이것은 나를 유감스럽게 생각했습니다. 유휴 기능을 사용하는 것이 실제로 게임 루프에 대한 좋은 방법입니까?

내 질문은 OpenGL에서 게임 루프를 구현하는 더 좋은 방법은 무엇입니까? 유휴 기능 사용을 피해야합니까?



이 질문이 OpenGL에 더 구체적이고 GLUT를 루프에 사용하는 것이 좋은지에 대해 생각합니다
Jeff

1
여기서 큰 프로젝트 에는 GLUT 사용하지 마십시오 . 그래도 작은 사람에게는 좋습니다.
bobobobo

답변:


29

간단한 대답은 아니오입니다. 시뮬레이션이있는 게임에서 glutIdleFunc 콜백을 사용하고 싶지 않습니다. 그 이유는이 함수가 애니메이션을 분리하고 창 이벤트 처리에서 코드를 작성하지만 비동기 적으로 코드를 작성하지 않기 때문입니다. 다시 말해, 창 이벤트 스톨 수신 및 전달은 코드를 그리거나 (또는이 콜백에 넣은 모든 것) 대화 형 응용 프로그램 (상호 작용, 응답)에는 완벽하지만 물리 또는 게임 상태가 진행되어야하는 게임에는 적합하지 않습니다. 상호 작용 또는 렌더링 시간과 무관합니다.

입력 처리, 게임 상태 및 그리기 코드를 완전히 분리하려고합니다. 그래픽 라이브러리를 직접 포함하지 않는 쉽고 깨끗한 솔루션이 있습니다 (즉, 이식 가능하고 시각화하기 쉽습니다). 전체 게임 루프에서 시간을 생성하고 시뮬레이션에서 생성 된 시간 (청크 단위)을 사용하게합니다. 그러나 핵심은 시뮬레이션에 소요 된 시간을 애니메이션에 통합하는 것입니다.

내가 찾은 최고의 설명과 튜토리얼은 Glenn Fiedler 's Fix Your Timestep입니다

이 튜토리얼은 완전한 처리 방법을 제공하지만 실제 물리 시뮬레이션 이없는 경우 실제 통합을 건너 뛸 수 있지만 기본 루프는 여전히 자세한 의사 코드로 요약됩니다.

// The amount of time we want to simulate each step, in milliseconds
// (written as implicit frame-rate)
timeDelta = 1000/30
timeAccumulator = 0
while ( game should run )
{
  timeSimulatedThisIteration = 0
  startTime = currentTime()

  while ( timeAccumulator >= timeDelta )
  {
    stepGameState( timeDelta )
    timeAccumulator -= timeDelta
    timeSimulatedThisIteration += timeDelta
  }

  stepAnimation( timeSimulatedThisIteration )

  renderFrame() // OpenGL frame drawing code goes here

  handleUserInput()

  timeAccumulator += currentTime() - startTime 
}

이렇게하면 렌더링 코드, 입력 처리 또는 운영 체제가 중단되어 게임 상태가 뒤지지 않습니다. 이 방법은 또한 휴대용 및 그래픽 라이브러리 독립적입니다.

GLUT는 훌륭한 라이브러리이지만 엄격하게 이벤트 중심입니다. 콜백을 등록하고 메인 루프를 종료합니다. GLUT를 사용하여 항상 메인 루프를 제어 할 수 있습니다. 해결해야 할 해킹이 있으며 타이머 등을 사용하여 외부 루프를 가짜로 만들 수도 있지만 다른 라이브러리가 더 나은 (더 쉬운) 방법입니다. 많은 대안이 있습니다. 여기 몇 가지가 있습니다 (좋은 문서와 빠른 자습서가있는 것).

  • GLFW 를 통해 인라인 이벤트를 인라인으로 가져올 수 있습니다 (자체 메인 루프에서).
  • 그러나 SDL 은 특별히 OpenGL이 아닙니다.

좋은 기사입니다. OpenGL 에서이 작업을 수행하려면 glutMainLoop을 사용하지 않고 대신 내 자신을 사용합니까? 그렇게하면 glutMouseFunc 및 기타 함수를 사용할 수 있습니까? 나는 그것이 의미가 있기를 희망한다. 나는 아직도이 모든 것에 대해 아주 새롭다
Jeff

불행히도. GLUT에 등록 된 모든 콜백은 이벤트 큐가 비워지고 반복 될 때 glutMainLoop 내에서 시작됩니다. 답변 본문에 대안에 대한 링크를 추가했습니다.
charstar

1
다른 것을 위해 GLUT를 버리고 +1합니다. 내가 아는 한 GLUT는 테스트 애플리케이션 이외의 용도로는 결코 사용되지 않았습니다.
Jari Komppa

그래도 시스템 이벤트를 처리해야합니까? 내 이해는 glutIdleFunc를 사용하여 (Windows에서) GetMessage-TranslateMessage-DispatchMessage 루프를 처리하고 가져올 메시지가 없을 때마다 함수를 호출한다는 것입니다. 어쨌든 스스로 그렇게하겠습니까, 아니면 응용 프로그램을 응답하지 않는 것으로 플래그 지정하는 데 OS의 초조함을 겪고 있습니까?
Ricket

@Ricket 기술적으로 glutMainLoopEvent ()는 등록 된 콜백을 호출하여 들어오는 이벤트를 처리합니다. glutMainLoop ()은 효과적으로 {glutMainLoopEvent (); 유휴 콜백 (); } 중요한 고려 사항은 여기에 다시 그리기는, 즉 또한 이벤트 루프 내에서 처리하는 콜백. 당신은 시간 구동 같은 이벤트 구동 라이브러리없이 행동을하지만, 매슬로우의 망치와 모든 것을 ...
charstar

4

glutIdleFunc그냥 괜찮아 생각 합니다; 그것은 당신이 그것을 사용하지 않으면 어쨌든 손으로 끝내는 일입니다. 고정 패턴으로 호출되지 않는 타이트 루프가 무엇이든 상관없이 고정 시간 루프로 변환하거나 가변 시간 루프를 유지하고 만들지 여부를 선택해야합니다. 모든 수학은 보간 시간을 설명합니다. 또는 어쨌든 이들의 혼합.

당신은 확실히 myIdle전달 하는 함수를 가질 수 있고 glutIdleFunc, 그 안에서, 지나간 시간을 측정하고, 고정 된 시간 간격으로 나누고, 여러 번 다른 함수를 호출하십시오.

여기에 무엇을 하지 할 :

void myFunc() {
    // (calculate timeDifference here)
    int numOfTimes = timeDifference / 10;
    for(int i=0; i<numOfTimes; i++) {
        fixedTimestep();
    }
}

fixedTimestep은 10 밀리 초마다 호출되지 않습니다. 실제로 실시간보다 느리게 실행되며 문제의 근본이 timeDifference10으로 나눌 때 나머지가 손실되는 경우를 보완하기 위해 숫자를 피할 수 있습니다 .

대신 다음과 같이하십시오.

long queuedMilliseconds; // static or whatever, this must persist outside of your loop

void myFunc() {
    // (calculate timeDifference here)
    queuedMilliseconds += timeDifference;
    while(queuedMilliseconds >= 10) {
        fixedTimestep();
        queuedMilliseconds -= 10;
    }
}

이제이 루프 구조는 fixedTimestep함수가 나머지 또는 이와 유사한 것으로 밀리 초를 잃지 않고 10 밀리 초마다 한 번 호출되도록합니다.

운영 체제의 멀티 태스킹 특성으로 인해 정확히 10 밀리 초마다 호출되는 함수에 의존 할 수 없습니다. 그러나 위의 루프는 충분히 빨리 일어날 수 있기를 바라며, 각 호출이 fixedTimestep시뮬레이션을 10 밀리 초만큼 증가 시킨다고 가정 할 수 있습니다 .

이 답변 과 특히 답변에 제공된 링크를 읽으십시오 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.