본문 바로가기
C.W.K.
Stream
Lesson 06 of 08 · published

멀티플레이어 게임 상태

~13 min · app, games, prediction

Level 0Poller
0 XP0/60 lessons0/10 achievements
0/120 XP to next level120 XP to go0% complete

입력과 상태는 서로 다른 빈도로

멀티플레이어 게임은 전송 빈도를 나눠. 클라이언트는 렌더링에 맞춰 입력을 30~60Hz 의 높은 빈도로 보내고, 서버는 권위 있는 상태를 10~20Hz 의 낮은 빈도로 보내면서 처치, 점수, 레벨 변경처럼 게임을 바꾸는 행동은 별도 이벤트 메시지로 보내. 클라이언트가 서버 스냅숏 사이를 보간하면 상태가 15Hz 로 와도 움직임은 60fps 로 부드럽게 보여.

클라이언트 측 예측

입력을 보내고 서버 상태를 기다리면 키를 누를 때마다 100ms 가 넘는 지연이 느껴질 수 있어. 해법은 클라이언트 측 예측이야. 입력을 로컬에 즉시 적용하고 서버의 권위 있는 상태가 오면 둘을 맞춰. 서버와 다르면 위치를 바로 옮기거나 부드럽게 보정해. Quake III 가 만든 이 패턴을 현대 멀티플레이어 게임은 어떤 형태로든 사용해.

Code

클라이언트: 입력 반복, 예측과 조정·javascript
const INPUT_RATE = 1000 / 30; // 30 Hz
let seq = 0;
const pendingInputs = [];

setInterval(() => {
  if (ws.readyState !== WebSocket.OPEN) return;
  const input = {
    seq: seq++,
    keys: getPressedKeys(),
    mouse: getMousePosition(),
    ts: performance.now(),
  };
  // 1. Send to server.
  ws.send(JSON.stringify({ type: 'game.input', data: input }));
  // 2. Apply locally for instant feedback.
  applyInputLocal(input);
  // 3. Remember it for reconciliation.
  pendingInputs.push(input);
}, INPUT_RATE);

ws.on('game.state', (state) => {
  // Server says authoritative position is X for input seq N.
  // Drop everything we already reconciled.
  while (pendingInputs.length && pendingInputs[0].seq <= state.lastInputSeq) {
    pendingInputs.shift();
  }
  // Re-apply any inputs the server hasn't seen yet.
  let pos = state.position;
  for (const i of pendingInputs) pos = predict(pos, i);
  setPosition(pos);
});
서버: 낮은 빈도의 스냅숏·python
async def state_broadcaster(manager, world):
    while True:
        snapshot = world.snapshot()  # current authoritative state
        for room in list(manager.rooms):
            await manager.broadcast(room, {
                'type': 'game.state',
                'data': snapshot,
            })
        await asyncio.sleep(1 / 15)  # 15 Hz

External links

Exercise

플레이어 둘이 방향키로 걷는 2D 데모를 만들어. 입력은 30Hz, 상태 전체 전송은 15Hz 로 하고 조정 기능이 있는 클라이언트 측 예측을 넣어. 브라우저에 100ms 의 인공 지연을 더해도 로컬 움직임은 즉시 반응해야 해. 예측을 끄고 입력 지연이 어떻게 달라지는지 비교해.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.