본문 바로가기
JavaScript

[JavaScript] 스네이크 게임 만들기

by teamnova 2024. 10. 19.
728x90

안녕하세요!

오늘은 자바스크립트를 사용해 클래식한 스네이크 게임을 만드는 방법을 알려드리겠습니다.

 

1. index.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>
    <style>
        body {
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
            background-color: #f0f0f0;
        }
        canvas {
            background-color: #fff;
            border: 2px solid #333;
        }
    </style>
</head>
<body>
    <canvas id="gameCanvas" width="400" height="400"></canvas>
    <script src="snake.js"></script>
</body>
</html>

 

  • canvas 태그를 사용해 스네이크 게임을 그릴 공간을 마련합니다.
  • id를 통해 JavaScript에서 이 캔버스를 쉽게 접근할 수 있도록 설정했습니다.
  • style 태그로 배경색과 캔버스 테두리를 설정해 간단한 디자인을 적용했습니다.

2. 스네이크와 음식의 초기 상태 정의

이제 snake.js 파일을 생성하고, 스네이크와 음식의 초기 상태를 정의해보겠습니다.

// 캔버스 설정
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');

// 게임 설정
const gridSize = 20; // 한 칸의 크기
let snake = [{ x: 100, y: 100 }]; // 스네이크의 초기 위치
let food = { x: 200, y: 200 }; // 음식의 초기 위치
let dx = gridSize; // x축 이동 방향 (오른쪽으로 시작)
let dy = 0; // y축 이동 방향 (세로 이동 없음)
let gameOver = false; // 게임 종료 상태 변수
let growing = false; // 스네이크가 자라는 중인지 상태를 관리

 

 

  • 스네이크는 배열로 관리하며, 각 요소는 스네이크의 몸 부분을 나타냅니다.
  • food는 음식의 위치를 나타내고, dx와 dy는 스네이크의 이동 방향을 관리합니다.
  • gameOver 변수는 게임 종료 상태를, growing 변수는 스네이크가 자라는 상태를 관리합니다.

 

3.  스네이크와 음식 그리기

스네이크와 음식을 캔버스에 그리는 함수를 작성합니다.

// 스네이크와 음식을 그리는 함수
function drawSnake() {
    ctx.fillStyle = 'green';
    snake.forEach(part => {
        ctx.fillRect(part.x, part.y, gridSize, gridSize);
    });
}

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

// 초기 화면 그리기
function draw() {
    ctx.clearRect(0, 0, canvas.width, canvas.height); // 화면 지우기
    drawSnake();
    drawFood();
}

 

  • drawSnake() 함수는 스네이크의 각 부분을 forEach로 순회하며 그립니다.
  • drawFood() 함수는 캔버스에 음식의 위치를 그립니다.
  • draw() 함수는 화면을 매번 지우고, 스네이크와 음식을 새로 그립니다.

4. 스네이크 움직이기

스네이크가 방향키를 누를 때마다 이동하도록 설정합니다.

// 스네이크 이동 함수
function moveSnake() {
    const head = { x: snake[0].x + dx, y: snake[0].y + dy };
    snake.unshift(head);

    // 스네이크가 자라지 않는다면 꼬리 제거
    if (!growing) {
        snake.pop();
    } else {
        growing = false; // 자라기를 완료했으니 다시 false로 설정
    }
}

// 방향 변경 함수
function changeDirection(event) {
    const keyPressed = event.keyCode;

    const LEFT = 37;
    const UP = 38;
    const RIGHT = 39;
    const DOWN = 40;

    if (keyPressed === LEFT && dx === 0) {
        dx = -gridSize;
        dy = 0;
    } else if (keyPressed === UP && dy === 0) {
        dx = 0;
        dy = -gridSize;
    } else if (keyPressed === RIGHT && dx === 0) {
        dx = gridSize;
        dy = 0;
    } else if (keyPressed === DOWN && dy === 0) {
        dx = 0;
        dy = gridSize;
    }
}

// 키보드 이벤트 리스너 추가
document.addEventListener('keydown', changeDirection);

 

  • moveSnake() 함수는 스네이크의 새로운 머리 위치를 계산하고, growing에 따라 꼬리를 제거하거나 유지합니다.
  • changeDirection() 함수는 방향키 입력에 따라 스네이크의 이동 방향을 변경합니다.

 

 

5. 음식 먹기와 길이 증가

스네이크가 음식을 먹었을 때 길이가 증가하도록 설정합니다.

// 음식 먹기 함수
function checkFoodCollision() {
    if (snake[0].x === food.x && snake[0].y === food.y) {
        // 음식 먹으면 스네이크가 자라도록 설정
        growing = true;
        placeFood();
    }
}

// 랜덤 위치에 음식 배치
function placeFood() {
    food.x = Math.floor(Math.random() * canvas.width / gridSize) * gridSize;
    food.y = Math.floor(Math.random() * canvas.height / gridSize) * gridSize;
}

 

  • 스네이크의 머리가 음식 위치와 일치할 때 growing을 true로 설정하여 길이를 증가시킵니다.
  • placeFood() 함수는 랜덤한 위치에 음식을 새로 생성합니다.

6. 벽 또는 자기자신과 충돌 처리

스네이크가 벽이나 자신의 몸에 부딪히면 게임이 종료되도록 설정합니다.

// 벽 충돌 확인 함수
function checkWallCollision() {
    const head = snake[0];
    if (
        head.x < 0 || head.x >= canvas.width ||
        head.y < 0 || head.y >= canvas.height
    ) {
        gameOver = true;
    }
}

// 몸 충돌 확인 함수
function checkSelfCollision() {
    const head = snake[0];
    for (let i = 1; i < snake.length; i++) {
        if (head.x === snake[i].x && head.y === snake[i].y) {
            gameOver = true;
        }
    }
}

// 게임 오버 메시지 표시 함수
function displayGameOver() {
    ctx.fillStyle = 'black';
    ctx.font = '30px Arial';
    ctx.fillText('Game Over', canvas.width / 2 - 80, canvas.height / 2);
}

 

 

 

7. 게임 루프 작성하기

게임이 계속 진행되도록 루프를 작성하고, 게임 오버 시 메시지를 표시합니다.

// 게임 루프
function gameLoop() {
    if (gameOver) {
        displayGameOver();
        return;
    }

    moveSnake();
    checkFoodCollision();
    checkWallCollision();
    draw();
    checkSelfCollision();

    setTimeout(gameLoop, 100);
}

gameLoop();

 

 

  • gameLoop() 함수는 0.1초마다 게임 상태를 업데이트하고, 스네이크의 이동 및 충돌 상태를 체크합니다.
  • 게임 오버 시 displayGameOver() 함수를 호출하여 게임 종료 메시지를 표시합니다.