물론 이러한 디자인 패턴에 대해서 공부해봤다.
[Game Developer, 게임개발자/디자인패턴] - Game Loop Pattern, 게임 루프 패턴 ** [디자인패턴]
하지만 조금 더 일반적인 이야기를 해보려고 한다.
게임 루프는 앞의 글처럼 게임 프로그램의 전체적인 흐름을 주관한다.
즉, 사용자가 임의적으로 중지하지 않는 한 게임은 계속 수행을 하는 것이다.
게임 루프는 말 그대로 LOOP, 계속 반복되어 진행된다.
일반적으로 우리는 60프레임으로 진행되는 게임을 많이 보는데
이는 게임루프가 초당 60번 실행된다는 뜻이다.
하지만 최신 하드웨어에 맞게 게임루프도 점차 변해가는데
우선 전통적으로 쓰여온 게임루프의 형식부터 알아보자.
전통적인 게임 루프
1. 입력 처리
2. 게임 업데이트
3. 출력
이 3가지로 이루어져있다.
간단하게 말하면 3가지이지만 자세히 보면 많은 내용이 담겨져 있다.
예를 들어 입력 처리만해도
사용자 입력뿐만 아니라 외부 입력에 대해서도 모두 처리해야 한다.
예를 들어 멀티플레이일 경우에는 네트워크를 통해서 들어온 입력까지 처리해야한다는 말이다.
게임 업데이트도 할 것이 엄청나게 많다.
수백 수천개의 오브젝트들을 검사해서 업데이트 해야 한다.
마지막으로 출력은 가장 부하가 큰 단계라고 할 수 있는데
그래픽, 사운드와 같은 부하가 높은 작업들이 많다.
우리는 현재 싱글 코어가 아닌 멀티 코어 시스템을 가지고 실행하고 있다.
즉, CPU가 동시에 여러 줄의 코드 또는 여러 개의 스레드를 실행할 수 있다는 말이다.
멀티 코어의 성능을 이끌어내기 위해선 코어의 성능자체 뿐만아니라
우리가 할 수 있는 소프트웨어적 설계에서 성능을 추구할 수 있다.
그래픽은 앞서 말했던 대로 엄청난 부하를 요구하는 작업으로
현대에 와서는 더욱 작업량이 방대해져 시간이 더 오래걸리게 되었다.
게임루프를 기존처럼 돌리게 된다면
우리는 게임 업데이트가 0.3초에 1번되는 버벅이는 현상을 겪을지도 모른다.
즉, 우리는 게임루프를 조금 더 효율적으로 돌릴 수 있게 설계해야 한다는 말이다.
예를 들어 렌더링하는데 50ms 걸리고 업데이트하는데 30ms가 걸린다면
업데이트 후 렌더링해서 사용자가 보기까지 총 80ms가 걸린다.
이것은 정말 게임 할 맛이 나지 않는다.
그래서 우리는 멀티 스레딩을 하려고 한다.
업데이트와 렌더링을 동시에 수행하고 싶다는 말이다.
그렇다면 우리는 결국 50ms의 시간에 유저가 업데이트된 화면을 볼 수 있다.
하지만 렌더링 스레드가 화면을 그리는 동안 메인 스레드가 가만히 있는 동기적 수행을 한다면
그다지 도움이 되지 않는다.
다시 말해서 하나의 스레드로 전부 처리할 때와 다른 것이 없다.
즉, 우리는 비동기적 수행을 할 것인데..?
어떻게 해야 서로의 작업을 방해하거나 오류가 없이 수행할 수 있느냐?
간단하게 서로 작업하는 내용이 다르면 된다.
렌더링 스레드를 메인 스레드보다 한 프레임씩 늦게 수행하는 것이다.
다시 말해서 메인스레드가 업데이트를 하는 동안에
렌더링 스레드는 그 전 프레임의 업데이트한 결과를 화면에 출력하는 것이다.
** Double Buffer랑 비슷한 느낌이 들었다면 잘하는 사람
오.. 우리는 전 프레임의 결과를 지금 보지만 그래도 게임 루프가 빨리 돌아간다는 것에 만족한다.
하지만 이렇게 입력을 한 프레임 늦게 실행하면... 문제 없을까??
물론 위의 결과에 만족하는 게임이 있는 반면에
격투 게임과 같이 한 틱에 결정되는 긴장감 넘치는 게임에는 어울리지 않는다.
즉, input lag이 늘어나서 고객들이 하는 소리가
"아 눌렀는데 안나가잖아요 이 게임 ****** " 라고 들을 것이다.
프레임 1에서는 이전 프레임에 대해서 렌더링이 될 것이며
즉, 프레임 2에 점프버튼을 눌렀다고해도
프레임 3에서 사용자 입력이 처리되지 않으며
프레임 4에서나마 3에서 업데이트 된 점프 동작이 하는 것이 화면에 렌더링 될 것이다.
즉, 입력 지연 시간에 민감한 게임은 게임 루프를 잘 설계해야 한다는 것을 알 수 있다.
**우리는 하드웨어 성능이 올라가는 것에 멈추지 않고 최적화를 위해 최선을 다해야 한다.
** 이를 해결하려면 이론적으로 deterministic인 UDP이면서 realiable한 통신이 빠르게 가능하게 하면 된다.
게임 루프에 관한 질문
1. 프레임 고정은 어떻게 하는가??
게임루프에 대해서 해당 시간 안에 처리가 되었으면 나머지 시간은 Sleep하는 형태로 구현할 수 있다.
이런 식이다.
func(targetT, realT){
...
if(realT < targetT){
...
sleep(targetT-realT);
}
}
2. 멀티 스레딩으로 프레임 속도를 높이는 방법은?
위에서 말했던 대로 한 프레임에 입력을 처리하고 게임을 업데이트하고 렌더링하는 것이라고 한다면
가장 시간이 걸리는 렌더링을 이전 프레임에 계산된 것으로 하여 업데이트 하면서도 렌더링을 같이함으로써 한 프레임당 걸리는 시간을 줄일 수 있다. -> 1초당 게임루프를 많이돌릴 수 있다.
참고
https://askagamedev.tumblr.com/post/160552949016/is-there-a-situation-where-input-lag-is-purposely
'Game Development, 게임개발 > 배경지식' 카테고리의 다른 글
이벤트 큐의 활용 - 서버 요청 응답의 순서 보장 (0) | 2022.04.03 |
---|---|
튜토리얼에 대한 고찰 (0) | 2022.03.12 |
Reactive Programming이란, Rx란? (0) | 2022.01.17 |
Scroll, 스크롤에 대한 것 (0) | 2021.11.11 |
게임의 시간과 현실의 시간의 차이에 대해서 (4) | 2021.11.09 |