3. 멀티플레이어 기능 구현(지렁이 온라인 게임)

안녕하세요. 온라인 Snake 게임 개발 교육과정의 세 번째 시간입니다. 오늘은 Socket.io를 이용해 실시간 멀티플레이어 기능을 구현하는 과정을 살펴보겠습니다.

1. Socket.io 설정

먼저 서버와 클라이언트에 Socket.io를 설정합니다.

서버 측 (server.js):

const express = require('express');
const app = express();
const http = require('http').createServer(app);
const io = require('socket.io')(http);

app.use(express.static('public'));

io.on('connection', (socket) => {
  console.log('A user connected');
  // 여기에 소켓 이벤트 핸들러를 추가할 예정입니다.
});

const PORT = process.env.PORT || 3000;
http.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});

클라이언트 측 (game.js):

const socket = io();

2. 방 생성 및 참여 기능

플레이어들이 게임 방을 만들고 참여할 수 있도록 구현합니다.

서버 측:

const rooms = new Map();

io.on('connection', (socket) => {
  socket.on('createRoom', () => {
    const roomCode = generateRoomCode();
    rooms.set(roomCode, { players: [], gameStarted: false });
    socket.join(roomCode);
    socket.emit('roomCreated', roomCode);
  });

  socket.on('joinRoom', (roomCode) => {
    if (rooms.has(roomCode) && !rooms.get(roomCode).gameStarted) {
      socket.join(roomCode);
      socket.emit('joinedRoom', roomCode);
    } else {
      socket.emit('roomNotFound');
    }
  });
});

function generateRoomCode() {
  return Math.random().toString(36).substring(2, 8).toUpperCase();
}

클라이언트 측:

document.getElementById('createRoom').addEventListener('click', () => {
  socket.emit('createRoom');
});

document.getElementById('joinRoom').addEventListener('click', () => {
  const roomCode = document.getElementById('roomCodeInput').value;
  socket.emit('joinRoom', roomCode);
});

socket.on('roomCreated', (roomCode) => {
  console.log(`Room created with code: ${roomCode}`);
  // 방 코드를 화면에 표시
});

socket.on('joinedRoom', (roomCode) => {
  console.log(`Joined room: ${roomCode}`);
  // 게임 준비 화면으로 전환
});

3. 게임 상태 동기화

서버에서 게임 상태를 관리하고 모든 클라이언트에게 주기적으로 전송합니다.

서버 측:

function gameLoop(roomCode) {
  const room = rooms.get(roomCode);
  // 게임 상태 업데이트 로직
  // ...

  io.to(roomCode).emit('gameState', room.gameState);
  setTimeout(() => gameLoop(roomCode), 100);
}

io.on('connection', (socket) => {
  // ...
  socket.on('startGame', (roomCode) => {
    if (rooms.has(roomCode)) {
      rooms.get(roomCode).gameStarted = true;
      gameLoop(roomCode);
    }
  });
});

클라이언트 측:

socket.on('gameState', (gameState) => {
  // 받은 게임 상태로 화면 업데이트
  updateGameScreen(gameState);
});

function updateGameScreen(gameState) {
  // 캔버스에 게임 상태 그리기
  // ...
}

4. 플레이어 입력 처리

플레이어의 입력을 서버로 전송하고 서버에서 처리합니다.

클라이언트 측:

document.addEventListener('keydown', (event) => {
  let direction;
  switch(event.key) {
    case 'ArrowUp': direction = 'UP'; break;
    case 'ArrowDown': direction = 'DOWN'; break;
    case 'ArrowLeft': direction = 'LEFT'; break;
    case 'ArrowRight': direction = 'RIGHT'; break;
  }
  if (direction) {
    socket.emit('changeDirection', { roomCode, direction });
  }
});

서버 측:

io.on('connection', (socket) => {
  // ...
  socket.on('changeDirection', ({ roomCode, direction }) => {
    if (rooms.has(roomCode)) {
      const player = rooms.get(roomCode).players.find(p => p.id === socket.id);
      if (player) {
        player.direction = direction;
      }
    }
  });
});

5. 네트워크 지연 보정

클라이언트 사이드 예측을 구현하여 네트워크 지연에 의한 불편함을 줄입니다.

클라이언트 측:

let lastProcessedInput = 0;

function predictGameState(gameState) {
  const player = gameState.players.find(p => p.id === socket.id);
  while (lastProcessedInput < currentInput) {
    player.move();
    lastProcessedInput++;
  }
  return gameState;
}

socket.on('gameState', (gameState) => {
  gameState = predictGameState(gameState);
  updateGameScreen(gameState);
});

이렇게 구현하면 서버로부터 새로운 게임 상태를 받기 전에 클라이언트에서 먼저 예측하여 화면을 업데이트할 수 있습니다.

결론

이번 포스팅에서는 Socket.io를 이용하여 실시간 멀티플레이어 기능을 구현하는 방법을 살펴보았습니다. 방 생성 및 참여, 게임 상태 동기화, 플레이어 입력 처리, 그리고 네트워크 지연 보정까지 다루었습니다.

다음 포스팅에서는 프론트엔드 개발에 대해 더 자세히 다루겠습니다. Canvas를 이용한 게임 렌더링과 사용자 인터페이스 디자인에 대해 알아보겠습니다. 감사합니다!

관련 포스팅

결과물: 지렁이 게임 멀티 6인용(ver. 2.0) – CSAI

1. 프로젝트 개요 및 기획(지렁이 게임 온라인) – CSAI

2. 게임 로직 설계(지렁이 온라인 게임) – CSAI

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다