게임 프로그래밍에서의 delta time / dt :: 게임제작[SSISO Community]
 
SSISO 카페 SSISO Source SSISO 구직 SSISO 쇼핑몰 SSISO 맛집
추천검색어 : JUnit   Log4j   ajax   spring   struts   struts-config.xml   Synchronized   책정보   Ajax 마스터하기   우측부분

게임제작
[1]
등록일:2019-03-20 17:10:51 (0%)
작성자:
제목:게임 프로그래밍에서의 delta time / dt
7 Comments

게임 프로그래밍을 하다 보면 물리 문제에 많이 접하게 되는데요,
물리에는 거의 대부분 시간 개념이 들어가게 됩니다.

따라서 게임에서 시간이 얼마나 지났는지 체크하는 로직은 기본적으로 꼭 들어가야하는
Factor가 되는 것이죠.

이 시간 차이, 가장 기본적인  dt (Delta Tiem)의 구현은  아래와 같습니다.
뭐 중학교 수준의 dt 정도 라고 해두죠.

 

dt, 즉 시간 차이를 1/60초 라고 정한 다음에,
한 프레임 마다 1/60초를 더해주면서, update 연산 및 render 까지 이뤄지는 구조입니다.
dt의 개념이 들어갔다는 것만으로도 초등학교는 벗어난 것이 아닌가 해서 중학교 수준이라고 했습니다.

위 코드의 문제는 실제 게임을 돌려보면 1/60 초마다 한 루프씩 돌면 좋겠지만,
그렇지 못하다는 것입니다. 그래서 자동차가 정속으로 주행해야하는데, 버벅거리는 로딩이 있는 곳에서는
갑자기 차가 느려지는 현상이 발생하겠죠.

이를 개선한 코드입니다.

자, 이제는 한 프레임 마다 걸리는 시간을 계산해서
dt를 계산해 줌으로써 버벅이는 로딩이 있는 순간이라도 자동차의 속도는 일정할 겁니다.
물론 버벅일 경우 다음 장면 렌더에서는 갑자기 뿅 점프하는 듯한 느낌을 받겠지만,
정속으로  가고 있는 것과 같은 지점으로 가 있겠죠.

그런데, 위의 구현까지만 간 프로그래머도 사실 문제가 있습니다.
고등학교 수준 정도라고 해두죠.

어떤 상황에서 문제가 생기느냐 하면,
가령 자동차가 앞에 벽을 향해서 달린다고 가정을 하죠.
한 프레임 마다 자동차가 벽에 충돌했는지 안했는지 체크하여 충돌할 경우
새로 변해서 하늘나라로 날라가는 것을 구현하려고 했다고 합시다.
그런데, 자동차가 벽을 충돌하기 바로 직전에 하필 그 때에!!!
유저가 멀티 작업으로 엄청난 I/O 부하와 CPU를 괴롭히는 짓을 해버렀다고 한다면…
dt는 엄청 커지게 되겠죠. 그런데 문제는 커진 dt를 가지고 계산을 해보니,
자동차는 이미 벽을 뚫고 지나가서 아무런 충돌이 발생하지 않는 어처구니 없는 상황이 발생하게 됩니다. ㅎ

자 이런 경우도 피하기 위해서는 과연 어떻게 해야 될까요?
아래는 이런 Jumping 문제의 개선 코드입니다. 대학교 수준 정도 되겠죠.

한 프레임을 계산하는 최소의 frameTime을 준 후에 dt를 그 frameTime으로 잘게 나누어서
물리 계산을 해줍니다. 그 이후에 Render를 한방에 때리는 것이죠.
모든 게임 계산 로직을 이 inner while loop에 돌릴 필요는 없지만,
물리 dt에 관계된 로직은 모두 이 inner while loop 내에서 연산해 주시면 됩니다.

 

자, 그러면 이게 끝인가요?
위의 코드를 잘 보시면 뭔가 아쉬운 점이 있으시다는 것을 발견하신 분 계실 겁니다.
바로 dt보다 더 작은 단위의 숫자들이 truncate되는 문제일 텐데요,
정확한 계산을 위해서는 0.001초라도 버려지는 시간이 있으면 안되겠죠.
네트워크 게임에서는 Sync 맞출 때 이런 짜투리 시간 버려지면 골치아프게 되는 것 뻔합니다.
이렇게 개선할 수 있습니다.

