HTML, CSS 및 JavaScript로 Snake 게임 만드는 방법

HTML, CSS 및 JavaScript로 Snake 게임 만드는 방법
Cozy CodingPosted On Jun 19, 20249 min read

Game screenshot

클래식 스네이크 게임을 만드는 것은 JavaScript 기술을 향상시키는 환상적인 방법입니다. 이 튜토리얼에서는 HTML, CSS 및 JavaScript를 사용하여 간단한 스네이크 게임을 만드는 방법을 단계별로 안내해 드리겠습니다.

여기에서 게임을 확인하실 수 있고, 여기에서 GitHub 저장소를 확인하실 수 있습니다.

HTML 구조

HTML 구조부터 시작할게요. 주요 구성 요소는 게임을 렌더링하는 캔버스, 현재 및 최고 점수를 표시하는 스코어보드, 그리고 효과음을 출력하는 오디오 요소입니다.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Snake Game</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <div class="game-container">
        <div class="score-board">
            <span id="score">Score: 0</span>
            <span id="high-score">High Score: 0</span>
        </div>
        <canvas id="gameCanvas"></canvas>
    </div>
    <audio id="eatSound" src="eat.wav"></audio>
    <audio id="bombSound" src="bomb.wav"></audio>
    <audio id="gameOverSound" src="gameover.wav"></audio>
    <script src="script.js"></script>
</body>
</html>

CSS 스타일링

다음으로, 게임을 중앙 정렬하고 캔버스와 스코어보드 스타일을 추가할게요.

body {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    margin: 0;
    background-color: #282c34;
    color: #61dafb;
    font-family: 'Arial', sans-serif;
}
.game-container {
    text-align: center;
}
.score-board {
    margin-bottom: 10px;
    font-size: 24px;
}
canvas {
    border: 1px solid #61dafb;
    background-color: #000;
}

JavaScript Logic

자, 이제 게임을 기능적으로 만들기 위해 JavaScript에 집중해 봅시다. 게임을 초기화하는 것, 게임 루프를 처리하는 것, 뱀을 그리고 움직이는 것, 음식을 다루는 것, 그리고 게임 오버 조건을 확인하는 것 등이 있습니다.

초기 설정

먼저 캔버스, 콘텍스트 및 초기 게임 변수를 설정합니다.

const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const scoreElement = document.getElementById('score');
const highScoreElement = document.getElementById('high-score');
const eatSound = document.getElementById('eatSound');
const bombSound = document.getElementById('bombSound');
const gameOverSound = document.getElementById('gameOverSound');
const gridSize = 20;
canvas.width = 400;
canvas.height = 400;
let snake = [{x: gridSize * 2, y: gridSize * 2}];
let direction = {x: 0, y: 0};
let food = {};
let bombFood = null;
let score = 0;
let highScore = localStorage.getItem('highScore') || 0;
let bombTimeout;
highScoreElement.innerText = `High Score: ${highScore}`;

게임 초기화

init 함수는 초기 음식을 배치하고 키 입력에 대한 이벤트 리스너를 설정합니다.

function init() {
    placeFood();
    document.addEventListener('keydown', changeDirection);
    gameLoop();
}

게임 루프

게임 루프 함수는 게임의 핵심입니다. 일정 간격으로 게임 상태를 업데이트합니다.

function gameLoop() {
    if (isGameOver()) {
        return;
    }
    setTimeout(() => {
        clearCanvas();
        drawSnake();
        moveSnake();
        drawFood();
        if (bombFood) drawBombFood();
        checkFoodCollision();
        checkBombFoodCollision();
        gameLoop();
    }, 100);
}

캔버스 초기화

clearCanvas 함수는 매번 새로 그려짐에 앞서 캔버스를 지웁니다.

function clearCanvas() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
}

뱀 그리기

다음은 캔버스에 뱀을 렌더링하는 drawSnake 함수입니다.

function drawSnake() {
    ctx.fillStyle = 'green';
    snake.forEach((segment, index) => {
        if (index === 0) {
            ctx.fillStyle = 'lightgreen'; // 머리 색상
        } else {
            ctx.fillStyle = 'green';
        }
        ctx.fillRect(segment.x, segment.y, gridSize, gridSize);
    });
}

뱀 이동

moveSnake 함수는 현재 방향을 기반으로 뱀의 위치를 업데이트합니다.

function moveSnake() {
    const head = {x: snake[0].x + direction.x, y: snake[0].y + direction.y};
    if (head.x >= canvas.width) {
        head.x = 0;
    } else if (head.x < 0) {
        head.x = canvas.width - gridSize;
    }
    if (head.y >= canvas.height) {
        head.y = 0;
    } else if (head.y < 0) {
        head.y = canvas.height - gridSize;
    }
    snake.unshift(head);
    snake.pop();
}

방향 변경

changeDirection 함수는 뱀의 방향을 변경하는 키 입력을 처리합니다.

