Sunday, June 14, 2015

鳥の群舞の簡単シミュレーション

鳥の群れが空を舞う様子をシミュレートしてみた。
それぞれの鳥は、

1. すべて同じ速さで飛ぶ。
2. 飛ぶ方向は、近くにいる鳥の向かっている方角の平均値とする。

という単純な原則とし、さらにプログラムを簡単にするため、

3. 立体空間ではなく平面上のシミュレーションとする。
4. 鳥同士はいくら近づいてもよい。重なってもかまわない。

とした。

シミュレーション結果の例。


いちおう鳥の群舞らしく見える。
この動画はループしていて、画面全体に鳥の散らばっている状態が最初で、群れが右上に消えていくところが最後。画面から消えた鳥はまっすぐに遠ざかり、もどって来ることはない。
地上の草むら、風、天敵などアイテムの追加や、鳥の飛行区域の限定など宿題。すべての鳥の方向がそろってしまうのもおもしろくないので、個々の鳥の飛行方向にゆらぎを与えること等も。

プログラムは Processing で書いた。
コードは次のとおり。要検討事項は多いが、とりあえず動いてる。
int canvasW = 400, canvasH = 400;
int population = 500;
float neighborhoud = 50;
float speed = 1.5;
Bird[] birds = new Bird[population];

class Bird {
  float x, y, dir;
 
  Bird(float tempX, float tempY, float tempDir) {
    x = tempX;
    y = tempY;
    dir = tempDir;
  }
 
  void display() {
    arc(x, y, 20, 20, dir-PI-0.2, dir-PI+0.2);
  }
  
  float newDir() {
    float accumX = cos(dir), accumY = sin(dir);
    for (int i = 0; i < population; i++) {
      if (dist(x, y, birds[i].x, birds[i].y) < neighborhoud) {
        accumX += cos(birds[i].dir);
        accumY += sin(birds[i].dir);
      }
    }
    return atan2(accumY, accumX);
  }
}

void setup() {
  size(canvasW, canvasH);

  fill(0);
  for (int i = 0; i < population; i++) {
    birds[i] = new Bird(random(0, canvasW),
                        random(0, canvasH),
                        random(0, TWO_PI));
  }
}

void draw() {
  background(204);
  for (int i = 0; i < population; i++) {
    birds[i].display();
    float newdir = birds[i].newDir();
    birds[i].dir = newdir;
    birds[i].x += speed * cos(newdir);
    birds[i].y += speed * sin(newdir);
  }
}
このプログラムの難点は、群れの行動に対する個々の鳥の影響度合いが平等でないことで、鳥(Bird オブジェクト)の配列 birds の先頭に近い鳥ほど影響力が大きい。「追記] atan2の使い方もいけない。