728x90
안녕하세요.
오늘은 지난 시간에 만들었던 스네이크 게임에 몇가지 요소들을 추가해서 완성도를 높여보겠습니다.
이전에 작성한 코드는 아래 링크를 참고해주세요.
2024.10.19 - [JavaScript] - [JavaScript] 스네이크 게임 만들기
1단계: 점수 시스템 추가하기
스네이크 게임의 재미 중 하나는 얼마나 높은 점수를 기록할 수 있는지 도전하는 것입니다. 이번에는 점수 시스템을 추가해 게임을 한층 더 흥미롭게 만들어보겠습니다.
1. index.html 수정
div 태그를 사용해 점수를 표시할 영역을 추가합니다.
<body>
<canvas id="gameCanvas" width="400" height="400"></canvas>
<div id="score">Score: 0</div>
<script src="snake.js"></script>
</body>
2. snake.js 에 점수 관련 코드 추가
score 변수를 추가해 점수를 관리합니다. 음식을 먹을 때마다 score를 10점씩 증가시키고, updateScore() 함수로 점수를 화면에 표시합니다.
let score = 0; // 점수 초기화
function updateScore() {
document.getElementById('score').innerText = 'Score: ' + score;
}
function checkFoodCollision() {
if (snake[0].x === food.x && snake[0].y === food.y) {
growing = true;
score += 10; // 점수 증가
updateScore(); // 점수 업데이트
placeFood();
}
}
2단계: 속도 조절하기
스네이크 게임은 시간이 지날수록 어려워져야 재미있겠죠? 이제 스네이크의 속도가 점점 빨라지도록 만들어봅시다.
- speed 변수를 사용해 게임 루프의 속도를 관리합니다. 음식을 먹을 때마다 speed를 감소시켜 스네이크가 더 빨리 움직이도록 하였습니다.
- 속도가 너무 빨라져 게임이 너무 어려워지는 것을 방지하기 위해 최소 속도를 50ms로 제한합니다.
let speed = 100; // 초기 속도 (0.1초)
function gameLoop() {
if (gameOver) {
displayGameOver();
return;
}
moveSnake();
checkFoodCollision();
checkWallCollision();
draw();
checkSelfCollision();
setTimeout(gameLoop, speed);
}
function checkFoodCollision() {
if (snake[0].x === food.x && snake[0].y === food.y) {
growing = true;
score += 10;
updateScore();
placeFood();
if (speed > 50) speed -= 5; // 속도를 점점 빠르게
}
}
3단계: 시각적 효과 개선하기
게임의 시각적 효과를 개선하여 좀 더 재미있게 만들어볼까요? 스네이크의 머리를 더 눈에 띄게 하고, 음식을 특별한 모양으로 만들어봅시다.
- 스네이크의 머리를 더 진한 초록색으로 그려 방향을 쉽게 알 수 있도록 했습니다.
- drawFood() 함수에서는 사각형 대신 원형으로 음식을 그려 시각적인 변화를 주었습니다.
unction drawSnake() {
snake.forEach((part, index) => {
if (index === 0) {
ctx.fillStyle = 'darkgreen'; // 머리는 더 진한 색으로
} else {
ctx.fillStyle = 'green';
}
ctx.fillRect(part.x, part.y, gridSize, gridSize);
});
}
function drawFood() {
ctx.fillStyle = 'orange';
ctx.beginPath();
ctx.arc(food.x + gridSize / 2, food.y + gridSize / 2, gridSize / 2, 0, 2 * Math.PI); // 원 모양의 음식
ctx.fill();
}
4단계: 게임 오버 시 리스타트 기능 추가
게임 오버 후에 다시 게임을 시작할 수 있는 기능을 추가해볼까요? 사용자가 게임을 여러 번 도전할 수 있게 해줍니다.
function displayGameOver() {
ctx.fillStyle = 'black';
ctx.font = '30px Arial';
ctx.fillText('Game Over', canvas.width / 2 - 80, canvas.height / 2);
ctx.font = '20px Arial';
ctx.fillText('Press R to Restart', canvas.width / 2 - 100, canvas.height / 2 + 30);
}
function restartGame() {
snake = [{ x: 100, y: 100 }];
food = { x: 200, y: 200 };
dx = gridSize;
dy = 0;
score = 0;
speed = 100;
gameOver = false;
updateScore();
gameLoop();
}
document.addEventListener('keydown', function(event) {
if (event.keyCode === 82 && gameOver) { // R 키로 게임 재시작
restartGame();
}
});
- 게임 오버 시 Press R to Restart 메시지를 추가했습니다.
- restartGame() 함수는 게임을 초기화하고 다시 시작하도록 설정합니다. keydown 이벤트 리스너에서 R 키를 눌렀을 때 게임이 리셋되도록 했습니다.
최종 코드
// 캔버스 설정
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; // 스네이크가 자라는 중인지 상태를 관리
let score = 0; // 점수 초기화
let speed = 100; // 초기 속도 (0.1초)
// 스네이크와 음식을 그리는 함수
function drawSnake() {
snake.forEach((part, index) => {
if (index === 0) {
ctx.fillStyle = 'darkgreen'; // 머리는 더 진한 색으로
} else {
ctx.fillStyle = 'green';
}
ctx.fillRect(part.x, part.y, gridSize, gridSize);
});
}
function drawFood() {
ctx.fillStyle = 'orange';
ctx.beginPath();
ctx.arc(food.x + gridSize / 2, food.y + gridSize / 2, gridSize / 2, 0, 2 * Math.PI); // 원 모양의 음식
ctx.fill();
}
// 초기 화면 그리기
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height); // 화면 지우기
drawSnake();
drawFood();
}
// 스네이크 이동 함수
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);
document.addEventListener('keydown', function(event) {
if (event.keyCode === 82 && gameOver) { // R 키로 게임 재시작
restartGame();
}
});
// 음식 먹기 함수
function checkFoodCollision() {
if (snake[0].x === food.x && snake[0].y === food.y) {
growing = true;
score += 10; // 점수 증가
updateScore(); // 점수 업데이트
placeFood();
if (speed > 50) speed -= 5; // 속도를 점점 빠르게
}
}
// 점수 업데이트 함수
function updateScore() {
document.getElementById('score').innerText = 'Score: ' + score;
}
// 랜덤 위치에 음식 배치
function placeFood() {
food.x = Math.floor(Math.random() * canvas.width / gridSize) * gridSize;
food.y = Math.floor(Math.random() * canvas.height / gridSize) * gridSize;
}
// 벽 충돌 확인 함수
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);
ctx.font = '20px Arial';
ctx.fillText('Press R to Restart', canvas.width / 2 - 100, canvas.height / 2 + 30);
}
// 게임 재시작 함수
function restartGame() {
snake = [{ x: 100, y: 100 }];
food = { x: 200, y: 200 };
dx = gridSize;
dy = 0;
score = 0;
speed = 100;
gameOver = false;
updateScore();
gameLoop();
}
// 게임 루프
function gameLoop() {
if (gameOver) {
displayGameOver();
return;
}
moveSnake();
checkFoodCollision();
checkWallCollision();
draw();
checkSelfCollision();
setTimeout(gameLoop, speed);
}
gameLoop();
'JavaScript' 카테고리의 다른 글
[JavaScript] 로컬스토리지 사용해 즐겨찾는 링크 저장하기 (0) | 2024.10.31 |
---|---|
[JavaScript] Leaflet를 활용한 OpenStreetMap 지도 출력하기 (2) | 2024.10.28 |
[JavaScript] 스네이크 게임 만들기 (4) | 2024.10.19 |
[JavaScript] 비행기 슈팅 게임 만들기 - (5) 점수, 목숨, 다시 시작 구현 (2) | 2024.10.10 |
[JavaScript] 비행기 슈팅 게임 만들기 - (4) 충돌 감지와 게임 오버 (2) | 2024.10.04 |