« とりあえずSFTP | Main | Processingであの花ED風-花クラス解説 »

Processingであの花ED風

            

いまさらながらアニメ「あの日見た花の名前を僕達はまだ知らない。」のDVDを見る機会がありました。ED­曲の"secret base"に乗せて花が大量に降ってくる映像はすごっく印象的で気に入ったので、自分で作ってみたいと思いました。

しかし、Flashは使ったことがないし、アニメーションの技術は皆無なので、どうしようかと考えた結果、プログラミングの経験はあるので、Processingを使ってやってみることにしました。

Processingは電子工作で知ったArduinoのIDE(Integrated Development Environment 統合開発環境)のもとになった言語および開発環境で、グラフィックプログラムを手軽に実現することに向いているそうです。記述はほとんどJavaやC++そのもので、内部的にもJavaがベースになっているようです。

とりあえず、出来上がりは次の通り。


とこんな感じになりました。しかし残念なことに動画に変換する段階でだいぶ劣化しています。実際のプログラムはもっと鮮やかです。

ソースコード。

メインのスケッチ
ArrayList<Flower> flowers = new ArrayList<Flower>();  //「花」を管理する配列

int fRate = 30;  // フレームレート

float zoomRange = 0;  //ズームする割合
float zoomRate = 0;  //1フレームあたりのズーム量
int zoomCount = 0;  //ズームにかけるフレーム数

float eyeX = 0;  //カメラ位置 X座標
float eyeY = 0;  //カメラ位置 Y座標
float eyeZ = 0;  //カメラ位置 Z座標
float centerX = 0;  //カメラ視点 X座標
float centerY = 0;  //カメラ視点 Y座標
float centerZ = 0;  //カメラ視点 Z座標

boolean isColor = false//カラーモードフラグ

//初期設定
void setup() {
  //画面設定
  size(1200, 800, P3D);
  frameRate(fRate);
  colorMode(HSB, 100);

  //カメラ設定
  zoomRange = height * 0.2;
  eyeX = width/2.0;
  eyeY = height/2.0;
  eyeZ = (height/2.0) / tan(PI*30.0 / 180.0);
  eyeZ -= zoomRange;
  centerX = width/2.0;
  centerY = height/2.0;
  camera(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, 0, 1, 0);

  //「花」生成 小さい方(奥)から大きい方(手前)の順に
  for (int i = 0; i < 1000; i++) {
    flowers.add(new Flower("fl24.png", 1, 1));
  }
  for (int i = 0; i < 400; i++) {
    flowers.add(new Flower("fl32.png", 1, 1));
  }
  for (int i = 0; i < 80; i++) {
    flowers.add(new Flower("fl64.png", 1.5, 2));
  }
  for (int i = 0; i < 20; i++) {
    flowers.add(new Flower("fl128.png", 2, 2));
  }
  for (int i = 0; i < 4; i++) {
    flowers.add(new Flower("fl256.png", 3, 3));
  }
}

//描画
void draw() { 
  //背景塗りつぶし
  background(98);

  //「花」描画
  if (zoomCount == 0) {
    //ズーム指定がない場合は、「花」をアニメーション描画
    for (Flower f : flowers) {
      f.run();
    }
  }
  else {
    //ズーム指定がある場合は、カメラを操作
    eyeZ += zoomRate;
    camera(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, 0, 1, 0);
    //「花」は現在の位置で再描画
    for (Flower f : flowers) {
      f.draw();
    }

    zoomCount -= 1;
    if (zoomCount == 0) {
      //ズームが終わったら、カラーモードを変更
      for (Flower f : flowers) {
        f.changeColor();
      }
      isColor = !isColor;
    }
  }
  //↓アニメーション用のフレーム画像を出力する場合はコメント解除 
  //saveFrame("frames/####.tif");
}

//マウスクリックイベントハンドラ
void mouseClicked() {
  //ズームにかけるフレーム数を設定する
  zoomCount = (int)(fRate * 1.2);
  if (isColor) {
    //カラー表示の場合はズームイン
    zoomRate = -zoomRange / zoomCount;
  }
  else {
    //モノクロ表示の場合はズームアウト
    zoomRate = zoomRange / zoomCount;
  }
}


「花」クラス
//「花」クラス
class Flower {
  PImage img;  //「花」画像

  float currentX = 0;  //現在のX座標
  float currentY = 0;  //現在のY座標
  float currentA = 0;  //現在の角度

