チュートリアル | (トピック一覧へ戻る) |
まわりのエージェントをチェックする範囲は、それぞれの規則で異なるものを設定します。 通常は、結束規則の範囲を一番大きく取り、次に整列規則の範囲、そして分離規則の範囲を一番小さくとります。 しかし、これに限らずこれらの範囲を調整することによって、様々なスウォーム動作が現れます。
Boidアルゴリズムの詳細は クレイグ・レイノルズのページを参照下さい。
結束規則のために調べる距離範囲をcohesionDistとします。 範囲内にあるエージェントの中心点を求めるために、 まずそれに該当するエージェントの数countとの位置の総和を計算し、 総和をcountで割ることで中心点を求めます。 その次に、エージェント自身の位置から中心点への差ベクトルを生成し、 係数cohesionRatioをかけて力のベクトルとし、 それを自身に適用します。
import processing.opengl.*; import igeo.*; void setup(){ size(480, 360, IG.GL); IG.duration(30); for(int i=0; i < 100; i++){ new MyBoid(IRand.pt(100,100,0), IRand.pt(-10,-10,0,10,10,0)).clr(IRand.clr()); } } class MyBoid extends IParticle{ double cohesionDist = 50; double cohesionRatio = 5; IVec prevPos; MyBoid(IVec p, IVec v){ super(p,v); } void interact(ArrayList< IDynamics > agents){ IVec center = new IVec(); //zero vector int count = 0; for(int i=0; i < agents.size(); i++){ if(agents.get(i) instanceof MyBoid){ MyBoid b = (MyBoid)agents.get(i); if(b != this){ if(b.pos().dist(pos()) < cohesionDist){ center.add(b.pos()); count++; } } } } if(count > 0){ center.div(count); //center of neighbors IVec force = center.sub(pos()); //difference of position push(force.mul(cohesionRatio)); } } void update(){ //drawing line IVec curPos = pos().cp(); if(prevPos!=null){ IG.crv(prevPos, curPos).clr(clr()); } prevPos = curPos; } }
結束規則と同様に、分離規則をチェックする範囲を
separationDistで与え、
この距離より近ければ近すぎると判断します。
その場合、相手のエージェントの位置から自分の位置への差ベクトルを生成して力のベクトルとします。
その力の大きさは、以下の式で設定され、
force.len( separationDist - dist )
近ければ近いほど大きくなるようにします。
この力のベクトルを、範囲内にあるすべてのエージェントに対して総和をとったのち
その数で割って平均をとり、そして係数separationRatioをかたものを
自身に対する力のベクトルとして適用します。
一つ注意が必要なのは、If条件分岐の中にある dist != 0 の部分で、dist==0の状況は他のエージェントが全く同じ場所に存在する場合に起こり、 そうすると位置の差ベクトルがゼロ・ベクトルとなってどちらの方向に力を与えるべきか計算ができなくなるため、 If条件分岐で除外されています。
import processing.opengl.*; import igeo.*; void setup(){ size(480, 360, IG.GL); IG.duration(60); for(int i=0; i < 100; i++){ new MyBoid(IRand.pt(100,100,0), IRand.pt(-5,-5,0,20,20,0)).clr(IRand.clr()); } } class MyBoid extends IParticle{ double separationDist = 30; double separationRatio = 5; IVec prevPos; MyBoid(IVec p, IVec v){ super(p,v); } void interact(ArrayList< IDynamics > agents){ IVec separationForce = new IVec(); //zero vector int count = 0; for(int i=0; i < agents.size(); i++){ if(agents.get(i) instanceof MyBoid){ MyBoid b = (MyBoid)agents.get(i); if(b != this){ double dist = b.pos().dist(pos()); if(dist < separationDist && dist!=0 ){ IVec force = pos().dif(b.pos()); force.len(separationDist - dist); //the closer the larger separationForce.add(force); count++; } } } } if(count > 0){ separationForce.div(count); //average force separationForce.mul(separationRatio); push(separationForce); } } void update(){ //drawing line IVec curPos = pos().cp(); if(prevPos!=null){ IG.crv(prevPos, curPos).clr(clr()); } prevPos = curPos; } }
整列規則を適用する範囲はalignmentDistで与えられます。 その範囲内にあるエージェントの数を数え、平均の速度ベクトルを求めます。 そして平均速度ベクトルと、自身の速度ベクトルの差ベクトルを求め、 それに係数alignmentRatioを掛けたものを、 力のベクトルとして自身に適用します。
import processing.opengl.*; import igeo.*; void setup(){ size(480, 360, IG.GL); IG.duration(100); for(int i=0; i < 100; i++){ new MyBoid(IRand.pt(100,100,0), IRand.pt(-5,-5,0,20,20,0)).clr(IRand.clr()); } } class MyBoid extends IParticle{ double alignmentDist = 40; double alignmentRatio = 5; IVec prevPos; MyBoid(IVec p, IVec v){ super(p,v); } void interact(ArrayList< IDynamics > agents){ IVec averageVelocity = new IVec(); //zero vector int count = 0; for(int i=0; i < agents.size(); i++){ if(agents.get(i) instanceof MyBoid){ MyBoid b = (MyBoid)agents.get(i); if(b != this){ if(b.pos().dist(pos()) < alignmentDist){ averageVelocity.add(b.vel()); count++; } } } } if(count > 0){ averageVelocity.div(count); IVec force = averageVelocity.sub(vel()); //difference of velocity push(force.mul(alignmentRatio)); } } void update(){ //drawing line IVec curPos = pos().cp(); if(prevPos!=null){ IG.crv(prevPos, curPos).clr(clr()); } prevPos = curPos; } }
import processing.opengl.*; import igeo.*; void setup(){ size(480, 360, IG.GL); IG.duration(150); for(int i=0; i < 100; i++){ new MyBoid(IRand.pt(100,100,0), IRand.pt(-5,-5,0,20,20,0)).clr(IRand.clr()); } } class MyBoid extends IParticle{ double cohesionDist = 50; double cohesionRatio = 5; double separationDist = 30; double separationRatio = 5; double alignmentDist = 40; double alignmentRatio = 5; IVec prevPos; MyBoid(IVec p, IVec v){ super(p,v); } void cohere(ArrayList< IDynamics > agents){ IVec center = new IVec(); //zero vector int count = 0; for(int i=0; i < agents.size(); i++){ if(agents.get(i) instanceof MyBoid && agents.get(i)!=this){ MyBoid b = (MyBoid)agents.get(i); if(b.pos().dist(pos()) < cohesionDist){ center.add(b.pos()); count++; } } } if(count > 0){ push(center.div(count).sub(pos()).mul(cohesionRatio)); } } void separate(ArrayList< IDynamics > agents){ IVec separationForce = new IVec(); //zero vector int count = 0; for(int i=0; i < agents.size(); i++){ if(agents.get(i) instanceof MyBoid && agents.get(i)!=this){ MyBoid b = (MyBoid)agents.get(i); double dist = b.pos().dist(pos()); if(dist < separationDist && dist!=0 ){ separationForce.add(pos().dif(b.pos()).len(separationDist - dist)); count++; } } } if(count > 0){ push(separationForce.mul(separationRatio/count)); } } void align(ArrayList< IDynamics > agents){ IVec averageVelocity = new IVec(); //zero vector int count = 0; for(int i=0; i < agents.size(); i++){ if(agents.get(i) instanceof MyBoid && agents.get(i) != this){ MyBoid b = (MyBoid)agents.get(i); if(b.pos().dist(pos()) < alignmentDist){ averageVelocity.add(b.vel()); count++; } } } if(count > 0){ push(averageVelocity.div(count).sub(vel()).mul(alignmentRatio)); } } void interact(ArrayList< IDynamics > agents){ cohere(agents); separate(agents); align(agents); } void update(){ //drawing line IVec curPos = pos().cp(); if(prevPos!=null){ IG.crv(prevPos, curPos).clr(clr()); } prevPos = curPos; } }
import processing.opengl.*; import igeo.*; void setup(){ size(480, 360, IG.GL); IG.duration(150); for(int i=0; i < 100; i++){ new MyBoid(IRand.pt(100,100,0), IRand.pt(-5,-5,0,20,20,0)).clr(IRand.clr()); } } class MyBoid extends IParticle{ double cohDist = 50; double cohRatio = 0.05; double sepDist = 30; double sepRatio = 0.05; double aliDist = 40; double aliRatio = 0.05; IVec prevPos; MyBoid(IVec p, IVec v){ super(p,v); } void interact(IDynamics agent){ if(agent instanceof MyBoid){ MyBoid b = (MyBoid)agent; if(dist(b) < cohDist) push( b.dif(this).mul(cohRatio) ); if(dist(b) < sepDist) push( dif(b).len((sepDist-dist(b))*sepRatio) ); if(dist(b) < aliDist) push( b.vel().dif(vel()).mul(aliRatio) ); } } void update(){ //drawing line IVec curPos = pos().cp(); if(prevPos!=null){ IG.crv(prevPos, curPos).clr(clr()); } prevPos = curPos; } }
import processing.opengl.*; import igeo.*; void setup(){ size(480, 360, IG.GL); IG.duration(150); for(int i=0; i < 100; i++){ MyBoid b = new MyBoid(IRand.pt(100,100,0), IRand.pt(-5,-5,0,20,20,0)); b.clr(IRand.clr()); b.cohesionDist(50); b.cohesionRatio(5); b.separationDist(30); b.separationRatio(5); b.alignmentDist(40); b.alignmentRatio(5); } } class MyBoid extends IBoid{ IVec prevPos; MyBoid(IVec p, IVec v){ super(p,v); } void update(){ //drawing line IVec curPos = pos().cp(); if(prevPos!=null){ IG.crv(prevPos, curPos).clr(clr()); } prevPos = curPos; } }
一つめの例では、結束、分離、整列の距離パラメータを全て小さく設定してあります。 その結果、スウォーム・エージェント集合して移動するクラスタが小さいグループで分断しています。
import processing.opengl.*; import igeo.*; void setup(){ size(480, 360, IG.GL); IG.duration(100); for(int i=0; i < 100; i++){ MyBoid b = new MyBoid(IRand.pt(100,100,0), IRand.pt(-5,-5,0,20,20,0)); b.clr(IRand.clr()); b.cohesionDist(21); b.cohesionRatio(5); b.separationDist(10); b.separationRatio(5); b.alignmentDist(15); b.alignmentRatio(5); } } class MyBoid extends IBoid{ IVec prevPos; MyBoid(IVec p, IVec v){ super(p,v); } void update(){ //drawing line IVec curPos = pos().cp(); if(prevPos!=null){ IG.crv(prevPos, curPos).clr(clr()); } prevPos = curPos; } }
二つめの例では整列規則の力の係数を小さくしてあります。 その結果、同じ方向へ進もうとする力が弱くなる一方、 集まろうとする力と離れようとする力が相対的に大きくなるので 集合状態と離散状態の振動が発生しながら、やがて少しづつ同じ方向へ向き始めます。
import processing.opengl.*; import igeo.*; void setup(){ size(480, 360, IG.GL); IG.duration(300); for(int i=0; i < 100; i++){ MyBoid b = new MyBoid(IRand.pt(100,100,0), IRand.pt(-5,-5,0,20,20,0)); b.clr(IRand.clr()); b.cohesionDist(50); b.cohesionRatio(5); b.separationDist(30); b.separationRatio(5); b.alignmentDist(40); b.alignmentRatio(0.5); } } class MyBoid extends IBoid{ IVec prevPos; MyBoid(IVec p, IVec v){ super(p,v); } void update(){ //drawing line IVec curPos = pos().cp(); if(prevPos!=null){ IG.crv(prevPos, curPos).clr(clr()); } prevPos = curPos; } }
三つめの例では 分離規則の距離パラメータを大きくし、結束規則の距離を小さく設定しています。 その結果、分離規則が強く働きスウォーム・エージェントは分散して行きますが、 整列規則により小さなクラスターを作りながらそれぞれ同じ方向へ進んでいきます。
import processing.opengl.*; import igeo.*; void setup(){ size(480, 360, IG.GL); IG.duration(800); for(int i=0; i < 100; i++){ MyBoid b = new MyBoid(IRand.pt(100,100,0), IRand.pt(-5,-5,0,20,20,0)); b.clr(IRand.clr()); b.cohesionDist(30); b.cohesionRatio(5); b.separationDist(50); b.separationRatio(5); b.alignmentDist(40); b.alignmentRatio(5); } } class MyBoid extends IBoid{ IVec prevPos; MyBoid(IVec p, IVec v){ super(p,v); } void update(){ //drawing line IVec curPos = pos().cp(); if(prevPos!=null){ IG.crv(prevPos, curPos).clr(clr()); } prevPos = curPos; } }