JS로 숫자 게임 만들기
얼마전 숫자 게임을 플레이하는 유튜브를 보게 되었습니다. 실제로 플레이해보니 간단한 방법에 비해 너무 재밌어서 흥미가 생겼습니다. 생각해보니 무작위 값이 담긴 테이블을 생성하는 것부터 마우스 이벤트까지 다양한 기능을 확인할 수 있어서 만들어 보기로 했습니다.
미리보기
-https://github.com/devseop/applegame-vanilla-js
목표
- 사용자 입력을 통해 무작위 값이 담긴 테이블을 만들기
- 드래그 앤 드롭 이벤트를 활용해 값을 저장하고 상태를 변경하기
- 제한시간을 두고 시간이 끝나면 더 이상 게임이 진행되지 않도록 설정, 게임 끝을 알리기
게임 규칙
- 10x10 격자에 1-9 사이의 랜덤 숫자를 생성합니다.
- 마우스로 드래그하여 숫자들을 선택할 수 있습니다.
- 선택한 숫자들의 합이 10이 되면 해당 숫자들이 제거됩니다. (X로 표시)
- 숫자 섞기 버튼을 사용하여 남은 숫자들을 재배치할 수 있습니다. (최대 3번)
- 2분(120초) 제한시간 내에 최대한 모든 숫자를 제거하는 것이 목표입니다.
1. 판다지기
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>숫자 게임</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="game-container">
<div class="game-header">
<div id="timeLeft" class="time-left">남은 시간: 120초</div>
<button id="shuffleButton" class="shuffle-button">숫자 섞기 0 / 3</button>
</div>
<div id="board" class="board"></div>
</div>
<script src="script.js"></script>
</body>
</html>
2. 게임을 위한 script.js 작성하기
2-1. 보드 생성하기
먼저 보드를 그리고 필요한 상태를 작성했습니다. 함수형으로 작성하는 것도 좋지만 상태가 분산될 뿐만 아니라 매번 상태를 전달해줘야 하는 불편함이 있어 class
로 작성했습니다. 보드를 그릴 때 생성되는 숫자들의 합이 10이 되는 것이 목적이므로 Math.ceil(Math.random() * 9)
로 반올림을 하면서 최대 9까지 생성되도록 했습니다.
class Board() {
constructor(n = 10) {
this.n = n;
this.board = [];
this.selected = [];
this.isDragging = false;
this.startPosition = null;
this.shuffleTime = 0;
this.maxShuffles = 3;
this.timeLeft = 120;
this.gameTimer = null;
this.isGameOver = false;
this.init();
}
generateBoard() {
this.board = Array.from({ length: this.n }, () =>
Array.from({ length: this.n }, () => Math.ceil(Math.random() * 9))
);
}
}
2. 드래그 앤 드롭 이벤트 처리
setupEventListeners() {
const boardElement = document.getElementById('board');
// 마우스 이벤트
boardElement.addEventListener('mousedown', (e) => {
if (e.target.classList.contains('cell') && !this.isGameOver) {
this.handleMouseDown(e);
}
});
// mousemove로 드래그 영역 처리
boardElement.addEventListener('mousemove', (e) => {
if (this.isDragging && e.target.classList.contains('cell') && !this.isGameOver) {
this.handleMouseMove(e);
}
});
document.addEventListener('mouseup', () => {
if (!this.isGameOver) {
this.handleMouseUp();
}
});
}
3. 제한시간 시스템
startTimer() {
this.gameTimer = setInterval(() => {
this.timeLeft--;
this.updateTimeDisplay();
if (this.timeLeft <= 0) {
this.endGame();
}
}, 1000);
}
updateTimeDisplay() {
const timeElement = document.getElementById('timeLeft');
const timeString = `${this.timeLeft}초`;
timeElement.textContent = `남은 시간: ${timeString}`;
// 30초 이하일 때 빨간색으로 변경
if (this.timeLeft <= 30) {
timeElement.style.color = 'red';
} else {
timeElement.style.color = 'black';
}
}
4. 게임 종료 처리
endGame() {
this.isGameOver = true;
clearInterval(this.gameTimer);
// 게임 헤더를 '게임끝!'으로 변경
const gameHeader = document.querySelector('.game-header');
gameHeader.innerHTML = '<div class="time-left" style="color: red; font-weight: bold; font-size: 18px;">게임끝!</div>';
// 게임 오버 상태로 UI 변경
const boardElement = document.getElementById('board');
boardElement.style.cursor = 'not-allowed';
// 모든 셀에 게임 오버 스타일 적용
const cells = document.querySelectorAll('.cell:not(.removed)');
cells.forEach(cell => {
cell.style.cursor = 'not-allowed';
cell.style.opacity = '0.6';
});
}