웹 브라우저에서 별도의 플러그인이나 무거운 게임 엔진 없이도 간단한 물리 법칙과 2D 캔버스를 이용해 인터랙티브한 새총 발사 게임을 구현할 수 있습니다. 마우스 드래그 앤 드롭으로 새를 당겨 날리고, 장애물과 충돌했을 때 반응하는 기본적인 게임 루프를 단계별로 살펴보겠습니다.
게임 인터페이스와 캔버스 구조 설계
게임 화면은 HTML5 Canvas 요소를 기반으로 구성합니다. 사용자가 마우스로 새를 당기는 인터랙션을 감지하기 위해 캔버스 엘리먼트에 이벤트 리스너를 연결해야 합니다.
index.htmlHTML
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>JS 앵그리버드 미니</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="game-container">
<h1>바닐라 JS 앵그리버드</h1>
<canvas id="gameCanvas" width="800" height="400"></canvas>
<p>새를 마우스로 클릭해 당긴 뒤 놓아두면 발사됩니다.</p>
</div>
<script src="app.js"></script>
</body>
</html>
물리 연산과 새총 발사 로직 구현
관련 글: 바닐라 자바스크립트 활용
새의 위치, 속도, 중력 가속도, 그리고 마우스 드래그 거리에 비례하는 탄성력을 계산하여 비행 궤적을 만듭니다. 발사 후 장애물 박스와 충돌하는 좌표를 매 프레임마다 판정합니다.
app.jsJAVASCRIPT
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
let bird = { x: 150, y: 300, radius: 15, vx: 0, vy: 0, isDragged: false, isLaunched: false };
const gravity = 0.4;
const slingPosition = { x: 150, y: 300 };
let target = { x: 650, y: 320, width: 40, height: 60, hit: false };
canvas.addEventListener('mousedown', (e) => {
const rect = canvas.getBoundingClientRect();
const mouseX = e.clientX - rect.left;
const mouseY = e.clientY - rect.top;
const dist = Math.hypot(mouseX - bird.x, mouseY - bird.y);
if (dist < 30 && !bird.isLaunched) {
bird.isDragged = true;
}
});
canvas.addEventListener('mousemove', (e) => {
if (!bird.isDragged) return;
const rect = canvas.getBoundingClientRect();
bird.x = e.clientX - rect.left;
bird.y = e.clientY - rect.top;
});
canvas.addEventListener('mouseup', () => {
if (!bird.isDragged) return;
bird.isDragged = false;
bird.isLaunched = true;
const dx = slingPosition.x - bird.x;
const dy = slingPosition.y - bird.y;
bird.vx = dx * 0.15;
bird.vy = dy * 0.15;
});
function update() {
if (bird.isLaunched) {
bird.vy += gravity;
bird.x += bird.vx;
bird.y += bird.vy;
if (
bird.x > target.x && bird.x < target.x + target.width &&
bird.y > target.y && bird.y < target.y + target.height
) {
target.hit = true;
}
if (bird.y > canvas.height || bird.x > canvas.width) {
resetGame();
}
}
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.arc(slingPosition.x, slingPosition.y, 5, 0, Math.PI * 2);
ctx.fillStyle = '#333';
ctx.fill();
if (!target.hit) {
ctx.fillStyle = '#8B4513';
ctx.fillRect(target.x, target.y, target.width, target.height);
}
ctx.beginPath();
ctx.arc(bird.x, bird.y, bird.radius, 0, Math.PI * 2);
ctx.fillStyle = '#ff4757';
ctx.fill();
ctx.closePath();
}
function resetGame() {
bird.x = slingPosition.x;
bird.y = slingPosition.y;
bird.vx = 0;
bird.vy = 0;
bird.isLaunched = false;
bird.isDragged = false;
target.hit = false;
}
function loop() {
update();
draw();
requestAnimationFrame(loop);
}
loop();
스타일 시트 구성
캔버스가 화면 중앙에 정렬되도록 간단한 플렉스박스 레이아웃과 배경 색상을 적용합니다.
style.cssCSS
body {
margin: 0;
padding: 0;
background-color: #f1f2f6;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.game-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
}
h1 {
margin-bottom: 10px;
color: #2f3640;
}
canvas {
background: linear-gradient(to bottom, #70c5ce, #ffffff);
border: 4px solid #2f3640;
border-radius: 8px;
box-shadow: 0 8px 16px rgba(0,0,0,0.15);
}
p {
margin-top: 15px;
color: #57606f;
}
구현 시 주의할 점
관련 글: 미니 게임 제작
- 마우스 좌표를 계산할 때 캔버스의 고정된 뷰포트 크기와 내부 해상도 비율이 일치해야 오차가 줄어듭니다.
- 새가 화면 밖으로 완전히 벗어났을 때 초기화 함수를 호출하지 않으면 메모리가 낭비되거나 무한 비행 상태에 빠질 수 있습니다.
- 충돌 판정 박스의 크기가 너무 작으면 정밀한 타격감이 떨어지므로 적절한 면적을 유지해야 합니다.
이미지 파일 없이 도형만으로 구현해도 게임성이 유지되나요?
외부 이미지 에셋 없이 HTML5 Canvas 기본 도형 메서드만 활용하므로 브라우저 호환성이 높고 별도의 파일 로딩 대기 시간이 없습니다.
완성 코드 예제
마치며
순수 자바스크립트와 HTML5 캔버스를 조합하면 복잡한 외부 엔진 없이도 간단한 물리 법칙을 적용한 아케이드 게임을 충분히 제작할 수 있습니다.
기본적인 궤적 계산과 충돌 판정 구조를 바탕으로 장애물 개수를 늘리거나 다양한 새의 특성을 추가해 나만의 게임으로 확장해 보시기 바랍니다.
