티스토리 뷰
출처 : http://gameprogrammingpatterns.com/game-loop.html
게임 루프의 핵심 요소
우리가 게임을 어떻게 하는지 곰곰히 생각해보면, 키보드와 마우스를 이용해서 입력을 받아오고, 그와 동시에 컴퓨터 모니터에 캐릭터가 움직인다. 게임 장르에 따라 총을 쏠 수도, 마법을 부리거나 칼을 휘두를 수도 있다. 그럼 이 개념들을 이용해서 비스무리한 코드를 작성한다면 아래와 같이 나올 것이다.
while (true)
{
processInput();
update();
render();
}
별거 없다. 그냥 무한루프에 입력, 갱신, 렌더링 이것들이 전부이다.
루프는 얼마나 빠르게 돌아갈까?
실제 게임을 하면서 우리는 캐릭터가 움직이는 것을 본다. 엄청 부드럽게 움직이지 않는가? 이러한 움직임을 구현하기 위해서 루프는 얼마나 빨라야 할까? 게임 루프의 주기를 실제 단위로 측정하면 게임의 "초당 프레임" 이 된다. 영어로 "Frame Per Second, FPS" 로 말한다. 게임 루프가 빠르게 주기적으로 돌면 우리는 fps가 빨라진다고 한다.
간단한 루프에서는 가능한 한 빠르게 주기적으로 돌기 때문에 두가지 요소가 프레임 속도를 결정한다.
프레임 속도 결정 요소
- 각 프레임마다 수행 해야 할 작업량
- 기반 플랫폼의 속도
1. 복잡한 물리학, 다수의 게임 객체 및 그래픽 상세도가 CPU와 GPU를 바쁘게 유지하며 한 프레임을 완료하는 데 더 많은 시간이 걸린다.
2. 더 빠른 칩은 동일한 시간 내에 더 많은 코드를 처리한다.
근본적인 문제
부드러운 움직임을 구현하기위해서 루프를 빠르게 돌리면 물론 좋다. 하지만, 과연 주기가 빠르기만 할까? 갑자기 fps가 낮아진다면 우리는 이 루프를 제어할 수 있을까? 답은 No이다. 위의 코드로는 fps를 제어할 수 없다. 우리는 일정한 fps를 갖기위해 루프를 제어해야 한다.
첫번째 변형
예를들어 60 FPS 로 루프를 실행하고 싶다고 가정해 보자. 이렇게 하면 약 16 ms의 시간이 주어진다. 만약 이 시간 안에 위의 3가지 작업을 모두 마친다면 16 ms에 도달할 때 까지 루프는 잠깐 대기하면 된다. 아래는 예제 코드이다.
while (true)
{
double start = getCurrentTime(); // 시작 시간 = 현 시점 시간
processInput();
update();
render();
// 대기 시간 = 시작 시간 + 목표 대기시간 (16 ms) - 현재 시간
sleep(start + MS_PER_FRAME - getCurrentTime());
}
위의 코드는 루프가 목표 fps보다 빨리 실행되지 못하도록 제어해준다. 그럼 반대로 목표 fps보다 느리게 실행됐다면?
두번째 변형
매 프레임마다, 업데이트 이후에 경과한 실제 시간 (elapsed)를 결정 하고, 게임 상태를 업데이트할 때 그 값을 전달하도록 한다.
double lastTime = getCurrentTime();
while (true)
{
double current = getCurrentTime();
double elapsed = current - lastTime;
processInput();
update(elapsed);
render();
lastTime = current;
}
이제 목표 fps에 근사할 수 있는 코드가 되어가고 있다. 그러나 문제점이 또 있다. 우리들의 집에 있는 컴퓨터의 성능은 각각 다르다. 누구는 1초에 50번 계산할 수 있는 스펙이고 누구는 1초에 5번 계산할 수 있는 스펙이라면 실시간으로 실행되어야하는 멀티 게임이 터질 수도 있다. 이를 해결할 방법이 필요하다.
세번째 변형
컴퓨터 내부의 시계가 아닌 하나의 공통된 시계를 기준으로 측정한다면 어떨까?
double previous = getCurrentTime();
double lag = 0.0;
while (true)
{
double current = getCurrentTime();
double elapsed = current - previous;
previous = current;
lag += elapsed;
processInput();
while (lag >= MS_PER_UPDATE)
{
update();
lag -= MS_PER_UPDATE;
}
render();
}
여기서 lag 이 개인 컴퓨터의 시간이 공통된 시간과 비교하여 얼마나 뒤쳐저 있는지의 척도가 된다.
남은 문제 중 하나는 잔여 지연이다. 우리는 고정된 시간 간격으로 게임을 업데이트하지만 임의의 시간에 렌더링을 수행한다. 그 말인 즉슨 렌더링이 무조건 업데이트 타이밍에 한다고 확신할 수 없다는 것이다.
위에서 우리는 지연시간을 구했었다. 정말 간단하게 생각한다면 지연시간이 0이 될 때 까지 루프를 제어하면 된다.
이 코드처럼 말이다.
'커리어' 카테고리의 다른 글
[커리어] 패킷 스위칭은 어떻게 고안됐을까? (2) | 2023.07.04 |
---|