개인 노트 정리) Canvas - 2-03. 불꽃놀이 원 모양으로 랜덤 파티클 만들어보기

2023. 6. 7. 20:46개인노트-인강

인터랙티브 웹 개발 Canvas 불꽃놀이 인강 정리

 

# 불꽃놀이 원 모양으로 랜덤 파티클 만들어보기

이번에는 폭죽의 모양을 제대로 구성하는 방법에 대해서 알아보겠습니다.

 

## 파티클이 원이 아닌 사각형으로 퍼져나간 이유

vx와 vy의 랜덤값 범위를 좌표에 표시해 보면 사각형 모양이 나옵니다. (예 : vx의 랜덤값 범위 -5~5, vy의 랜덤값 범위 -5~5)

랜덤값의 범위를 원 모양이 나오게 만들어주어야 합니다.

랜덤 r (반지름) 과 랜덤 세타 값을 구하면 사방으로 퍼지는 원의 모양을 만들 수 있게 됩니다.

 

### 원모양으로 퍼져나갈 vx와 vy 구하기

// ...
  createParticles() {
    const PARTICLE_NUM = 400;
    const x = randomNumBetween(0, this.canvasWidth);
    const y = randomNumBetween(0, this.canvasHeight);
    for (let i = 0; i < PARTICLE_NUM; i++) {
      const r = randomNumBetween(0, 3);
      const angle = (Math.PI / 180) * randomNumBetween(0, 360);

      // const vx = randomNumBetween(-5, 5);
      // const vy = randomNumBetween(-5, 5);
      const vx = r * Math.cos(angle);
      const vy = r * Math.sin(angle);
      this.particles.push(new Particle(x, y, vx, vy));
    }
  }
// ...

 

# 중력과 마찰효과 넣기

중력효과를 넣으면 파티클이 아래로 떨어지는 효과가 추가되어 좀 더 자연스러워 보이고, 마찰을 넣게 되면 속도가 점점 0에 수렴하게 되면서 좀 더 불꽃놀이같은 모양을 만들 수 있습니다.

 

## 중력 적용

export default class Particle extends CanvasOption {
  constructor(x, y, vx, vy) {
    super();
    // ...
    this.gravity = 0.12;
  }
  update() {
    this.vy += this.gravity;

    // ...
  }
  draw() {
    // ...
  }
}

 

## 마찰 적용

매 프레임 마다 속도 x와 속도 y 값에 모두 같은 마찰값을 곱해주어서 점점 느려지게 만들어 0으로 수렴하게 합니다.

export default class Particle extends CanvasOption {
  constructor(x, y, vx, vy) {
    super();
    // ...
    this.friction = 0.93;
  }
  update() {
    this.vx *= this.friction;
    this.vy *= this.friction;

    // ...
  }
  draw() {
    // ...
  }
}

 

# 파티클마다 opacity 값도 랜덤으로 적용하기

opacity 값도 랜덤으로 생성하여 생성자 함수로 넘겨줍니다.

  createParticles() {
    // ...
      const opacity = randomNumBetween(0.6, 0.9);
      this.particles.push(new Particle(x, y, vx, vy, opacity));
    }
  }
  // ...
  constructor(x, y, vx, vy, opacity) {
    super();
    // ...
    this.opacity = opacity;
    // ...
  }
  // ...

 

# 꼬리효과 만들기

현재 캔버스 전체에 매 프레임마다 이전 프레임을 지우는 대신에 검정 배경색을 칠하고 있습니다. 여기에 alpha 값을 적용하면 꼬리 모양을 만들 수 있습니다.

예를 들어 alpha 값을 10을 적용하면 alpha 값이 10 밖에 되지않아서 여러번 칠해야 화면이 검정색이 되기 때문에 잔상이 남는 것처럼 보이게 됩니다.

// ...
  render() {
    const frame = () => {
      requestAnimationFrame(frame);
      // ...
      this.ctx.fillStyle = this.bgColor + "40"; // #00000010
      // ...
    };
  }

 

# 화면 크기에 따라 폭죽이 퍼지는 크기 조절하기(반지름 r 크기 조절하기)

innerWidth를 사용하여 랜덤 r를 구하게 된다면 화면 가로가 작아지거나 화면 가로가 클 때는 비슷한 사이즈 비율로 원을 만들 수 있어서 문제를 해결할 수 있는 것처럼 보이지만, 세로의 화면이 작아질 때는 그 문제를 해결하지 못하고,

반대로 innerWidth 대신에 innerHeight 값을 토대로 랜덤 r을 구하게 되면 그 문제가 반대로 생깁니다.

 

그래서 innerWidth 와 innerHeight 의 빗변의 길이를 피타고라스의 정의를 이용하여 길이를 구한다음, 빗변의 길이를 토대로 랜덤 반지름 r를 구해보겠습니다.

 

util.js

빗변구하기 함수

// ...
export const hypotenuse = (x, y) => {
  return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
}

index.js

// ...
createParticles() {
    // ...
    for (let i = 0; i < PARTICLE_NUM; i++) {
      const r =
        randomNumBetween(2, 100) * hypotenuse(innerWidth, innerHeight) * 0.0001;
      // ...
    }
  }
// ...