概要
Boids 2.1では、いよいよBoidsのルールの一つ「分離(Separation)」を導入する。分離行動の考え方は、ここでは以下の通りとした。
- 視野内に個体が存在するとき、最も距離が近い個体を選択する
- その直近個体と反対方向に、自身の進行方向を変化させる
分離のアルゴリズム
視野
個体は自身の進行方向の左右90度以内、180度の視野角を持ち、その視野角内で一定距離以内の個体に反応するものとする。
視野内外の判定の概念は下図の通りで、個体Pは視野内と判定され、個体Qは視野外として無視される。
視野内外判定の手順は以下の通り。
- 前後判定の方法により、視野角内の個体があればそれを選定
- その個体についてならば次へ進む
- その個体が最も近いなら入れ替え
回避動作
回避対象が自身の進路の右にいるなら左に、進路の左にいるなら右に、方向ベクトルの角度を変更する。進路変更の角度は予め設定しておく。
回避動作の手順は以下の通り。
- 左右判定の方法により、回避対象が進行方向の左右どちらにあるかを判定
- 回避対象が進行方向の左にあるなら右へ、右にあるなら左へ方向を少し変化させる。
ここで方向ベクトルの変化量θは予め設定しておき、これに対応するcos、sinによる回転行列を適用して方向を変える。cos、sinの値は、できるだけ計算量が少なくなるようにする。
コード内容
初期パラメータ定義
HTMLで定義する初期パラメータに、分離に関する以下の変数を追加する。
- 分離に関する視野の深さ(
BD21_VIEW_FIELD_LENGTH
) - 回避行動の進行方向の変化(
BD21_SEPARATION_ANGLE
)
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<script> //<![CDATA[ BD21_V_MAX = 100; BD21_CREATURE_SIZE = 10; BD21_WALL_DETECTION_LENGTH = 100; BD21_WALL_REPULSION_PARAM = 16; // Boids 2.1で追加:視野の深さ BD21_VIEW_FIELD_LENGTH = 100; // Boids 2.1で追加:分離行動の速度の向きを変更する角 BD21_SEPARATION_ANGLE = 10 * Math.PI / 180; BD21_INTERVAL_SEC = 0.05; //]]> </script> |
クラス変数
初期パラメータの保存のため、Creature
クラスのクラス変数を追加する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
class Creature extends taustation_geom2d.MovingAgent # private staticなクラス・プロパティ # 最大速度 @_vMax: 100 # 描画の基本となるサイズ @_drawSize: 8 # 壁の衝突回避を認識する距離 @_wallDetectionLength: 100 # 反発力パラメータ @_wallRepulsionParam: 4 # 視野の長さ(奥行) @_viewFieldLength: 100 # 分離行動の回避計算用 @_separationAngle: 0 |
パラメータのセッター
Controler
クラスに、Creature
クラス変数へのセッターを定義する。
1 2 3 4 5 6 7 8 9 10 11 12 |
class Controler ..... # 定数として扱うクラス変数を設定するメソッド群 setVMax: (vMax) -> Creature._vMax = vMax setCreatureSize: (size) -> Creature._drawSize = size setWallDetectionLength: (pix) -> Creature._wallDetectionLength = pix setWallRepulsionParam: (param) -> Creature._wallRepulsionParam = param setViewFieldLength: (length) -> Creature._viewFieldLength = length setSeparationAngle: (angle) -> Creature._separationAngle = angle setIntervalSec: (@interval_sec) -> |
パラメータのセット
HTMLで定義されたパラメータをControler
オブジェクトのセッターによってセットする。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
jQuery ($) -> # Controlerを作成し、canvasオブジェクトを渡す canvas = $("#Boids21_canvas")[0] controler = new Boids21.Controler(canvas) # 固定パラメータのセット # パラメータの値は、Javascript形式でHTMLに埋め込まれている想定 controler.setVMax(BD21_V_MAX) controler.setCreatureSize(BD21_CREATURE_SIZE) controler.setWallDetectionLength(BD21_WALL_DETECTION_LENGTH) controler.setWallRepulsionParam(BD21_WALL_REPULSION_PARAM) controler.setViewFieldLength(BD21_VIEW_FIELD_LENGTH) controler.setSeparationAngle(BD21_SEPARATION_ANGLE) controler.setIntervalSec(BD21_INTERVAL_SEC) # Generateボタンが押されたとき、指定された個体数で群をつくる $("#boids21_generate").click -> pop = $("#boids21_population").val() if pop < 1 pop = 1 $("#boids21_population").val(pop) controler.generate(pop) # Start/Stopボタンが押されたとき、アニメーションの動作を切り替える $("#boids21_start_and_stop").click -> controler.startAndStop() |
Ceatureクラスの変更 – separation()メソッドの追加
Creature
クラスのmove()
メソッドがseparation()
メソッドを呼び出すようにする。
1 2 3 4 5 6 7 8 9 10 11 12 |
class Creature extends MovingAgent ..... move: (interval_sec) -> # 壁を避ける @wallAvoidance() # 分離行動(separation) @separation() ..... |
separation()
メソッドを新たに定義する。
- 速度の方向を変化させる回転行列のcos、sinに対応したローカル変数
cs
、sn
を定義している - 最も近い個体への参照が
nearestCreature
プロパティに保存されるが、視野内に個体が存在しないときはnullとなる - 速度の回転方向は、右がマイナス方向、左がプラス方向となることに注意
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
# Boids2.1 # 分離行動(separation) # 前方視角180度内で一定距離内の視野の中で、最も近い個体を避ける # 相手方の速度の向きには関係なく、相手側と反対方向に速度の向きをを少し変更する。 separation: -> # 回避行動の方向転換計算用cos/sin cs = Math.cos(Creature._separationAngle) sn = Math.sin(Creature._separationAngle) # 自身に最も近い個体 @nearestCreature = null # 自分以外の群のすべての個体についてサーチ for other in @cluster.creatures if other != this # その個体が自分の前方にいるなら if other.isInFrontOf(this) d = Vector.distance(this.pos, other.pos) # その個体が視野の中にいるなら if d <= Creature._viewFieldLength # 既に個体がセットされていて、今回の個体の方が近ければ入れ替え # 最初に認識した個体ならそのままセット if @nearestCreature? if d <= Vector.distance(@nearestCreature.pos, this.pos) @nearestCreature = other else @nearestCreature = other # 近くの個体が視野内に存在するなら回避計算 if @nearestCreature? # 相手が自分の右にいるなら左へ、自分の左にいるなら右へ向きを変更 if @nearestCreature.isOnTheRightOf(this) vx = @v.x * cs - @v.y * sn vy = @v.x * sn + @v.y * cs else vx = @v.x * cs + @v.y * sn vy = -@v.x * sn + @v.y * cs @v.x = vx @v.y = vy |