반응형

 

canvas에서 충돌 애니메이션을 구현하는 방법을 간단하게 알아봅시다. 

아래 글을 이해하는데는 벡터 관련 지식이 약간 필요합니다.

출처: https://www.amazon.com/HTML5-Canvas-Native-Interactivity-Animation/dp/1449334989

 

 

핵심 개념은 단 세 가지뿐입니다.

  1. “좌표” 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. 법선 : 공→장애물로 그린 선을 1칸 길이로 만든 화살표
  2. → (nx, ny) = (dx/dist, dy/dist)
  • 정의: 법선 벡터는 “충돌 지점에서 표면에 수직으로 뻗은 단위 벡터”를 말해요.
  • 충돌 예제에서는 공→장애물 중심을 잇는 방향으로 벡터를 구한 뒤, 이걸 길이 1로 정규화(normalize) 한 것이 법선입니다.

법선 계산하는 방법

 

  1. 반사 공식
    • 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)를 뒤집거나 반사 공식으로 바꿨다.

 

 

코드 예시

 

충돌 시뮬레이션 예시 코드(동영상은 20FPS)

 

 

아래 링크에서 확인 가능합니다.

 

https://codesandbox.io/p/devbox/kc8674

 

 

 

reference 

 

HTML5 Canvas: Native Interactivity and Animation for the Web 에서 일부 내용 발췌

반응형

+ Recent posts