canvas에서 충돌 애니메이션을 구현하는 방법을 간단하게 알아봅시다.
아래 글을 이해하는데는 벡터 관련 지식이 약간 필요합니다.
핵심 개념은 단 세 가지뿐입니다.
- “좌표” 2. “속도(움직임)” 3. “거울에 비치듯 방향을 뒤집기”
1. 좌표 — 종이에 그린 그래프랑 똑같다
용어 뜻 예시
x | 왼쪽 ↔ 오른쪽 거리 | x = 0 → 맨 왼쪽, x = 500 → 맨 오른쪽 |
y | 위 ↔ 아래 거리 | y = 0 → 맨 위, y = 500 → 맨 아래 |
화면을 가로 500 × 세로 500 칸짜리 눈금종이라 생각하면 됩니다.
빨간 공 하나를 “좌표 (x, y)” 두 숫자로 위치시킵니다.
2. 속도 = “한 프레임(0.016초쯤)마다 몇 칸 움직이느냐”
let vx = 3; // x축으로 한 번에 +3칸
let vy = 2; // y축으로 한 번에 +2칸
- 속도 벡터 (vx, vy) : “오른쪽으로 3칸, 아래로 2칸”이라는 화살표
- 움직이기 : x += vx; y += vy;
- → 숫자를 더하기만 하면 새 위치가 나옵니다.
👉 ‘벡터’란 단어를 어려워하지 마세요.
그냥 “Δx(+3), Δy(+2)”라는 두 개의 숫자를 한 쌍으로 들고 다닌다는 뜻뿐입니다.
3. 충돌 감지 — “부딪쳤다”를 어떻게 알까?
3‑1. 벽(사각형)과 부딪힘
- 왼쪽 벽 : 공의 왼쪽 끝 x - R 이 0보다 작아졌다면 → “벽을 뚫었다”는 뜻
- 오른쪽 벽 : x + R 이 500보다 크면 뚫음
- 위·아래도 같은 논리로 구현
if (x - R <= 0 && vx < 0) vx = -vx; // 왼쪽 벽
- vx < 0 여야만 뒤집는 이유: 이미 오른쪽(+)으로 가고 있는 상황이라면 굳이 뒤집을 필요 없죠.
3-2. 동글동글 장애물(원)과 부딪힘
- 두 원 중심 사이의 거리가
- 공 반지름 + 장애물 반지름 보다 작으면 겹쳤다(충돌)
const dx = x - obs.x;
const dy = y - obs.y;
const dist = Math.hypot(dx, dy); // √(dx² + dy²)
if (dist < R + obs.r) { … }
** Math.hypot(a,b) 는 “피타고라스” 공식(√(a²+b²))을 간단히 써 주는 함수
4 튕겨 나가기 — 거울에 비친 화살표처럼 “반사”하기
4‑1. 벽은 간단 — 한 축만 부호 반전
- 왼·오른쪽 벽 → x 방향만 뒤집기 → vx = ‑vx
- 위·아래 벽 → y 방향만 뒤집기 → vy = ‑vy
결과 : “↘” 로 가던 화살표가 벽에 부딪히면 “↙”로 바뀝니다.
4‑2. 둥근 벽(원) — 법선(normal)이라는 “정면 방향”을 이용
- 법선 : 공→장애물로 그린 선을 1칸 길이로 만든 화살표
- → (nx, ny) = (dx/dist, dy/dist)
- 정의: 법선 벡터는 “충돌 지점에서 표면에 수직으로 뻗은 단위 벡터”를 말해요.
- 충돌 예제에서는 공→장애물 중심을 잇는 방향으로 벡터를 구한 뒤, 이걸 길이 1로 정규화(normalize) 한 것이 법선입니다.
- 반사 공식
- v·n (v dot n) = vx·nx + vy·ny
→ 스칼라곱: 그냥 두 숫자씩 곱해서 더한 값입니다
- v·n (v dot n) = vx·nx + vy·ny
3. 코드
const dot = vx*nx + vy*ny;
vx = vx - 2 * dot * nx;
vy = vy - 2 * dot * ny;
왜 이렇게 하면 “거울 반사”가 될까?
화살표(속도)를 “정면 성분”과 “옆으로 스치는 성분”으로 쪼갠 뒤,
정면 성분만 → 반대 방향으로 두 배 돌려서 빼 버린다고 생각하면 됩니다.
한글 말장난보다, 거울에 비친 모습을 빼서 더한다는 직관이 더 쉬워요!
5 끼임 방지 — 살짝 밀어내기
충돌 직후, 공이 이미 장애물 안쪽에 일부 들어가 있을 수 있습니다.
겹친 거리(overlap)만큼 한 발짝 밀어내기
✋ 정리
궁금증 아주 쉬운 답
“벡터?” | 숫자 두 개를 한 묶음(↗ 화살표)으로 본다 |
“dot(스칼라곱)?” | a₁·b₁ + a₂·b₂ — 곱하고 더하기뿐 |
“왜 반사가 되죠?” | 화살표를 거울에 비춘 뒤 그 방향으로 바꿔 꽂는 것 |
“무슨 고급 수학 쓰나요?” | 피타고라스(√)와 곱셈·뺄셈 끝! |
결국 우리가 한 일
① 위치(x, y)를 더하기로 옮겼다 → ② 부딪혔는지 간단한 비교·√로 확인 →
③ 맞으면 화살표(vx, vy)를 뒤집거나 반사 공식으로 바꿨다.
코드 예시
아래 링크에서 확인 가능합니다.
https://codesandbox.io/p/devbox/kc8674
reference
HTML5 Canvas: Native Interactivity and Animation for the Web 에서 일부 내용 발췌
'Front-end > 애니메이션 & 인터렉션' 카테고리의 다른 글
figma의 svg 파일을 추출해 인터렉션 구현하기 (0) | 2025.04.26 |
---|---|
브라우저 부드러운 인터렉션 구현(easing) (0) | 2025.04.22 |