メディアアート~ウォールパーティクル~

# 作品名: ウォールパーティクル

この作品はボールが壁に当たるとパーティクルを出し、さらにボールの移動領域が変則的に変わります。

<iframe width="560" height="315" src="https://www.youtube.com/embed/WhuuaUjbZ9c?si=jh-62W1ch9f1k56o" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

## プログラム
まず、Particleクラスを作ります。
ここでは、Particleクラスは別のプログラムで記述します。
以下はディレクトリ構成です。
```
basic5
└basic5.pde→メインプログラム
└Particle.pde→Particleクラスを定義するプログラム
```

以下はクラスです。
```
// 粒子クラス
class Particle {
  float x, y;
  float speedX, speedY; // 速度
  float gravity = 0.1;  // 重力
  int startTime;
  float alpha = 255;  // 透明度（初期値 255）
  float particle_hue;

  Particle(float startX, float startY, int time, float hue) {
    x = startX;
    y = startY;
    startTime = time;
    
    // ランダムな角度と速度を設定（多方面に発射）
    float angle = random(-PI - PI, -PI + PI); // -180度±180度
    float speed = random(2, 5); // 速度をランダム化
    
    speedX = cos(angle) * speed;
    speedY = sin(angle) * speed;
    particle_hue = hue;
  }
  
  void update() {
    // 速度に基づいて移動
    x += speedX;
    y += speedY;

    // 透明度を時間とともに減少（3秒で完全に消えるように調整）
    alpha = 255 - (millis() - startTime) / 18.0;
    if (alpha < 0) alpha = 0; // 負の値を防ぐ
  }
  
  void display() {
    noStroke();
    fill(particle_hue, Saturation, Value, alpha); // アルファ値を適用
    ellipse(x, y, 10, 10);
  }
  boolean isOutOfScreen() {
    return y > height  || y<0 || x>width || x<0 || alpha <= 0; // 画面下に到達 or 透明度ゼロで削除
  }
}

```
※update関数: draw関数同様にオブジェクトの位置や形状を変えることができる, display関数: 設定した位置や形状に基づいて描画する , isOutOfScreen関数: 特定の条件に達したら配列から削除する
<ol>
    <li>まずボールの位置と色を定義し、さらにx方向y方向に進む向きをランダムに設定
    <li>パーティクルの位置をスピードに応じて追加
    <li>時間がたつにつれて透明度を増加させる
    <li>上記に基づいてパーティクルを描画
</ol>

また1作品目のランダムパーティクルの違いは、重力を使っていない点です。

次、メインプログラムでパーティクルの配列(Particle)と色、衝突時の判定の準備をします。
```
//ボールの移動
float dx = 100;
float dy = 100;
//衝突の判定
float collidex = 1;
float collidey = 1;
//ボールの位置
float posx=300;
float posy=300;
//色の準備
float Hue = 0; // 色相
float Saturation = 500; // 彩度
float Value = 1000; // 明度
int MaxColor;

//壁の領域
//x0,y0は左上, x1,y1は右下
float x0 = 0;
float y0 = 0;
float x1 = 0;
float y1 = 0;
//衝突時に壁の領域を変更するために使用
float randomparam = 0;

//Particleクラスを呼び出す
ArrayList<Particle> particles = new ArrayList<Particle>();

```

次、初期設定を行います。
backgroundを白にするため、MaxColor-1と設定し、スクリーンサイズは大きくします。
さらに、使用するボールの位置は画面の中央に配置し、壁の領域はスクリーンサイズの幅のそれぞれ60%に設定します。
```
void setup() {
  size(1250, 600);
  posx = width/2;
  posy = height/2;
  x0 = width*0.2;
  y0 = height*0.2;
  x1 = width*0.8;
  y1 = height*0.8;
  strokeWeight(2);
  MaxColor = 800;
  colorMode(HSB, MaxColor, 255, 255); // HSB + 透明度対応
}
```

最後、描画処理を行います。
ここでは壁とバックグラウンドを設定し、さらに描画処理も施します。
また壁に衝突した時、パーティクルを表示させて壁の場所を変更させます。
例えば右の壁に衝突した際、左の壁にrandomparamで-300~300ランダムに変形し、移動速度と向きも物理法則に抗ってランダムに設定します。
また、randomparamの影響でウインドウサイズを超えたかつボールの位置が領域外に移動した際の例外処理を行いました。
```
void draw() {
  background(Hue, Saturation, Value);
  fill(MaxColor-1);
  rect(x0,y0,x1-x0,y1-y0);

  float a = 0.05; //移動するための重み
  
  // 全ての粒子を更新・描画
  for (int i = particles.size() - 1; i >= 0; i--) {
    Particle p = particles.get(i);
    p.update();
    p.display();
    
    // 透明度が0になったら削除
    if (p.isOutOfScreen()) {
      particles.remove(i);
    }
  }

  // 壁に衝突したら
  if (posy > y1){
      collidey = -1;
      dy = random(0,200);
      for (int i = 0; i < 50; i++) { // 一度の衝突で複数の粒子を生成
        particles.add(new Particle(posx, posy, millis(),Hue));
      }
      Hue = random(0, 1000); 
      randomparam =  random(-300,300);
      if ((y0+randomparam)<y1){
          y0 += randomparam;
      }
  } else if (posy < y0){
      collidey = 1;
      dy = random(0,200);
      for (int i = 0; i < 30; i++) { // 一度の衝突で複数の粒子を生成
        particles.add(new Particle(posx, posy, millis(),Hue));
      }
      Hue = random(0, 1000); 
      randomparam =  random(-300,300);
      if ((y1+randomparam)>x0){
          y1 += randomparam;
      }
  }
  if (posx > x1){
      collidex = -1;
      dx = random(0,200);
      for (int i = 0; i < 30; i++) {// 一度の衝突で複数の粒子を生成
        particles.add(new Particle(posx, posy, millis(),Hue));
      }
      Hue = random(0, 1000); 
      randomparam =  random(-300,300);
      if ((x0+randomparam)<x1){
          x0 += randomparam;
      }
  } else if (posx < x0){
      collidex = 1;
      dx = random(0,200);
      for (int i = 0; i < 30; i++) { // 一度の衝突で複数の粒子を生成
        particles.add(new Particle(posx, posy, millis(),Hue));
      }
      Hue = random(0, 1000); 
      randomparam =  random(-300,300);
      if ((x1+randomparam)>x0){
          x1 += randomparam;
      }
  }
  posx += collidex*dx*a;
  posy += collidey*dy*a;
  
  //例外処理
  if (x0<100){
      x0 = 100;
  }
  if (y0<100){
      y0 = 100;
  }
  if (x1>(width-100)){
      x1 = width-100;
  }
  if (y1>(height-100)){
      y1 = height-100;
  }
  stroke(100);
  fill(Hue, Saturation, Value);
  ellipse(posx,posy,20, 20);
}
```

# まとめ
本作品は、ボールが移動して壁の変形とともにパーティクルの発生を楽しむことができます。しかし本作品は、マウスクリックでは使い勝手悪いので騒音でも楽しめる音付きの環境で楽しむように開発する予定です。