조금 다르게 만들어 본 구조입니다.
역시, dt 단위보다 더 작은 수는 버리지 않고 모아서 다음 프레임 계산 때에 씁니다. 
이쯤 되면 거의 상용 게임을 만들어도 문제 없이 잘 돌아갑니다.
그런데 사실 위의 코드는 잠재적인 위험성이 있습니다.

극단적인 가정이지만, 정말 우연의 일치로 저렇게 만들어 놓은 게임을 하는 도중에
백그라운드로 이미지가 로딩이되고, 카카오톡 문자 메세지가 날라오면서 문자 메세지가
열통 연달아 날라오더니, 마이피플도 동시에 광고 문자를 보내고 때마침 설치해 놓고 플레이 하고 있는
게임 10개가 동시에 Local Noti를 날렸다고 가정합시다.
그랬더니 한 프레임에 걸리는 시간이 5초가 걸린 겁니다. 그야말로 대박 버벅이는 것이죠.
그런데 5초를 받아든 while loop은 5초를 잘게 dt으로 나눠서 반복하여 물리를
돌리면서 계산을 합니다. 0.1초가 dt라면 50번이 돌게 되죠.
문제는 이 때 만약 프로그래밍을 잘 해서 최적화된 물리 계산으로
5초보다 더 작게 걸린다면 회생의 가능성이 있습니다.

그런데 만약 5.1초가 걸렸다고 하면 다음 루프때에는 5.2~3초가 걸리게 되고,
이 경우 점점 걸리는 시간이 늘어나 결국 게임이 뻗어버리는 현상이 발생하게 되는 것이죠.

이 문제를 피해가기 위해서는 어떻게 해야할까요?
두가지 방법이 있겠는데요, 첫째는 dt 값을 너무 잘게 하면 안됩니다.
너무 잘게 할 경우 프레임당 쪼개지는 갯수가 많아서 더 시간이 많이 걸리겠죠.
그러므로 적당한 dt를 잡아서 한프레임당 너무 많은 연산을 하지 않도록 잡아주는 작업이 필요합니다.
랙이 걸려도 충분히 회복될 수 있도록 dt를 잡으셔야 합니다.
두번째로, max frameTime 값을 주어서 해당 프레임보다 더 클 경우
과감하게 frameTime을 그 max 값으로 주어서 나머지 부분에 대해서는 과감하게 스킵하는 방법이 있겠습니다.
이렇게 하면 적어도 뻗는 문제는 피해갈 수 있습니다.

조금 더 나아가 볼까요?
위의 코드의 문제점을 찾자면, dt보다 적은 accumulator에 있습니다.
accumulator 값에 항상 dt보다 적은 숫자들이 남아 있을 수 있어서 다음 프레임 때에
해당하는 계산을 추가로 해주게 되는데요, 이 아주 작은 숫자이지만 이런 부분으로 말미암아,
렌더링된 위치가 네트워크 게임에선 게임마다 정확히 일치하지 않는 문제가 발생합니다.
조금씩 다른데 아주 조금, 아주 조금씩 다릅니다. 환장하죠.

이런 문제를 마지막으로 개선시켜주는 방법은 아래와 같이 보정룰을 적용하는 겁니다.

해당 사항들을 개선한 코드입니다.

dt보다 더 작은 남은 시간을 이용해서 미리 선형 보정을 하여
render 시에 정확한 그림이 그려질 수 있도록 해줍니다.

자 쓰다보니 좀 길어졌는데요,
이정도로 하면 게임 프로그래밍에서의 delta time에 대해서는 어느정도 잘 동작하는
방법의 기본을 아신다고 보시면 될 것 같습니다.

[본문링크] 게임 프로그래밍에서의 delta time / dt
[1]
코멘트(이글의 트랙백 주소:/cafe/tb_receive.php?no=34889
작성자
비밀번호

 

SSISOCommunity

[이전]

Copyright byCopyright ⓒ2005, SSISO Community All Rights Reserved.