서버 중심 데이터 관리
GameData 객체를 사용하여 서버가 클라이언트에 정보를 전달하는 과정을 중점적으로 설명하겠습니다. 코드는 매칭 요청을 처리하여 두 명의 플레이어를 매칭하고, 게임 데이터를 생성하여 클라이언트에 전송합니다.
코드 개요
이 코드의 주요 기능은 다음과 같습니다:
- 매칭 요청 처리 (handleMatchRequest 함수)
- 랜덤 몬스터 경로 생성 (generateRandomMonsterPath 함수)
- 경로 근처의 랜덤 위치 생성 (getRandomPositionNearPath 함수)
- GameData 객체 생성 및 클라이언트로 전송
매칭 요청 처리
handleMatchRequest 함수는 클라이언트로부터 매칭 요청을 받으면 대기열에 추가하고, 대기열에 두 명 이상의 플레이어가 있을 때 매칭을 성사시킵니다.
function handleMatchRequest(socket, data) {
const { userId } = data;
console.log(`매치 요청을 보낸 유저 ID: ${userId}`);
queue.push({ socket, userId });
console.log(`현재 대기열 상태: ${queue.map((user) => user.userId).join(', ')}`);
if (queue.length >= 2) {
const player1 = queue.shift();
const player2 = queue.shift();
console.log(`매칭 성공: ${player1.userId} vs ${player2.userId}`);
랜덤 몬스터 경로 생성
generateRandomMonsterPath 함수는 게임에서 몬스터가 이동할 랜덤 경로를 생성합니다. 경로는 캔버스 크기 내에서 랜덤한 x, y 좌표를 가지며, 경로가 캔버스 너비를 초과하지 않도록 합니다.
function generateRandomMonsterPath() {
const canvasHeight = RESOLUTION_HEIGHT;
const canvasWidth = RESOLUTION_WIDTH;
const path = [];
let currentX = 0;
let currentY = Math.floor(Math.random() * 21) + 500; // 500 ~ 520 범위의 y 시작
path.push({ x: currentX, y: currentY });
while (currentX < canvasWidth) {
currentX += Math.floor(Math.random() * 100) + 50; // 50 ~ 150 범위의 x 증가
if (currentX > canvasWidth) {
currentX = canvasWidth;
}
currentY += Math.floor(Math.random() * 200) - 100; // -100 ~ 100 범위의 y 변경
if (currentY < 0) {
currentY = 0;
}
if (currentY > canvasHeight) {
currentY = canvasHeight;
}
path.push({ x: currentX, y: currentY });
}
return path;
}
경로 근처의 랜덤 위치 생성
getRandomPositionNearPath 함수는 주어진 경로 근처의 랜덤 위치를 생성합니다. 이는 플레이어의 초기 타워 위치를 설정하는 데 사용됩니다.
function getRandomPositionNearPath(maxDistance, monsterPath) {
const segmentIndex = Math.floor(Math.random() * (monsterPath.length - 1));
const startX = monsterPath[segmentIndex].x;
const startY = monsterPath[segmentIndex].y;
const endX = monsterPath[segmentIndex + 1].x;
const endY = monsterPath[segmentIndex + 1].y;
const t = Math.random();
const posX = startX + t * (endX - startX);
const posY = startY + t * (endY - startY);
const offsetX = (Math.random() - 0.5) * 2 * maxDistance;
const offsetY = (Math.random() - 0.5) * 2 * maxDistance;
return {
x: posX + offsetX,
y: posY + offsetY,
};
}
GameData 객체 생성 및 클라이언트로 전송
매칭이 성사되면, 두 플레이어의 몬스터 경로와 초기 타워 위치를 생성합니다. 그런 다음 GameData 객체를 생성하고, createPlayData 함수로 데이터베이스에 저장합니다. 마지막으로, 각 클라이언트에 매칭 성공 패킷과 게임 데이터를 전송합니다.
const player1MonsterPath = generateRandomMonsterPath();
const player2MonsterPath = generateRandomMonsterPath();
let player1InitialTowerCoords = [];
let player2InitialTowerCoords = [];
for (let i = 0; i < 5; i++) {
player1InitialTowerCoords.push(getRandomPositionNearPath(200, player1MonsterPath));
player2InitialTowerCoords.push(getRandomPositionNearPath(200, player2MonsterPath));
}
createPlayData(
player1.userId,
new GameData(
player1MonsterPath,
player1InitialTowerCoords,
player1MonsterPath[player1MonsterPath.length - 1],
player2MonsterPath,
player2InitialTowerCoords,
player2MonsterPath[player2MonsterPath.length - 1],
),
);
createPlayData(
player2.userId,
new GameData(
player2MonsterPath,
player2InitialTowerCoords,
player2MonsterPath[player2MonsterPath.length - 1],
player1MonsterPath,
player1InitialTowerCoords,
player1MonsterPath[player1MonsterPath.length - 1],
),
);
const player1Payload = getPlayData(player1.userId);
const player2Payload = getPlayData(player2.userId);
player1.socket.emit('event', packet, player1Payload);
player2.socket.emit('event', { ...packet, opponentId: player1.userId }, player2Payload);
결론
이 코드는 클라이언트로부터 매칭 요청을 받아 두 명의 플레이어를 매칭하고, GameData 객체를 생성하여 각 클라이언트에 전송합니다. 이를 통해 게임 시작 시 필요한 초기 데이터(몬스터 경로, 타워 위치 등)를 클라이언트에 제공하게 됩니다. 이 과정은 실시간 게임에서 플레이어 간의 공정한 시작을 보장하고, 게임 서버와 클라이언트 간의 원활한 데이터 동기화를 가능하게 합니다.