function changeDirection(event) {
    const keyPressed = event.keyCode;
    const LEFT = 37;
    const UP = 38;
    const RIGHT = 39;
    const DOWN = 40;
    const W = 87;
    const A = 65;
    const S = 83;
    const D = 68;
    const goingUp = direction.y === -gridSize;
    const goingDown = direction.y === gridSize;
    const goingRight = direction.x === gridSize;
    const goingLeft = direction.x === -gridSize;
    if ((keyPressed === LEFT || keyPressed === A) && !goingRight) {
        direction = {x: -gridSize, y: 0};
    } else if ((keyPressed === UP || keyPressed === W) && !goingDown) {
        direction = {x: 0, y: -gridSize};
    } else if ((keyPressed === RIGHT || keyPressed === D) && !goingLeft) {
        direction = {x: gridSize, y: 0};
    } else if ((keyPressed === DOWN || keyPressed === S) && !goingUp) {
        direction = {x: 0, y: gridSize};
    }
}

음식 배치

placeFood 함수는 캔버스의 무작위 위치에 음식을 배치합니다.

function placeFood() {
    food = {
        x: Math.floor(Math.random() * canvas.width / gridSize) * gridSize,
        y: Math.floor(Math.random() * canvas.height / gridSize) * gridSize
    };
}

음식 그리기

drawFood 함수는 캔버스에 음식을 렌더링합니다.

function drawFood() {
    ctx.fillStyle = 'red';
    ctx.fillRect(food.x, food.y, gridSize, gridSize);
}

음식 충돌 처리

checkFoodCollision 함수는 뱀이 음식을 먹었는지 확인합니다.

function checkFoodCollision() {
    if (snake[0].x === food.x && snake[0].y === food.y) {
        eatSound.play();
        score++;
        scoreElement.innerText = `Score: ${score}`;
        snake.push({});
        placeFood();
        if (Math.random() < 0.1) {
            placeBombFood();
        }
    }
}

폭탄 음식 배치

placeBombFood 함수는 뱀이 추가 점수를 얻을 수 있는 폭탄 음식을 배치합니다.

function placeBombFood() {
    bombFood = {
        x: Math.floor(Math.random() * canvas.width / gridSize) * gridSize,
        y: Math.floor(Math.random() * canvas.height / gridSize) * gridSize
    };
    bombSound.play();
    bombTimeout = setTimeout(() => {
        bombFood = null;
    }, 5000);
}

폭탄 음식 그리기

drawBombFood 함수는 캔버스에 폭탄 음식을 렌더링합니다

function drawBombFood() {
    if (bombFood) {
        ctx.fillStyle = 'purple';
        ctx.fillRect(bombFood.x, bombFood.y, gridSize, gridSize);
    }
}

폭탄 음식 충돌 처리

테이블 태그를 Markdown 형식으로 변경하세요.

function checkBombFoodCollision() {
    if (bombFood && snake[0].x === bombFood.x && snake[0].y === bombFood.y) {
        eatSound.play();
        score += 2;
        scoreElement.innerText = `점수: ${score}`;
        snake.push({}, {});
        bombFood = null;
        clearTimeout(bombTimeout);
    }
}

게임 종료 확인

isGameOver 함수는 뱀이 자신과 충돌했는지 확인합니다.

function isGameOver() {
    for (let i = 4; i < snake.length; i++) {
        if (snake[i].x === snake[0].x && snake[i].y === snake[0].y) {
            playGameOverSound()
                .then(() => {
                    if (score > highScore) {
                        highScore = score;
                        localStorage.setItem('highScore', highScore);
                        highScoreElement.innerText = `High Score: ${highScore}`;
                    }
                    alert('게임 오버!');
                })
                .catch((error) => {
                    console.error('게임 오버 사운드 재생 중 오류 발생:', error);
                    if (score > highScore) {
                        highScore = score;
                        localStorage.setItem('highScore', highScore);
                        highScoreElement.innerText = `High Score: ${highScore}`;
                    }
                    alert('게임 오버!');
                });
            return true;
        }
    }
    return false;
}

게임 오버 사운드 재생

playGameOverSound 함수는 게임 오버 사운드 효과를 재생합니다.

function playGameOverSound() {
    return new Promise((resolve, reject) => {
        gameOverSound.onended = resolve;
        gameOverSound.onerror = reject;
        gameOverSound.play();
    });
}

게임 시작

마침내 init 함수를 호출하여 게임을 시작합니다.

init();

결론

여기에서 간단한 뱀 게임이 HTML, CSS 및 JavaScript로 구축되었습니다. 레벨, 다양한 종류의 음식 또는 보다 복잡한 뱀 이동과 같은 기능을 추가하여 이 게임을 확장할 수 있습니다.

읽어 주셔서 감사합니다! 이 기사를 즐겁게 보셨다면 미디엄에서 저를 팔로우해 주시면 저의 향후 컨텐츠를 계속 받아보실 수 있습니다. 응원을 보내주세요!

콘텐츠가 마음에 드셨다면 커피 한 잔 사주는 것으로 응원해 주세요!

BuildingaSimpleSnakeGamewithHTMLCSSandJavaScript_1.png