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
반응형