ランダムウォーク
TLで見かけたランダムウォークをProcessingで実装しました。
ランダムウォークを見たので描いてみました。これすごく好きです。
— string s="ごつちやん"; (@gotutiyan_kapi) October 3, 2018
実装解説とコードは以下から。https://t.co/drS6Pxb2H5#processing pic.twitter.com/Ss7Bu3VyDo
実装の方針は以下の通りです。
・円を動かす方向を数十フレームごとに更新する。
・円の軌跡を数十フレーム分残して、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); //軌跡を描画 } } };