p5.js でシンプルなパーティクル

この記事は、以下の動画のようなシンプルなパーティクル（種類としては 2パターン）に関する処理を試した時のメモです。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">p5.js を使った、シンプルなパーティクルのテスト。<a href="https://twitter.com/hashtag/p5js?src=hash&amp;ref_src=twsrc%5Etfw">#p5js</a> <a href="https://t.co/ChUpvAae3o">pic.twitter.com/ChUpvAae3o</a></p>&mdash; you (@youtoy) <a href="https://twitter.com/youtoy/status/1396390301229486082?ref_src=twsrc%5Etfw">May 23, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## 冒頭の動画の内容（一つ目の作例）について
### 元ネタと手を加えた部分の概要
今回の内容は、以下の記事に掲載されたものを元にして、少し内容を変えて作りました。

●パーティクルを追加 | p5.js でゲーム制作
　https://fal-works.github.io/make-games-with-p5js/docs/arrange/particles/

主に対応・変更した内容などは、以下のとおりです。

- [p5.js Web Editor](https://editor.p5js.org/)上にコピーして内容を修正
   - パーティクルに関係しない部分（ゲームのプレイヤーに関する部分など）の処理を削除
   - パーティクルの出現位置が上記の削除した部分に依存していたので、キャンバスの中心になるように変更
   - `function setup()` と `function draw()` の部分を少し書きかえ
   - パーティクルが動いていく方向が360度の範囲になるように変更
   - 色がグレースケール（＋透過）だったのをカラー（＋透過）に変更
   - パーティクルが消えるまでの時間を長くなるように変更
- p5.js Web Editor で動かしたもののファイル一式ダウンロード後、HTMLファイル 1つで動くように変更
   - p5.js のファイルは CDN を参照するように変更
   - CSS と JavaScript のファイルの内容を HTML内に直書き

なお、HTMLファイル 1つで動くように変更したのは、コピペ 1回で動作させるための一式を準備できるようするためです。

### ソースコード
上記の対応を行った後のソースコードは以下のとおりです。

```html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <script src="https://cdn.jsdelivr.net/npm/p5@1.3.1/lib/p5.js"></script>
    <style type="text/css">
      html,
      body {
        margin: 0;
        padding: 0;
      }
      canvas {
        display: block;
      }
    </style>
    <meta charset="utf-8" />
  </head>
  <body>
    <script>
      let particles = [];

      function setup() {
        createCanvas(400, 400);
        background(0, 0, 0);
      }

      function draw() {
        background(0, 0, 0);

        particles.push(createParticle(200, 200));
        particles = particles.filter(particleIsAlive);
        for (let particle of particles) {
          updatePosition(particle);
          decreaseLife(particle);
          drawParticle(particle);
        }
      }

      function createParticle(x, y) {
        let direction = random(TWO_PI);
        let speed = 2;

        return {
          x,
          y,
          vx: speed * cos(direction),
          vy: speed * sin(direction),
          life: 1, // = 100%
        };
      }

      function particleIsAlive(particle) {
        return particle.life > 0;
      }

      function updatePosition(particle) {
        particle.x += particle.vx;
        particle.y += particle.vy;
      }

      function decreaseLife(particle) {
        particle.life -= 0.01;
      }

      function drawParticle(particle) {
        push();
        noStroke();
        fill(random(255), random(255), random(255), particle.life * 255);
        square(particle.x, particle.y, particle.life * 10);
        pop();
      }
    </script>
  </body>
</html>
```

## 別バージョン（2つ目の作例）
さらに、以下のような見え方が違うものも、上記の内容をベースに作ってみました。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">先ほどのものと少し違ったバージョン（p5.js を使ったパーティクル）。<a href="https://twitter.com/hashtag/p5js?src=hash&amp;ref_src=twsrc%5Etfw">#p5js</a> <a href="https://t.co/eXmbYMPQRm">pic.twitter.com/eXmbYMPQRm</a></p>&mdash; you (@youtoy) <a href="https://twitter.com/youtoy/status/1396418307813613574?ref_src=twsrc%5Etfw">May 23, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

### 手を加えた部分とソースコード
2つ目の内容について、手を加えた部分の説明とソースコードを以下に記載します。

主に変更した内容は、以下のとおりです。

- パーティクルが出現する初期位置を変更
- 各パーティクルのスピードに幅を持たせるよう変更
- パーティクルの色が時間経過で変化しないように変更
- 透過率の変更度合いを少し変更

また、以下がソースコードです。

```html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <script src="https://cdn.jsdelivr.net/npm/p5@1.3.1/lib/p5.js"></script>
    <style type="text/css">
      html,
      body {
        margin: 0;
        padding: 0;
      }
      canvas {
        display: block;
      }
    </style>
    <meta charset="utf-8" />
  </head>
  <body>
    <script>
      let particles = [];
      const CANVAS_SIZE = 400;

      function setup() {
        frameRate(60);
        createCanvas(CANVAS_SIZE, CANVAS_SIZE);
        background(0, 0, 0);
      }

      function draw() {
        background(0, 0, 0);

        // 初期位置を変更
        particles.push(
          createParticle(random(CANVAS_SIZE), random(CANVAS_SIZE))
        );
        particles = particles.filter(particleIsAlive);
        for (let particle of particles) {
          updatePosition(particle);
          decreaseLife(particle);
          drawParticle(particle);
        }
      }

      function createParticle(x, y) {
        let direction = random(TWO_PI);
        let speed = 2;

        return {
          x,
          y,
          vx: speed * random(0.1, 1.5) * cos(direction),
          vy: speed * random(0.1, 1.5) * sin(direction),
          life: 1, // = 100%
          color: [random(255), random(255), random(255)],
        };
      }

      function particleIsAlive(particle) {
        return particle.life > 0;
      }

      function updatePosition(particle) {
        particle.x += particle.vx;
        particle.y += particle.vy;
      }

      function decreaseLife(particle) {
        particle.life -= 0.01;
      }

      function drawParticle(particle) {
        push();
        noStroke();
        fill(...particle.color, (particle.life / 1.5) * 255);
        square(particle.x, particle.y, particle.life * 10);
        pop();
      }
    </script>
  </body>
</html>
```

今回のような内容を p5.js で実装するときは、今までは以下の Class を使ったりしていましたが、上記の filter などを組み合わせた書き方はやったことがなく参考になりました。
　●reference | class
　　https://p5js.org/reference/#/p5/class