AI, Coding
윈도우 화면 보호기 - 2D 버블 충돌 효과 구현
Dasol
2025. 3. 5. 03:07
See the Pen Untitled by sol Da (@sol-Da) on CodePen.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>버블 충돌 효과</title>
<style>
body,
html {
margin: 0;
padding: 0;
overflow: hidden;
}
canvas {
display: block;
background: #000;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
class Bubble {
constructor(x, y, radius, dx, dy) {
this.x = x;
this.y = y;
this.radius = radius;
this.dx = dx;
this.dy = dy;
}
draw() {
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
ctx.fillStyle = 'rgba(0, 150, 255, 0.5)';
ctx.fill();
ctx.strokeStyle = '#fff';
ctx.stroke();
ctx.closePath();
}
update(bubbles) {
// 위치 업데이트
this.x += this.dx;
this.y += this.dy;
// 벽과 충돌 검사
if (this.x + this.radius > canvas.width || this.x - this.radius < 0) {
this.dx = -this.dx;
}
if (this.y + this.radius > canvas.height || this.y - this.radius < 0) {
this.dy = -this.dy;
}
// 다른 버블과 충돌 검사
for (let bubble of bubbles) {
if (bubble === this) continue;
const dx = bubble.x - this.x;
const dy = bubble.y - this.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < this.radius + bubble.radius) {
// 간단한 충돌 반응: 속도 교환 (더 정교한 반응은 충돌 벡터를 계산)
let tempDx = this.dx;
let tempDy = this.dy;
this.dx = bubble.dx;
this.dy = bubble.dy;
bubble.dx = tempDx;
bubble.dy = tempDy;
}
}
this.draw();
}
}
// 여러 개의 버블 생성
const bubbles = [];
const bubbleCount = 10;
for (let i = 0; i < bubbleCount; i++) {
const newBubble = createBubble(bubbles, canvas.width, canvas.height);
bubbles.push(newBubble);
}
// 애니메이션 루프
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
bubbles.forEach(bubble => bubble.update(bubbles));
requestAnimationFrame(animate);
}
function createBubble(bubbles, canvasWidth, canvasHeight) {
const maxAttempts = 100;
let newBubble;
for (let attempt = 0; attempt < maxAttempts; attempt++) {
const radius = 20;
const x = Math.random() * (canvasWidth - 2 * radius) + radius;
const y = Math.random() * (canvasHeight - 2 * radius) + radius;
newBubble = new Bubble(x, y, radius, (Math.random() - 0.5) * 4, (Math.random() - 0.5) * 4);
let overlapping = false;
for (let bubble of bubbles) {
const dx = bubble.x - newBubble.x;
const dy = bubble.y - newBubble.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < bubble.radius + newBubble.radius) {
overlapping = true;
break;
}
}
if (!overlapping) {
return newBubble;
}
}
return newBubble; // maxAttempts 안에 겹치지 않는 위치를 찾지 못하면 그냥 반환
}
animate();
</script>
</body>
</html>
윈도우 화면 보호기에서 종종 버블들이 튀는 걸 보면서 저 버블이 어디로 갈지 계속 보면서 빠져들었었다.
자연스레 어떻게 구현되는지 궁금했는데 이번기회에 코드로 확인해 보았다.
먼저 화면 크기에 맞게 캔버스를 잡고
버블의 중심좌표(x, y)와 반지름(r), 각 방향으로의 속도(dx, dy)의 속성을 가진 버블클래스를 만들고 생성한다.
캔버스의 가장자리에 부딪히면(버블의 중심좌표 + 반지름이 가장자리보다 작아지면) 속도의 방향을 전환한다.
예) 왼쪽 가장자리에 버블이 부딪히면 반대방향인 dx를 -dx로 변환
버블끼리 충돌도 캔버스 가장자리와 부딪히는 것과 원리는 비슷하다.
두 버블의 반지름 합보다 거리가 작으면 충돌로 판단하고 서로의 속도를 교환한다.
그리고 html5의 requestAnimationFrame이란 내장 함수를 통해서 무한 반복으로
화면을 지우고 새로 버블들을 그리는 animate함수를 무한 반복한다.
나중에 더 활용하고 싶으면 requestAnimationFrame 함수의 원리를 더 파악해 보면 좋을 것 같다.
또한 이미지나 계산되는 함수들을 조절하면 더 다이나믹한 화면을 만들 수 있다.
3D도 만들어봐야겠다ㅋㅋ
728x90
반응형