자바스크립트 벽돌깨기 게임 소스
어릴 때 아케이드 게임에서 즐겼던 '벽돌깨기' 게임 기억하시나요? 별돌깨기 게임은 정말 단순하지만 중독성이 강해서 재미있었죠. 오늘은 그런 추억을 떠올리며, HTML, CSS, JavaScript를 활용해서 직접 벽돌깨기 게임을 만들어 보았습니다.
이 게임은 500px × 600px 크기의 캔버스에 200개의 무작위 색상 벽돌이 쌓여 있어요. 각 벽돌을 깨면 다른 점수를 얻을 수 있고, 공이 바닥에 닿으면 게임이 끝나죠. 키보드로 바를 움직여서 공을 튕겨 벽돌을 깨는 동작은 고전적인 아케이드 게임의 느낌을 그대로 살렸습니다.
각 벽돌의 색깔마다 다른 점수를 획득할 수 있고, 점수가 올라갈 때마다 공의 속도가 조금씩 빨라지도록 했습니다.
벽돌깨기 게임의 코드를 완성하고 실행하면 다음과 같이 동작됩니다. 정말 흥미롭죠?
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>벽돌깨기 게임</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="game-container">
<h1>벽돌깨기 게임</h1>
<div class="score">점수: <span id="score">0</span></div>
<canvas id="gameCanvas" width="500" height="600"></canvas>
<div id="gameOver" class="game-over">GAME OVER</div>
<button id="startButton">시작</button>
<button id="stopButton">종료</button>
</div>
<script src="script.js"></script>
</body>
</html>
style.css
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #f0f0f0;
}
.game-container {
text-align: center;
position: relative;
}
canvas {
display: block;
margin: 0 auto;
background-color: #000;
}
button {
margin: 5px;
}
.game-over {
display: none;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 48px;
color: red;
background-color: rgba(0, 0, 0, 0.7);
padding: 20px;
border-radius: 10px;
}
111
script.js
// 캔버스와 캔버스 컨텍스트 설정
const canvas = document.getElementById("gameCanvas"); // HTML에서 캔버스 요소를 가져옵니다.
const ctx = canvas.getContext("2d"); // 2D 렌더링 컨텍스트를 얻어옵니다.
// 게임 요소 초기화
let score = 0; // 게임 점수를 저장하는 변수를 초기화합니다.
let ball = { x: canvas.width / 2, y: canvas.height - 30, dx: 2, dy: -2, radius: 5, speed: 1 }; // 공의 초기 위치, 이동 속도, 반지름, 속도 등을 설정합니다.
let paddle = { height: 10, width: 50, x: (canvas.width - 50) / 2 }; // 바의 초기 크기와 위치를 설정합니다.
let rightPressed = false; // 오른쪽 화살표 키가 눌려 있는지 여부를 저장하는 변수를 초기화합니다.
let leftPressed = false; // 왼쪽 화살표 키가 눌려 있는지 여부를 저장하는 변수를 초기화합니다.
let isGameOver = false; // 게임이 종료되었는지 여부를 저장하는 변수를 초기화합니다.
// 벽돌 초기화
const brickRowCount = 20; // 벽돌 행의 개수를 설정합니다.
const brickColumnCount = 10; // 벽돌 열의 개수를 설정합니다.
const brickWidth = 50; // 벽돌의 너비를 설정합니다.
const brickHeight = 10; // 벽돌의 높이를 설정합니다.
const brickPadding = 2; // 벽돌 사이의 간격을 설정합니다.
const brickOffsetTop = 30; // 벽돌이 시작되는 영역의 상단 여백을 설정합니다.
const brickOffsetLeft = 0; // 벽돌이 시작되는 영역의 좌측 여백을 설정합니다.
let bricks = []; // 벽돌을 저장할 배열을 선언합니다.
// 이중 배열을 사용하여 벽돌 초기화
for (let c = 0; c < brickColumnCount; c++) {
bricks[c] = [];
for (let r = 0; r < brickRowCount; r++) {
// 무작위로 색상 및 포인트(점수) 설정
const colors = ['red', 'blue', 'yellow', 'green'];
const color = colors[Math.floor(Math.random() * colors.length)];
const points = color === 'red' ? 40 : color === 'blue' ? 30 : color === 'yellow' ? 20 : 10;
// 벽돌 객체 생성 및 초기화
bricks[c][r] = { x: 0, y: 0, status: 1, color: color, points: points };
}
}
// 키보드 입력 핸들링
document.addEventListener("keydown", keyDownHandler, false); // 키가 눌렸을 때 이벤트를 처리합니다.
document.addEventListener("keyup", keyUpHandler, false); // 키가 떼졌을 때 이벤트를 처리합니다.
// 게임 시작 및 종료 버튼에 대한 클릭 이벤트 핸들링
document.getElementById("startButton").addEventListener("click", startGame); // 시작 버튼 클릭 이벤트를 처리합니다.
document.getElementById("stopButton").addEventListener("click", stopGame); // 종료 버튼 클릭 이벤트를 처리합니다.
// 키보드 입력 핸들러 함수
function keyDownHandler(e) { // 키가 눌렸을 때 실행되는 함수입니다.
if (e.key === "Right" || e.key === "ArrowRight") { // 오른쪽 화살표 키가 눌렸는지 확인합니다.
rightPressed = true; // 오른쪽 화살표 키가 눌렸음을 표시합니다.
} else if (e.key === "Left" || e.key === "ArrowLeft") { // 왼쪽 화살표 키가 눌렸는지 확인합니다.
leftPressed = true; // 왼쪽 화살표 키가 눌렸음을 표시합니다.
}
}
function keyUpHandler(e) { // 키가 떼졌을 때 실행되는 함수입니다.
if (e.key === "Right" || e.key === "ArrowRight") { // 오른쪽 화살표 키가 떼졌는지 확인합니다.
rightPressed = false; // 오른쪽 화살표 키가 떼졌음을 표시합니다.
} else if (e.key === "Left" || e.key === "ArrowLeft") { // 왼쪽 화살표 키가 떼졌는지 확인합니다.
leftPressed = false; // 왼쪽 화살표 키가 떼졌음을 표시합니다.
}
}
// 충돌 감지 함수
function collisionDetection() {
for (let c = 0; c < brickColumnCount; c++) { // 모든 벽돌에 대해 반복합니다.
for (let r = 0; r < brickRowCount; r++) {
const b = bricks[c][r]; // 현재 벽돌을 가져옵니다.
if (b.status === 1) { // 벽돌이 존재하는 상태인지 확인합니다.
// 공과 벽돌이 충돌하는지 확인합니다.
if (
ball.x > b.x &&
ball.x < b.x + brickWidth &&
ball.y > b.y &&
ball.y < b.y + brickHeight
) {
ball.dy = -ball.dy; // 공의 이동 방향을 바꿉니다.
b.status = 0; // 벽돌을 제거합니다.
score += b.points; // 점수를 증가시킵니다.
document.getElementById("score").innerText = score; // 화면에 점수를 업데이트합니다.
updateBallSpeed(); // 점수에 따라 공의 속도를 업데이트합니다.
}
}
}
}
}
// 점수에 따라 공의 속도를 업데이트하는 함수
function updateBallSpeed() {
ball.speed = 1 + Math.floor(score / 100) * 0.1; // 점수에 따라 공의 속도를 계산합니다.
// 공의 x 방향과 y 방향 속도를 설정합니다.
ball.dx = ball.dx > 0 ? ball.speed : -ball.speed;
ball.dy = ball.dy > 0 ? ball.speed : -ball.speed;
}
// 공 그리기 함수
function drawBall() {
ctx.beginPath(); // 새로운 경로를 시작합니다.
ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2); // 원을 그립니다.
ctx.fillStyle = "#fff"; // 채우기 색상을 설정합니다.
ctx.fill(); // 도형을 채웁니다.
ctx.closePath(); // 경로를 닫습니다.
}
// 바 그리기 함수
function drawPaddle() {
ctx.beginPath(); // 새로운 경로를 시작합니다.
ctx.rect(paddle.x, canvas.height - paddle.height - 10, paddle.width, paddle.height); // 직사각형을 그립니다.
ctx.fillStyle = "#fff"; // 채우기 색상을 설정합니다.
ctx.fill(); // 도형을 채웁니다.
ctx.closePath(); // 경로를 닫습니다.
}
// 벽돌 그리기 함수
function drawBricks() {
for (let c = 0; c < brickColumnCount; c++) { // 모든 벽돌에 대해 반복합니다.
for (let r = 0; r < brickRowCount; r++) {
if (bricks[c][r].status === 1) { // 벽돌이 존재하는 상태인지 확인합니다.
const brickX = c * (brickWidth + brickPadding) + brickOffsetLeft; // 벽돌의 x 좌표를 계산합니다.
const brickY = r * (brickHeight + brickPadding) + brickOffsetTop; // 벽돌의 y 좌표를 계산합니다.
bricks[c][r].x = brickX; // 벽돌의 x 좌표를 설정합니다.
bricks[c][r].y = brickY; // 벽돌의 y 좌표를 설정합니다.
ctx.beginPath(); // 새로운 경로를 시작합니다.
ctx.rect(brickX, brickY, brickWidth, brickHeight); // 직사각형을 그립니다.
ctx.fillStyle = bricks[c][r].color; // 채우기 색상을 설정합니다.
ctx.fill(); // 도형을 채웁니다.
ctx.closePath(); // 경로를 닫습니다.
}
}
}
}
// 게임 그리기 함수
function draw() {
if (isGameOver) return; // 게임이 종료되었으면 함수를 종료합니다.
ctx.clearRect(0, 0, canvas.width, canvas.height); // 캔버스를 지웁니다.
drawBricks(); // 벽돌을 그립니다.
drawBall(); // 공을 그립니다.
drawPaddle(); // 바를 그립니다.
collisionDetection(); // 충돌을 감지합니다.
// 공의 이동 경로를 처리합니다.
if (ball.x + ball.dx > canvas.width - ball.radius || ball.x + ball.dx < ball.radius) {
ball.dx = -ball.dx; // 공의 x 방향을 변경합니다.
}
if (ball.y + ball.dy < ball.radius) {
ball.dy = -ball.dy; // 공의 y 방향을 변경합니다.
} else if (ball.y + ball.dy > canvas.height - ball.radius - paddle.height - 10) {
if (ball.x > paddle.x && ball.x < paddle.x + paddle.width) { // 공이 바에 닿았는지 확인합니다.
ball.dy = -ball.dy; // 공의 y 방향을 변경합니다.
} else {
gameOver(); // 게임 오버 처리를 실행합니다.
}
}
ball.x += ball.dx; // 공의 x 좌표를 이동시킵니다.
ball.y += ball.dy; // 공의 y 좌표를 업데이트합니다.
// 바의 위치를 업데이트합니다.
if (rightPressed && paddle.x < canvas.width - paddle.width) {
paddle.x += 7; // 오른쪽 화살표 키가 눌려 있고 바가 화면을 벗어나지 않았을 경우, 바를 오른쪽으로 이동시킵니다.
} else if (leftPressed && paddle.x > 0) {
paddle.x -= 7; // 왼쪽 화살표 키가 눌려 있고 바가 화면을 벗어나지 않았을 경우, 바를 왼쪽으로 이동시킵니다.
}
requestAnimationFrame(draw); // 다음 프레임을 그립니다.
}
// 게임 시작 함수
function startGame() {
isGameOver = false; // 게임 오버 상태를 초기화합니다.
gameOverText.style.display = "none"; // 게임 오버 텍스트를 숨깁니다.
draw(); // 게임을 그립니다.
}
// 게임 종료 함수
function stopGame() {
isGameOver = true; // 게임 오버 상태를 설정합니다.
document.location.reload(); // 페이지를 새로고침하여 게임을 종료합니다.
}
// 게임 오버 처리 함수
function gameOver() {
isGameOver = true; // 게임 오버 상태를 설정합니다.
gameOverText.style.display = "block"; // 게임 오버 텍스트를 표시합니다.
}
- 캔버스와 캔버스 컨텍스트 설정
- const canvas = document.getElementById("gameCanvas");: HTML에서 id가 "gameCanvas"인 캔버스 요소를 가져옵니다.
- const ctx = canvas.getContext("2d");: 2D 렌더링 컨텍스트를 얻어옵니다.
- 게임 요소 초기화
- let score = 0;: 게임 점수를 저장하는 변수를 초기화합니다.
- let ball = { x: canvas.width / 2, y: canvas.height - 30, dx: 2, dy: -2, radius: 5, speed: 1 };: 공의 초기 위치, 이동 속도, 반지름, 속도 등을 설정합니다.
- let paddle = { height: 10, width: 50, x: (canvas.width - 50) / 2 };: 바의 초기 크기와 위치를 설정합니다.
- let rightPressed = false;: 오른쪽 화살표 키가 눌려 있는지 여부를 저장하는 변수를 초기화합니다.
- let leftPressed = false;: 왼쪽 화살표 키가 눌려 있는지 여부를 저장하는 변수를 초기화합니다.
- let isGameOver = false;: 게임이 종료되었는지 여부를 저장하는 변수를 초기화합니다.
- 벽돌 초기화
- 이중 배열 bricks를 사용하여 벽돌을 생성하고 초기 상태를 설정합니다. 각 벽돌의 색상과 포인트(점수), 상태 등을 무작위로 설정합니다.
- 이벤트 리스너 설정
- 사용자의 키보드 입력에 따라 keyDownHandler와 keyUpHandler 함수를 호출합니다. 이 함수들은 각각 키가 눌렸을 때와 떼졌을 때의 동작을 처리합니다.
- 시작 버튼과 종료 버튼에 클릭 이벤트 리스너를 추가하여 게임 시작 및 종료 동작을 처리합니다.
- 키보드 입력 핸들링
- keyDownHandler: 오른쪽 키와 왼쪽 키가 눌렸는지 여부를 설정합니다.
- keyUpHandler: 오른쪽 키와 왼쪽 키가 떼졌는지 여부를 설정합니다.
- 충돌 감지
- collisionDetection: 공과 벽돌 간의 충돌을 감지하고, 충돌한 벽돌을 제거하며 점수를 갱신합니다.
- updateBallSpeed: 점수에 따라 공의 속도를 업데이트합니다.
- 그리기 함수
- drawBall: 공을 그립니다.
- drawPaddle: 바를 그립니다.
- drawBricks: 벽돌을 그립니다.
- 게임 루프 및 게임 오버 처리
- draw: 게임을 그리는 메인 루프입니다. 공과 바의 이동, 벽돌 그리기, 충돌 감지 등을 수행합니다.
- startGame: 게임을 시작합니다. draw 함수를 호출하여 게임 루프를 실행합니다.
- stopGame: 게임을 종료하고 페이지를 새로고침합니다.
- gameOver: 게임 오버 상태를 설정하고 "GAME OVER" 메시지를 화면에 표시합니다.
이상으로 이번 글에서는 벽돌깨기 게임을 자바스크립트, HTML, CSS를 이용하여 제작해 보았습니다.
이 게임을 만들면서 JavaScript의 객체지향 프로그래밍과 캔버스 다루는 법을 익혔어요. 또한 게임 로직과 그래픽을 구현하는 과정에서 창의성도 발휘할 수 있었죠. HTML과 CSS로 사용자 인터페이스를 디자인하고, JavaScript로 동적인 동작을 부여하면서 웹 개발의 기본기도 다질 수 있었습니다.
이번 글을 통해 벽돌깨기 게임을 만들어보면서 코드 구현 과정과 함께, 게임 개발에 대한 즐거움과 만족감을 경험할 수 있었습니다. 프로그래밍과 게임 개발에 관심 있는 분들에게 유익한 정보가 되었으면 좋겠네요. 앞으로도 재미있는 게임 개발 이야기를 더 들려드리겠습니다.
'vita_Programing' 카테고리의 다른 글
Javascript 두더지게임 소스 (1) | 2024.05.30 |
---|---|
MSSQL 문자열 자르기 함수 LEFT RIGHT SUBSTRING (2) | 2024.05.30 |
MSSQL case when then else 문 예제와 활용 (0) | 2024.05.28 |
Javascript 엘리베이터 동작 시뮬레이션 (0) | 2024.05.28 |
MSSQL 개별 테이블 사용 용량 확인하는 방법 (0) | 2024.05.27 |