gotutiyan’s blog

競技プログラミングをやったりopenframeworksでお絵かきをしたりしています。

ランダムウォーク

TLで見かけたランダムウォークをProcessingで実装しました。

実装の方針は以下の通りです。
・円を動かす方向を数十フレームごとに更新する。
・円の軌跡を数十フレーム分残して、lineで描画する。

今回は4方向のみのランダムウォークです。自身としては初めて、PVectorとArrayListを使ってみました。どちらも使い慣れると便利でした。
PVectorはfloat型変数x,yを持ったクラスで、以下のようにして扱えます。

PVector p=new PVector(0,50);
//pのx座標に50、y座標に100足すとします。add()で足す。
p.add(new PVector(50,100));

ellipse(p.x,p.y,100,100); //ellipse(0+50,50+100,100,100)と同じ

また、ArrayListは動的配列で、C++で言う所のvectorクラスと同じようなものです。以下のようにして扱えます。

ArrayList<PVector> prev=new ArrayList<PVector>();
//値を追加。一番後ろに入ります。
prev.add(new PVector(x座標,y座標));
//値を削除。
prev.remove(消したい要素の添字);
//配列の長さを取得。
prev.size();
//配列の要素を参照。
prev.get(添字);

要素の取得にはprev.get(0);とする必要があり、prev[0]のようにCライクでは書けません。

また、今回は要素の追加のadd()でめちゃくちゃハマリました。このことについてはまた別の記事として書きたいと思います。
今回はこのArrayListを「後ろに行くほど新しい軌跡が入った配列」として利用しました。後ろから座標を入れて行って、前から消して行きます。

コードは以下の通り。

int sz=50;  //描く数
RandomWalk random9[]=new RandomWalk[sz];
void setup(){
 size(500,500,P2D); 
 smooth();
 for(int i=0;i<sz;i++)random9[i]=new RandomWalk();
}

void draw(){
  background(0);
 for(int i=0;i<sz;i++)random9[i].move(); 
}

class RandomWalk {
  PVector now;  //円の現在の位置
  PVector dir[]=new PVector[4];  //向かう方向を4方向分格納
  PVector diff;  //現在円が向かうべき方向
  ArrayList<PVector> prev=new ArrayList<PVector>();  //円の軌跡を格納
  int changeDir;  //方向を変えるフレーム間隔

  RandomWalk() {
    now=new PVector((int)random(width), (int)random(height));
    dir[0]=new PVector(1, 0);
    dir[1]=new PVector(-1, 0);
    dir[2]=new PVector(0, 1);
    dir[3]=new PVector(0, -1);
    int x=(int)random(4);
    diff=dir[x];
    changeDir=(int)random(30,50);
  }

  void move() {
    if (frameCount%changeDir==0) {
      //もし画面外にいたなら、引き戻す方向に設定
      if (now.x<0)diff=dir[0];
      else if (now.x>width)diff=dir[1];
      else if (now.y<0)diff=dir[2];
      else if (now.y>height)diff=dir[3];
      else {
        //画面内ならランダムに
        int x=(int)random(0, 4);
        diff=dir[x];
      }
    }
    prev.add(new PVector(now.x,now.y));  //prev配列の一番後ろに今の座標を入れる
    now.add(diff);  //今の座標を更新
    fill(255);
    ellipse(now.x, now.y, 10, 10);
    while (prev.size()>100)prev.remove(0);  //前100フレーム分持ちたいので、多すぎる分古いもの(前)から削除
    stroke(255);
    for (int i=1; i<prev.size(); i++) {
      line(prev.get(i).x, prev.get(i).y,prev.get(i-1).x,prev.get(i-1).y);  //軌跡を描画
    }
  }
};