728x90
안녕하세요.
오늘은 자바스크립트와 HTML5 '<canvas>'를 이용해서 아래 동영상처럼 간단한 벽돌깨기 게임을 만들어보겠습니다.
이 시리즈는 총 3편으로 구성되고, 첫번째 편에서는 캔버스에 공, 벽돌, 패들을 그리는 방법을 다룹니다.
그럼 시작해보겠습니다.
1. Index.html 파일 생성
먼저 HTML 파일을 설정하여 게임의 기본 구조를 만듭니다.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>벽돌 깨기 게임</title>
<style>
canvas {
background: #eee;
display: block;
margin: 0 auto;
border: 1px solid #000;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="480" height="320"></canvas>
<script src="game.js"></script>
</body>
</html>
2. 공 그리기
이제 'game.js' 파일을 만들고, 캔버스에 공을 그리려보겠습니다.
// 캔버스와 2D 컨텍스트 가져오기
const canvas = document.getElementById("myCanvas");
const ctx = canvas.getContext("2d");
// 공의 위치와 반지름 설정
let x = canvas.width / 2; // 공의 최초 위치 x 좌표
let y = canvas.height - 30; // 공의 최초 위치 y 좌표
let dx = 2; // 공의 x축 이동 속도
let dy = -2; // 공의 y축 이동 속도
let ballRadius = 10; // 공의 반지름 크기
// 공 그리기 함수
function drawBall() {
ctx.beginPath(); // 새로운 경로 시작
ctx.arc(x, y, ballRadius, 0, Math.PI * 2); // 원 그리기
ctx.fillStyle = "#0095DD"; // 공의 색상
ctx.fill(); // 원 채우기
ctx.closePath(); // 경로 닫기
}
// 애니메이션 프레임마다 실행되는 함수
function draw() {
// 이전 프레임을 지우고, 새로운 프레임을 그리기 위해 준비
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBall(); // 공 그리기
// 공의 위치 업데이트
x += dx;
y += dy;
requestAnimationFrame(draw); // 다음 프레임 요청
}
draw(); // 첫 번째 프레임 실행
위 코드를 실행하면 다음 영상처럼 화면에 공이 매 프레임마다 이동하는 것을 확인할 수 있습니다.
3. 패들 추가하기
다음으로 공을 튕겨내기 위한 패들을 캔버스 하단에 추가해보겠습니다.
이 패들은 마우스의 움직임을 따라 좌우로 이동해야 합니다. game.js에 아래 내용을 추가합니다.
// 패들 설정
let paddleHeight = 10; // 패들 세로 길이
let paddleWidth = 75; // 패들 가로 길이
let paddleX = (canvas.width - paddleWidth) / 2; // 패들의 초기 위치
// 패들 그리기 함수
function drawPaddle() {
ctx.beginPath(); // 경로 그리기 시작
ctx.rect(paddleX, canvas.height - paddleHeight, paddleWidth, paddleHeight); // 사각형 그리기
ctx.fillStyle = "#0095DD"; // 패들 색상
ctx.fill(); // 내부 채우기
ctx.closePath(); // 경로 닫기
}
// 마우스 이동에 대한 이벤트 리스너 설정
document.addEventListener("mousemove", mouseMoveHandler, false);
// 마우스 이동시 패들이 마우스 위치를 따라가도록 구현
function mouseMoveHandler(e) {
let relativeX = e.clientX - canvas.offsetLeft;
if(relativeX > 0 && relativeX < canvas.width) {
paddleX = relativeX - paddleWidth / 2;
}
}
그리고 draw() 함수에 아래 처럼 패들 그리기 함수를 추가합니다.
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBall();
drawPaddle(); // 패들 그리기 추가
x += dx;
y += dy;
requestAnimationFrame(draw);
}
여기까지 진행하면 다음과 같이 패들이 추가된 것을 확인할 수 있습니다.
4. 벽돌 추가하기
이제 다음 코드를 game.js에 추가해서 캔버스 상단에 벽돌을 그려보겠습니다.
// 벽돌 설정
let brickRowCount = 3; // 벽돌 열 수
let brickColumnCount = 5; // 벽돌 행 수
let brickWidth = 75; // 벽돌 가로 길이
let brickHeight = 20; // 벽돌 세로 길이
let brickPadding = 10; // 벽돌 간 간격
let brickOffsetTop = 30; // 벽돌과 캔버스 위쪽 가장자리 사이의 간격
let brickOffsetLeft = 30; // 벽돌과 캔버스 왼쪽 가장자리 사이의 간격
/** 현재 화면에 존재하는 벽돌의 상태를 추적하고 관리하기 위해 2차원 배열 사용
* bricks[c][r] 형태
* x, y : 벽돌의 위치를 지정하기 위한 속성, 초기에 0으로 설정하고, 이후 실제 위치를 계산할 때 업데이트.
* status : 벽돌이 화면에 표시되어 있는지(1), 깨져서 사라졌는지(0)을 나타냄
*/
let bricks = [];
for(let c = 0; c < brickColumnCount; c++) {
bricks[c] = []; // 열을 위한 빈 배열 생성
for(let r = 0; r < brickRowCount; r++) {
bricks[c][r] = { x: 0, y: 0, status: 1 }; // 벽돌 상태 초기화
}
}
// 벽돌 그리기 함수 - 2차원 배열을 순회하면서 status가 1인 벽돌만 화면에 그린다.
function drawBricks() {
for(let c = 0; c < brickColumnCount; c++) {
for(let r = 0; r < brickRowCount; r++) {
if(bricks[c][r].status == 1) {
let brickX = (c * (brickWidth + brickPadding)) + brickOffsetLeft;
let brickY = (r * (brickHeight + brickPadding)) + brickOffsetTop;
bricks[c][r].x = brickX;
bricks[c][r].y = brickY;
ctx.beginPath();
ctx.rect(brickX, brickY, brickWidth, brickHeight);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
}
}
}
그리고 draw() 함수에 아래 처럼 벽돌 그리기 함수를 추가합니다.
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBricks(); // 벽돌 그리기 추가
drawBall();
drawPaddle();
x += dx;
y += dy;
requestAnimationFrame(draw);
}
여기까지 잘 진행되었다면, 다음처럼 화면에 공, 패들, 벽돌이 그려지게 됩니다.
5. 현재까지 전체 코드
index.html
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>벽돌 깨기 게임</title>
<style>
canvas {
background: #eee;
display: block;
margin: 0 auto;
border: 1px solid #000;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="480" height="320"></canvas>
<script src="game.js"></script>
</body>
</html>
game.js
// 캔버스와 2D 컨텍스트 가져오기
const canvas = document.getElementById("myCanvas");
const ctx = canvas.getContext("2d");
// 공의 위치와 반지름 설정
let x = canvas.width / 2; // 공의 최초 위치 x 좌표
let y = canvas.height - 30; // 공의 최초 위치 y 좌표
let dx = 2; // 공의 x축 이동 속도
let dy = -2; // 공의 y축 이동 속도
let ballRadius = 10; // 공의 반지름 크기
// 패들 설정
let paddleHeight = 10; // 패들 세로 길이
let paddleWidth = 75; // 패들 가로 길이
let paddleX = (canvas.width - paddleWidth) / 2; // 패들의 초기 위치
// 벽돌 설정
let brickRowCount = 3; // 벽돌 열 수
let brickColumnCount = 5; // 벽돌 행 수
let brickWidth = 75; // 벽돌 가로 길이
let brickHeight = 20; // 벽돌 세로 길이
let brickPadding = 10; // 벽돌 간 간격
let brickOffsetTop = 30; // 벽돌과 캔버스 위쪽 가장자리 사이의 간격
let brickOffsetLeft = 30; // 벽돌과 캔버스 왼쪽 가장자리 사이의 간격
/** 현재 화면에 존재하는 벽돌의 상태를 추적하고 관리하기 위해 2차원 배열 사용
* bricks[c][r] 형태
* x, y : 벽돌의 위치를 지정하기 위한 속성, 초기에 0으로 설정하고, 이후 실제 위치를 계산할 때 업데이트.
* status : 벽돌이 화면에 표시되어 있는지(1), 깨져서 사라졌는지(0)을 나타냄
*/
let bricks = [];
for(let c = 0; c < brickColumnCount; c++) {
bricks[c] = []; // 열을 위한 빈 배열 생성
for(let r = 0; r < brickRowCount; r++) {
bricks[c][r] = { x: 0, y: 0, status: 1 }; // 벽돌 상태 초기화
}
}
// 공 그리기 함수
function drawBall() {
ctx.beginPath(); // 새로운 경로 시작
ctx.arc(x, y, ballRadius, 0, Math.PI * 2); // 원 그리기
ctx.fillStyle = "#0095DD"; // 공의 색상
ctx.fill(); // 원 채우기
ctx.closePath(); // 경로 닫기
}
// 패들 그리기 함수
function drawPaddle() {
ctx.beginPath(); // 경로 그리기 시작
ctx.rect(paddleX, canvas.height - paddleHeight, paddleWidth, paddleHeight); // 사각형 그리기
ctx.fillStyle = "#0095DD"; // 패들 색상
ctx.fill(); // 내부 채우기
ctx.closePath(); // 경로 닫기
}
// 벽돌 그리기 함수 - 2차원 배열을 순회하면서 status가 1인 벽돌만 화면에 그린다.
function drawBricks() {
for(let c = 0; c < brickColumnCount; c++) {
for(let r = 0; r < brickRowCount; r++) {
if(bricks[c][r].status == 1) {
let brickX = (c * (brickWidth + brickPadding)) + brickOffsetLeft;
let brickY = (r * (brickHeight + brickPadding)) + brickOffsetTop;
bricks[c][r].x = brickX;
bricks[c][r].y = brickY;
ctx.beginPath();
ctx.rect(brickX, brickY, brickWidth, brickHeight);
ctx.fillStyle = "#0095DD";
ctx.fill();
ctx.closePath();
}
}
}
}
// 마우스 이동에 대한 이벤트 리스너 설정
document.addEventListener("mousemove", mouseMoveHandler, false);
// 마우스 이동시 패들이 마우스 위치를 따라가도록 구현
function mouseMoveHandler(e) {
let relativeX = e.clientX - canvas.offsetLeft;
if(relativeX > 0 && relativeX < canvas.width) {
paddleX = relativeX - paddleWidth / 2;
}
}
// 애니메이션 프레임마다 실행되는 함수
function draw() {
// 이전 프레임을 지우고, 새로운 프레임을 그리기 위해 준비
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBricks(); // 벽돌 그리기
drawBall(); // 공 그리기
drawPaddle(); // 패들 그리기
// 공의 위치 업데이트
x += dx;
y += dy;
requestAnimationFrame(draw); // 다음 프레임 요청
}
draw(); // 첫 번째 프레임 실행
오늘은 캔버스에 필요한 도형을 그리는 방법을 알아보았습니다.
다음 시간에는 패들로 공을 튕겨서 벽돌을 깨트리는 것을 구현해보겠습니다.
'JavaScript' 카테고리의 다른 글
[JavaScript] 벽돌깨기 게임 만들기 (2) 공 튕기기 (0) | 2024.08.18 |
---|---|
[JavaScript] 사칙연산 계산기 만들기 (0) | 2024.08.16 |
[JavaScript] 드래그 앤 드랍으로 이미지 순서 변경하기 (0) | 2024.08.08 |
[JavaScript] 마우스 드래그로 캔버스에 네모와 동그라미 그리기 (0) | 2024.08.06 |
[JavaScript] 다크 모드 토글 기능 구현 (0) | 2024.07.02 |