원문 : https://www.gabrielgambetta.com/entity-interpolation.html
Entity Interpolation - Gabriel Gambetta
Fast-Paced Multiplayer (Part III): Entity Interpolation Client-Server Game Architecture · Client-Side Prediction and Server Reconciliation · Entity Interpolation · Lag Compensation · Live Demo Introduction In the first article of the series, we introduced
www.gabrielgambetta.com
본 게시물은 Gabriel Gambetta가 쓴 Fast-Paced Multiplayer를 해석한 글입니다. 원문과 내용은 거의 같으나 일부 각색이 들어갈 수 있습니다.
혹시 읽으시면서 틀리거나 이상한 부분이 있으면 꼭 댓글에 적어주세요.
Introduction
첫 번째 글에서는 authoritative server의 개념과 치트 방지에 관해 소개했었다. 하지만 이 기술을 그대로 쓴다면 잠재적으로 게임 진행과 반응성에 showstopper(하드웨어나 소프트웨어를 사용 못하게 할 정도의 버그) 문제를 일으킬 수 있다. 두 번째 글에서는 이 문제를 극복할 수 있는 client-side prediction에 대해 소개했다.
앞선 두 글 덕분에 유저는 전송 지연(transmission delays)이 있는 authoritative server와 인터넷으로 연결되어 있음에도, 마치 싱글 플레이어 게임을 하는 듯한 느낌을 받을 수 있었다.
이번 글에서는 같은 서버에 다른 유저가 조종하는 캐릭터가 연결되어 있는 경우에 대해서 살펴볼 것이다.
Server time step
이전 글에서 묘사된 서버의 작동 방식은 꽤나 단순했다. 클라이언트의 입력을 읽고, 게임 상태를 갱신하고, 다시 클라이언트에게 결과를 전송한다. 그러나 둘 이상의 클라이언트가 연결되어 있을 때는 약간 다르다.
여러 클라이언트들이 연결되어 있을 때는 각각이 동시에 입력을 보낼 것이다. 각 클라이언트의 입력을 받을 때마다 게임 상태를 갱신하고 그것을 다시 전파하는 것(broadcasting)은 CPU와 네트워크 대역폭(bandwidth)을 너무 많이 소모할 것이다.
더 나은 방법으로 클라이언트의 입력을 받으면 처리하지 않은 상태로 큐에 넣는다. 대신 게임 상태를 주기적으로 갱신한다. 갱신 사이의 지연 시간을 time step이라 부른다. 매 갱신마다 처리되지 않은 클라이언트의 입력이 적용되고, 새로운 게임 상태는 클라이언트들에게 전파된다.
정리하면, 게임 상태는 클라이언트 입력의 유무와 양에 관계없이 예측 가능한 속도로 갱신된다.
Dealing with low-frequency updates
클라이언트의 입장에서 보면 이 방식은 이전과 마찬가지로 매끄럽게 작동한다. Client-side prediction은 갱신 지연과 무관하게 동작하므로 비교적 낮은 갱신 주기라 하더라도 정확하게 작동한다. 그러나 낮은 주기로 게임 상태를 전파하기 때문에, 클라이언트는 게임 속에서 움직이는 다른 객체(entity)들에 대한 정보가 매우 부족하다.
첫 예제로 time step은 100ms로 설정하고 게임 상태를 받을 때마다 다른 캐릭터들의 위치를 갱신할 것이다. 그리고 이건 매 100ms마다 부드럽게 움직이는 대신 뚝뚝 끊기는 움직임이 될 것이다.
개발하는 게임의 종류에 따라 이런 문제를 다루는 방법은 여러가지가 있다. 일반적으로 객체들의 움직임이 더 예측 가능할수록 해결하기 더 쉽다.
Dead reckoning
당신이 자동차 레이싱 게임을 만든다고 가정하자. 매우 빠른 속도로 달리는 차는 예측이 가능하다. 예를 들어 만약 초속 100m로 가고 있다면, 1초 뒤에는 시작 지점으로부터 거의 100m 앞에 있을 것이다.
왜 "거의"일까? 그 1초 동안 자동차는 조금 속도를 가속하거나 감속, 혹은 조금 오른쪽이나 왼쪽으로 틀 수도 있다. 여기서 중요한 것은 "조금"이다. 자동차 정도의 기동성이라면 다음 위치 정보는 유저가 무엇을 하던 이전의 위치, 속력과 방향에 크게 의존한다. 다시 말해 레이싱 자동차가 갑자기 180도 회전을 할 수는 없다는 것이다.
이것이 매 100ms마다 갱신되는 서버에서는 어떻게 작동할까? 클라이언트는 모든 자동차의 속력과 방향을 받는다. 그리고 그다음 100ms동안은 어떠한 정보도 받지 않지만, 계속 달리고 있다는 걸 보여주어야 한다. 가장 간단한 방법은 자동차의 방향과 속력이 100ms동안 일정하게 유지될 것이라 가정하고, 그 수치를 가지고 자동차를 계속 움직인다. 그리고 100ms 후 서버에서 게임 상태가 도착하면, 자동차의 위치는 변경된다.
그 변경이 여러가지 요인에 의해서 클 수도 상대적으로 작을 수도 있다. 만약 유저가 자동차의 방향을 유지하고 속력을 바꾸지 않는다면, 예상한 위치는 변경된 위치와 정확히 일치할 것이다. 반대로 만약 유저가 어떤 것과 부딪쳤다면, 예상은 완전히 틀리게 된다.
Dead reckoning은 느린 속도의 전함 시뮬레이션에 사용될 수도 있다. 사실 dead reckoning은 항해법에서 유래된 것이다.
Entity interpolation
유저의 방향과 속력이 갑자기 바뀔 수 있는 상황에서는 dead reckoning이 전혀 먹히지 않는다. 예를 들어 3D 슈팅 게임같이 유저들이 매우 빠른 속도로 달리거나 멈추고 돌면 이전 정보로 위치와 속도를 예측할 수가 없어지기 때문에 dead reckoning을 근본적으로 쓸모없게 만든다.
우리가 가지고 있는 것은 매 100ms의 위치 정보이다. 요점은 그 사이에 일어나는 일을 유저에게 어떻게 보여줄 것인가다. 해결의 열쇠는 유저에게 다른 유저들의 과거를 보여주는 것이다.
만약 t = 1000때의 위치 정보를 받았다고 하자. 이미 t = 900 때의 정보는 받았으므로 그 유저가 t = 900과 t = 1000 사이의 모습을 알 수 있다. 그러므로 t = 1000과 t = 1100 사이에 100ms 이전의 모습인 t = 900과 t = 1000 사이의 모습을 보여주는 것이다. 이 방식이라면 100ms "늦게" 보여주는 대신, 유저에게 항상 실제 이동 정보를 보여주게 된다.
t = 900부터 t = 1000까지 보간하는 데 사용하는 위치 정보는 게임에 의존한다. 보간법은 보통의 경우 충분히 잘 동작한다. 만약 그렇지 않는다면 매 갱신마다 더 세세한 이동 정보를 보낼 수 있다. 유저가 따라온 직선 정보들(a sequence of straight segments followed by player)이나 매 10ms마다 보간하였을 때 더 좋게 보이는 샘플링된 위치들(positions sampled every 10ms which look better when interpolated)이 그 예이다(솔직히 이 부분은 정확하게 해석하기 힘들어서 원문도 같이 써놨습니다).
이 방법의 특징은 모든 유저들이 각자의 현재에서 다른 객체들의 과거를 보기 때문에 각각 조금씩 다르게 렌더링된 게임을 보게 된다. 그러나 실시간 게임이라 할지라도 일반적으로 100ms정도의 차이는 느낄 수 없다.
예외인 경우는 유저가 다른 유저를 쏘는 등의 공간적, 시간적 정확성이 많이 요구될 때이다. 다른 유저의 과거를 보기 때문에 유저는 100ms 전의 상대방을 쏘게 되므로 조준이 빗나가게 된다. 이 문제에 대해서는 다음 글에서 다룰 예정이다.
Summary
Authoritative server, 느린 갱신과 네트워크 지연을 가진 클라이언트-서버 환경에서 유저들에게 연속성의 환상과 부드러운 움직임을 제공해야 한다. 두 번째 글에서는 client-side prediction과 server reconcilation을 사용하여 실시간으로 유저가 제어하는 움직임을 보여주는 방법에 대해서 알아보았다. 이것은 게임을 못할정도의 지연을 제거함으로써 유저의 입력이 즉각적으로 나타나는 것을 보장한다.
그러나 다른 객체들은 여전히 문제가 있다. 이 글에서는 그것들을 다루는 두 가지 방법에 대해서 알아보았다.
첫 번째는 dead reckoning으로 객체의 위치를 이전 위치, 속력과 가속 정보로 예측할 수 있는 종류의 게임에 사용한다. 이 방법은 조건이 맞지 않으면 쓸 수 없게 된다.
두 번째는 entity interpolation으로 다른 객체들을 약간 늦게 보여주는 대신, 서버로부터 받은 실제 객체 정보를 사용한다. 유저는 현재에서 다른 객체들의 과거를 본다. 이 방법은 보통의 경우 매우 잘 작동한다.
그러나 아무런 처리가 안되어 있다면, 움직이는 물체를 쏘는 것과 같이 높은 공간적, 시간적 정확성이 요구되는 상황에는 그 환상이 무너지게 된다. 유저1이 보는 유저2의 위치는 서버가 보는 유저2의 위치와 다르기 때문에, 헤드샷은 불가능하게 된다! 어떠한 게임도 헤드샷 없이는 성립이 안되기 때문에, 이 문제에 대해서는 다음 글에서 다룰 예정이다.