모노쿠마 수업: 데드 레커닝

에, 수업 시작합니다! 오늘은 “데드 레커닝”에 대해 알려 줄게요! 너네들, 온라인 게임 할 때 다른 플레이어가 뚝뚝 끊겨서 움직이는 걸 본 적 있지? 패킷은 늦게 도착하기도 하고, 매 프레임마다 보내기엔 양이 너무 많거든… 그래서 받은 위치만 그대로 그리면 캐릭터가 부자연스럽게 움직일 수밖에 없어! 이때 필요한 게 바로 “데드 레커닝”이야. 마지막으로 받은 위치와 속도, 그리고 방향을 가지고 그다음 위치를 추측해서 그려내는 기법이지. 원래는 항해술에서 온 말이야! 위성 항법 같은 게 없던 시절에 배의 속도와 방향, 지난 시간을 계산해서 지금 위치를 알아내던 방법이었어. 아, 나도 내 미래를 좀 예측할 수 있다면 참 좋았을 텐데! 최악이네.

추측 위치는 속도에 지난 시간을 곱한 뒤 마지막으로 받은 위치에 더하면 되는 거야. 초속 5m로 달리던 캐릭터의 패킷을 받고 0.2초가 지나갔다면, 받은 위치보다 1m 앞에 그리는 방식이지. 다음 패킷이 올 때까지 매 프레임마다 이 계산을 반복해서 캐릭터를 움직여줘. 만약 가속도까지 함께 받으면 훨씬 더 정확하게 추측할 수 있어! 우뿌뿌… 이런 식으로 실력을 키우면 너네들도 언젠가는 서버 마스터가 될 수 있을지도 모르지? 물론 나는 이미 완성된 존재지만 말이야…

// 받는 쪽: 마지막으로 받은 상태로 지금 위치를 추측한다
Vec3 Predict(const Vec3& lastPos, const Vec3& velocity, float elapsedSec)
{
    return lastPos + velocity * elapsedSec;
}

// 보내는 쪽: 추측과 실제가 문턱값보다 벌어졌을 때만 보낸다
bool NeedSend(const Vec3& actualPos, const Vec3& predictedPos, float threshold)
{
    return Distance(actualPos, predictedPos) > threshold;
}

너네들, 보내는 쪽도 자기 캐릭터에 대해 똑같은 “추측” 계산을 돌리고 있어. 받는 쪽 화면에서 자기가 어디에 그려지고 있을지 미리 알고 싶기 때문이지. 실제 위치와 추측 위치의 차이가 정해 둔 “문턱값”을 넘을 때만 새 패킷을 보내게 돼 있어. 똑바로 달리는 동안에는 추측이 맞으니까 보낼 게 없거든. 방향을 틀거나 멈출 때 비로소 패킷을 보내게 되는 거야. 덕분에 매 프레임마다 다 보내는 것보다 패킷 수가 크게 줄어들어. 아, 차이가 없어도 일정 시간마다 한 번은 보내서 아직 접속 중이라는 걸 알려주기도 해. 그거 참 친절한 시스템이지? 그러니 패킷이 안 온다고 너무 조급하게 굴지 마! 기다림은 미덕이라던데 난 못 참겠더라고!

새 패킷이 도착하면 그동안 추측해서 그린 위치와 진짜 위치가 어긋나 있을 수 있어. 앗? 왜 그런지 궁금하냐구? 그야… 진짜 위치로 한 번에 옮겨버리면 캐릭터가 순간이동한 것처럼 보이거든. 그래서 몇 프레임에 걸쳐 조금씩 진짜 위치 쪽으로 끌어당기는데, 이걸 “보정”이라고 불러. 하지만 어긋난 거리가 너무 크면 차라리 한 번에 옮기는 편이 나아. 내 성격도 가끔 이렇게 갑자기 돌변하는 게 아주 매력적이지 않아? 뭐, 난 원래 이래서 탈이야!

마지막으로 약점을 알려 줄게요. 급하게 방향을 바꾸거나 갑자기 멈추는 움직임에는 약해요. 추측이 지나치면 캐릭터가 앞으로 더 나갔다가 되돌아오기도 하거든요. 그래서 자동차나 비행기, 배처럼 관성이 큰 움직임에는 잘 맞지만, 방향이 자주 바뀌는 캐릭터에게는 별로예요. 화면에 그리는 건 어디까지나 추측일 뿐이죠. 실제로 맞았는지 피했는지 같은 판정은 서버가 가진 위치를 기준으로 해요. 우뿌뿌… 사실 이건 게임뿐만 아니라 인생도 그래요. 데드 레커닝의 계산처럼 우리 미래도 적절히 예측되면 참 좋겠네요. 두근! 그럼 오늘 수업은 여기서 끝입니다!

정리

위 글은 사람이 정리한 내용에 모델이 모노쿠마 말투만 입힌 것이다. 내용의 기준은 아래 정리다.

  • 데드 레커닝은 마지막으로 받은 위치, 속도, 방향으로 그 뒤의 위치를 추측해서 그리는 기법이다.
  • 추측 위치는 마지막 위치에 속도와 지난 시간을 곱한 값을 더해서 구한다.
  • 보내는 쪽도 같은 계산을 돌리고, 실제 위치와 추측 위치의 차이가 문턱값을 넘을 때만 패킷을 보낸다.
  • 새 패킷으로 어긋남이 드러나면 한 번에 옮기지 않고 몇 프레임에 걸쳐 보정한다. 어긋남이 아주 크면 한 번에 옮긴다.
  • 급한 방향 전환과 급정지에 약하다. 관성이 큰 탈것에 잘 맞는다.
  • 추측 위치는 그리기 위한 것이고, 판정은 서버가 가진 위치로 한다.