WEEK 06 · 2026
맞히면 죽는다
점수가 쌓인다
지금까지 미사일과 적이 따로 놀았다. 이번 주는 둘이 만나는 순간 — 충돌 감지(AABB). 적 3종류 등장, 미사일 맞으면 적이 죽고 점수 +. 비행기에도 HP 바가 생깁니다.
Duration90 minutes
ToolClaude Code
Output충돌 + 점수 + HP
Phase비행기 게임 (3/4)
02 / Learning Goals⏱ 5분
오늘의 학습 목표
1
적 3종류 (빠른/느린/지그재그)
type 속성으로 적을 분류 — 빠른 적·느린 적·지그재그로 움직이는 적. switch문 첫 등장.
2
충돌 감지 (AABB)
미사일이 적에 닿았는지 어떻게 알까? 사각형 4개 좌표만 비교하면 끝.
3
점수 시스템
적 종류에 따라 점수 다르게 (10/20/50점). 화면 위에 큰 글씨로 표시.
4
HP 시스템 + 게임오버
비행기 HP 3개로 시작. 적과 부딪히면 -1. 0이 되면 게임오버 화면.
03 / Concept · AABB⏱ 10분
충돌 감지 = 사각형 4개 비교
게임의 가장 큰 비밀 — 두 사각형이 겹쳤는지 어떻게 알까? 답은 단순해. 한쪽이 다른 쪽보다 완전히 왼쪽·오른쪽·위·아래에 있으면 안 겹친 것. 그 반대만 충돌.
◆
미사일
x, y, w, h
(왼쪽위 + 크기)
(왼쪽위 + 크기)
vs
▼
적
x, y, w, h
(왼쪽위 + 크기)
(왼쪽위 + 크기)
→
💥
충돌?
4개 조건
모두 만족 시 YES
모두 만족 시 YES
AABB 공식 (외울 필요 X, 한번 보면 끝)
→ 네 조건 모두 true 면 두 사각형이 겹친 것. 마리오·앵그리버드·서바이버 다 이 공식.
a.x < b.x+b.w && a.x+a.w > b.x && a.y < b.y+b.h && a.y+a.h > b.y
→ 네 조건 모두 true 면 두 사각형이 겹친 것. 마리오·앵그리버드·서바이버 다 이 공식.
04 / Enemy Design⏱ 10분
적 3종류 — type로 분류
SLOW · 10점
🟢 초록 큰 사각형
(40x40px)
속도: 1
점수: 10점
크기 커서 잘 맞음
(40x40px)
속도: 1
점수: 10점
크기 커서 잘 맞음
FAST · 20점
🟡 노란 작은 사각형
(25x25px)
속도: 4
점수: 20점
빨라서 어려움
(25x25px)
속도: 4
점수: 20점
빨라서 어려움
ZIGZAG · 50점
🔴 빨간 작은 사각형
(30x30px)
좌우 sin 흔들림
점수: 50점
가장 까다로움
(30x30px)
좌우 sin 흔들림
점수: 50점
가장 까다로움
💡 핵심: 적 객체에
type: 'slow' | 'fast' | 'zigzag' 속성을 추가하고, 매 프레임 switch로 분기하면 끝.
모든 게임 디자인의 시작은 "다른 타입에 다른 행동".
05 / Live Build · Step 1⏱ 12분
Step 1 — 적 3종류 구현
지난주 단일 적 코드를 3종류로 분기. 스폰 시 랜덤하게 골라서 다른 색·속도·움직임.
1
Claude에게 보낼 프롬프트
"지난주 적 스폰 코드를 3종류로 분기해줘.
- slow: 초록색, 40x40, 속도 1, 직선 하강
- fast: 노란색, 25x25, 속도 4, 직선 하강
- zigzag: 빨간색, 30x30, 속도 2, x가 sin(y*0.05)*60으로 좌우 흔들림
- 스폰할 때 랜덤하게 셋 중 하나 선택
- 각 적에 hp, points, type 속성 추가"
✓
핵심 코드 — type 분기
const ENEMY_TYPES = {
slow: { w: 40, h: 40, speed: 1, points: 10, hp: 1, color: '#4A7A68' },
fast: { w: 25, h: 25, speed: 4, points: 20, hp: 1, color: '#C4935A' },
zigzag: { w: 30, h: 30, speed: 2, points: 50, hp: 1, color: '#B84A4A' }
};
function spawnEnemy() {
const types = ['slow', 'fast', 'zigzag'];
const t = types[Math.floor(Math.random() * 3)];
enemies.push({ ...ENEMY_TYPES[t], type: t, x0: Math.random()*canvas.width, x: 0, y: -40 });
}
// 매 프레임 — type별 다른 움직임
enemies.forEach(en => {
en.y += en.speed;
if (en.type === 'zigzag') en.x = en.x0 + Math.sin(en.y * 0.05) * 60;
else en.x = en.x0;
});
🎙
객체 안에 객체
"ENEMY_TYPES는 '적 종류 설명서'예요. 새 타입 추가하고 싶으면 여기에 한 줄만 더하면 돼. 보스도, UFO도, 미니언도 다 이렇게 만드는 거예요."
05 / Live Build · Step 2⏱ 15분
Step 2 — 충돌 감지 함수
미사일 모두 × 적 모두 → 이중 forEach로 비교. AABB 공식 한 번 짜면 평생 쓴다.
2
Claude에게 보낼 프롬프트
"미사일이 적에 닿으면 둘 다 사라지게 해줘.
- 매 프레임 missiles × enemies 이중 루프
- AABB 공식으로 충돌 확인
- 충돌하면 둘 다 .dead = true로 표시
- 매 프레임 마지막에 dead가 true인 것 filter로 제거
- 충돌 함수는 따로 isColliding(a, b)로 빼서 재사용 가능하게"
✓
핵심 코드 — AABB 함수
function isColliding(a, b) {
return a.x < b.x + b.w &&
a.x + a.w > b.x &&
a.y < b.y + b.h &&
a.y + a.h > b.y;
}
// 매 프레임 — 모든 미사일 × 모든 적
missiles.forEach(m => {
enemies.forEach(en => {
if (isColliding(m, en)) {
m.dead = true;
en.dead = true;
}
});
});
// 죽은 것들 제거
missiles = missiles.filter(m => !m.dead && m.y > 0);
enemies = enemies.filter(en => !en.dead && en.y < canvas.height);
🎙
왜 따로 함수로 빼나?
"isColliding 함수는 이제 비행기 vs 적 충돌에도 쓸 거예요. 한 번 만들어두면 모든 충돌에 다 써먹어요. 이게 함수의 존재 이유 — 재사용."
05 / Live Build · Step 3⏱ 12분
Step 3 — 점수 시스템 + UI
적 죽일 때마다 점수 +. 화면 좌상단에 큰 글씨로 표시. 적 종류별로 점수 다르게.
3
Claude에게 보낼 프롬프트
"점수 시스템 추가해줘.
- let score = 0 전역 변수
- 충돌 시 score += enemy.points (slow 10, fast 20, zigzag 50)
- 화면 좌상단에 큰 글씨(36px)로 'SCORE 0' 표시
- 폰트는 픽셀 게임 느낌나는 monospace 굵게
- 최고점도 함께 표시 (localStorage 사용)"
✓
핵심 코드 — 점수 + 그리기
let score = 0;
let hiScore = parseInt(localStorage.getItem('hiScore') || '0');
// 충돌 시
if (isColliding(m, en)) {
score += en.points; // 적 종류별 점수
if (score > hiScore) {
hiScore = score;
localStorage.setItem('hiScore', score);
}
m.dead = true;
en.dead = true;
}
// 매 프레임 그리기 (canvas)
ctx.fillStyle = '#3D5A80';
ctx.font = 'bold 36px monospace';
ctx.fillText(`SCORE ${score}`, 20, 50);
ctx.font = '14px monospace';
ctx.fillText(`BEST ${hiScore}`, 20, 75);
🎙
localStorage
"localStorage는 브라우저에 저장하는 작은 메모장. 여기 적어두면 페이지 새로고침해도 안 사라져요. 그래서 최고점이 평생 남아요."
05 / Live Build · Step 4⏱ 12분
Step 4 — 비행기 HP 바
적과 비행기가 부딪히면 HP -1. HP는 변수, 바는 fillRect 2개로 그린다. 검정 배경 + 빨간 채움.
4
Claude에게 보낼 프롬프트
"비행기 HP 시스템 추가해줘.
- let hp = 3, maxHp = 3 전역
- 매 프레임 비행기 × 적 충돌 검사 → 충돌 시 hp--, 적 사라짐
- 화면 우상단에 HP 바 그리기 (200x20px)
· 배경: 어두운 회색
· 채움: 현재 HP에 비례한 빨간 사각형
· 텍스트: HP 3/3
- 무적 시간 1초 — 한번 맞으면 깜빡거리며 무적"
✓
핵심 코드 — HP + 무적 + UI
let hp = 3, maxHp = 3, invincibleUntil = 0;
// 매 프레임 충돌 검사
enemies.forEach(en => {
if (isColliding(plane, en) && Date.now() > invincibleUntil) {
hp--;
en.dead = true;
invincibleUntil = Date.now() + 1000; // 1초 무적
}
});
// 무적이면 깜빡임 (50% 시간만 그림)
const blink = Date.now() < invincibleUntil && Math.floor(Date.now()/100) % 2;
if (!blink) drawPlane();
// HP 바 그리기 우상단
const barX = canvas.width - 220, barY = 20;
ctx.fillStyle = '#333';
ctx.fillRect(barX, barY, 200, 20); // 배경
ctx.fillStyle = '#B84A4A';
ctx.fillRect(barX, barY, 200 * (hp/maxHp), 20); // 비율 채움
ctx.fillStyle = '#fff';
ctx.font = 'bold 12px monospace';
ctx.fillText(`HP ${hp}/${maxHp}`, barX + 80, barY + 14);
🎙
무적 시간이 왜 필요한가
"무적 안 만들면 적 한 마리에 닿는 순간 HP 3이 한 프레임에 다 까져요. 0.016초 만에 게임 끝. 모든 액션 게임이 'i-frame'이라는 무적 시간을 둬요."
05 / Live Build · Step 5⏱ 9분
Step 5 — 게임오버 화면
HP가 0이 되는 순간 — 전체 화면 어둡게, 가운데에 "GAME OVER" + 최종 점수 + 재시작 버튼.
5
Claude에게 보낼 프롬프트
"게임오버 시스템 추가해줘.
- hp가 0 이하가 되면 gameOver = true
- gameOver이면 게임 루프 멈춤 (update/spawn 안 함)
- 화면 위에 반투명 검정 오버레이
- 가운데에 'GAME OVER' (대형 빨간 글씨)
- 그 밑에 'SCORE: 1240 / BEST: 2100'
- '스페이스를 누르면 재시작' 안내
- 스페이스 누르면 score=0, hp=3, 배열 비우고 다시 시작"
✓
핵심 코드 — 게임오버 + 재시작
let gameOver = false;
function update() {
if (hp <= 0) gameOver = true;
if (gameOver) return; // 게임 멈춤
// ... 평소 게임 로직
}
function draw() {
// ... 평소 그리기
if (gameOver) {
ctx.fillStyle = 'rgba(0,0,0,0.75)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#B84A4A';
ctx.font = 'bold 72px monospace';
ctx.textAlign = 'center';
ctx.fillText('GAME OVER', canvas.width/2, canvas.height/2 - 40);
ctx.fillStyle = '#fff';
ctx.font = '20px monospace';
ctx.fillText(`SCORE: ${score} / BEST: ${hiScore}`, canvas.width/2, canvas.height/2 + 20);
ctx.fillText('Press SPACE to restart', canvas.width/2, canvas.height/2 + 60);
}
}
// 재시작 키
document.addEventListener('keydown', e => {
if (gameOver && e.code === 'Space') {
score = 0; hp = 3; missiles = []; enemies = []; gameOver = false;
}
});
🎙
오늘의 마무리
"이제 진짜 게임이 됐어요. 점수가 있고, HP가 있고, 죽으면 다시 시작. 다음 주는 마지막 — 보스전과 BGM. 시간을 음악과 함께 마무리해요."
10 / Checkpoint⏱ 5분
여기까지 됐나? 확인하기
✅잘 됐다면
- 적 3종류 (초록·노랑·빨강) 다 등장
- 빨간 적은 좌우로 흔들면서 내려옴
- 미사일 맞으면 적이 사라지고 점수 +
- 비행기 HP 바 우상단 표시
- HP 0이 되면 GAME OVER + 재시작 가능
⚠️자주 막히는 부분
- 미사일 통과 → isColliding 4개 조건 부등호 방향 확인
- 지그재그가 직선 → Math.sin(en.y * 0.05) 빠짐
- HP 한순간에 0 → invincibleUntil 무적시간 빠짐
- 죽어도 계속 진행 → update에 if(gameOver) return 빠짐
- 재시작 안 됨 → missiles=[], enemies=[] 초기화 빠짐
11 / Challenge⏱ 10분
더 해보고 싶다면 도전
💥
충돌 시 파티클 폭발 효과 — 작은 점들이 사방으로 튀게. 0.5초 후 사라짐.
난이도 ★
🛡
적이 2발 맞아야 죽는 종류 추가 — type "tank", HP 2, 노란 큰 사각형, 점수 100점.
난이도 ★★
⭐
점수에 따라 비행기 모양 변신 — 100점에 노란 테두리, 500점에 무지개 테두리.
난이도 ★★★
12 / Student Gallery⏱ 7분
작년 친구들 작품
💥
결과물 1
🎯
결과물 2
❤️
결과물 3
⚡
결과물 4
🏆
결과물 5
💀
결과물 6
📌 첫 학기 종료 후 학생 결과물 사진을 여기에 채울 예정입니다.
13 / Next Week⏱ 3분
다음 시간 예고
WEEK 07 · COMING UP
보스전과 게임 BGM —
비행기 게임 완성
드디어 마지막 — 100점 도달하면 보스 등장. 보스는 좌우로 움직이면서 탄막을 쏘고, HP 바도 있습니다. 그리고 Suno로 8비트 BGM을 직접 만들어 게임에 깐다. 4주짜리 작품 완성.
PREPARE · 준비물
- 이번 주 완성한 게임 (충돌·점수·HP·게임오버까지)
- 좋아하는 게임 보스전 영상 1분 (참고용)
- "어떤 분위기 BGM 깔고 싶은지" 한 단어 (긴장? 신남? 잔잔?)