  float offsetX = 0;  //1フレームのX方向の移動量
  float offsetY = 0;  //1フレームのY方向の移動量
  float offsetA = 0.05;  //1フレームの回転量

  float maxW = 0;  //描画領域のX方向の最大値
  float minW = 0;  //描画領域のX方向の最小値
  float maxH = 0;  //描画領域のY方向の最大値
  float minH = 0;  //描画領域のY方向の最小値

  float alpha = 0;  //画像の透明度
  float gray = 0;  //画像のモノクロ表示の明るさ
  float hue = 0;  //画像の色調
  float saturation = 0;  //画像の彩度

  boolean isColor = false//カラーモード 初期値:false=モノクロ

  //コンストラクタ
  //  引数
  //    imgFile : 描画する画像イメージ
  //    downSpeed : 花が「落下」する速度の基準値
  //    downRange : 花が「落下」する幅の基準値
  Flower(String imgFile, float downSpeed, float downRange) {
    //指定画像を読み込み
    img = loadImage(imgFile);

    //画像の描画領域を設定
    maxW = width + img.width;
    minW -= img.width;
    maxH = height + img.height;
    minH -= img.height;

    //最初の位置をランダムに設定
    currentX = random(minW, maxW);
    currentY = random(minH, maxH);

    //画像の色調をランダムに設定
    alpha = random(80, 96);
    saturation = random(15, 65);
    hue = random(90, 103);
    if (hue > 100) {
      hue -= 100;
    }
    gray = 100 - saturation + 30;

    //移動量をランダムに設定
    offsetX = random(-downRange, downRange);
    offsetY = random(downSpeed, downSpeed * 2);
  }

  //アニメーション描画...新しい位置を計算して描画
  void run() {
    //新しいX座標を計算...描画領域を超えたら反対側へ
    currentX += offsetX;
    if (currentX > maxW) {
      currentX = minW;
    }
    else if (currentX < minW) {
      currentX = maxW;
    }

    //新しいY座標を計算...描画領域を超えたら反対側へ
    currentY += offsetY;
    if (currentY > maxH) {
      currentY = minH;
    }
    else if (currentY < minH) {
      currentY = maxH;
    }

    //新しい角度を計算
    currentA += offsetA;
    if (currentA > TWO_PI) {
      currentA = 0;
    }

    //新しい位置で画像を描画
    draw();
  }

  //画像を描画
  void draw() {
    //描画前の座標系を保存
    pushMatrix();

    //座標系を描画する位置と角度に設定
    translate(currentX, currentY, 0);
    rotate(currentA);

    //画像に追加する色調を設定
    if (isColor) {
      tint(hue, saturation, 92, alpha);
    }
    else {
      tint(gray, alpha);
    }

    //画像を描画
    imageMode(CENTER);
    image(img, 0, 0);

    //設定した座標系を破棄して前の座標系に戻す
    popMatrix();
  }

  //カラーモード変更
  void changeColor() {
    isColor = !isColor;
    //Y方向の移動量を反転
    offsetY = - offsetY;
  }
}


解説は以下を参照ください。

花クラス解説

メインルーチン解説

参考。

参考にさせていただいたサイトは次の通り。

【ニコニコ動画】やってみようprocessing!!番外編 エセあの花EDに挑戦
これを見て、Processingでやってみようと思いました。概略が説明されていて、大まかな流れがつかめました。

processingであの花EDっぽいものを作る part4
ズームの操作が大変参考になりました。ほとんど出来上ていたところで、ズームアウトの再現がどうしてできず悩んでいたところで、大変助かりました。

後記とか。

実は、僕がメインで使っているノートPCは延命措置を繰り返している5年物なので、そもそもProcessingの3D空間でのカメラワークは無理でした("Framebuffer objects are not supported by this hardware"とかいわれちゃうし、花の数を増やすとカクカクしちゃうし)。それに気づくのに1日も浪費。仕上げと調整は、妻がメインで使っているノートPCで行いました。

ぼちぼちクリスマスシーズンなので、花を雪に変えてみるのもいいかもしれません。

|

« とりあえずSFTP | Main | Processingであの花ED風-花クラス解説 »

Processing」カテゴリの記事

プログラミング」カテゴリの記事

Comments

Post a comment



(Not displayed with comment.)




« とりあえずSFTP | Main | Processingであの花ED風-花クラス解説 »