概要
Boids 3.xシリーズで敵(Enemy)を導入する準備として、クラス構成の改良などを行った。
Boids 2.xまではBoidだけだった生物の種類に、新たにEnemyという別の種類が加わるが、どちらについても以下の特徴は共通している。
- 複数個体を
Cluster
オブジェクトにまとめて扱われる - Field内を移動する
- 壁との衝突を避ける
そこで、これらの特徴に関する部分をCreature
クラスから抜き出したものを新たにCreature
としてテンプレート化し、Boid特有の動作についてはCreature
クラスを継承したBoidCreature
で実装した。
このほか、BoidとEnemy2つの群を扱うことから、Cluster
で処理していた画面の消去処理を外に出すなどの変更を行っている。
Creatureクラスのテンプレート化
Boids 2.3
まず、元となったBoids 2.3のコードの構成を確認する。壁の回避、分離、結合、整列に関するメソッドを定義し、これらをmove()
メソッドで呼び出している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
class Creature extends MovingAgent constructor: (cluster, field) -> draw: (ctx) -> move: (interval_sec) -> @wallAvoidance() @separation() @cohesion() @alignment() .... wallAvoidance: -> separation: -> cohesion: -> alignment: -> |
Boids 3.0
Boids 3.0では2.3のCreature
クラスをCreature
とBoidCreature
に分け、Creature
クラスにはBoidとEnemyに共通する動作を書き、Boidに特有の動作についてはBoidCreature
のメソッドで実装した。
Creatureクラス
Creature
クラスは以下のメソッドを持っている。
- wallAvoidance()
- 壁との衝突を回避するメソッド。
- specificBehavior()
- このクラスを継承するクラスごとに特有の動作を記述するためのメソッド。抽象クラスのabstractメソッドに相当し、継承したクラスにおいてその内容が実装される。
- determinePosition()
- 上記2つのメソッドで計算された位置と速度から、次の時間ステップにおける位置を決定するメソッド。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class Creature extends MovingAgent constructor: (field) -> move: (intervalInSec) -> @wallAvoidance(intervalInSec) @specificBehavior(intervalInSec) @determinePosition(intervalInSec) wallAvoidance: (intervalInSec) -> specificBehavior: (intervalInSec) -> determinePosition: (intervalInSec) -> |
BoidCreatureクラス
BoidCreature
クラスはCreature
クラスを継承し、specificBehavior
()メソッドを実装する。
BoidCreature
クラスで分離、結合、整列に関するメソッドを定義し、specificBehavior()
メソッドでそれらを呼び出している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
class BoidCreature extends Creature constructor: (boidCluster, enemyCluster, field) -> draw: (ctx) -> specificBehavior: (intervalInSec) -> @separation() @cohesion() @alignment() separation: -> cohesion: -> alignment: -> |
描画処理の変更
Boid 3.xでは、BoidとEnemyという2つの生物をそれぞれ別のCluster
オブジェクトに登録して扱う。Boids 2.3までは、アニメーションフレーム再描画のための画面消去処理をCluster
クラスのdraw()
メソッドで行っていたが、このままだとBoid、Enemyそれぞれの描画の際に画像が消去されて、どちらか一方が画面に表示されなくなる。
1 2 3 4 5 6 7 8 9 |
class Cluster .... draw: -> @context.fillStyle = "#e0e0e0" @context.fillRect(0, 0, @canvas.width, @canvas.height) for creature in @creatures creature.draw(@context) return |
そこで、画面消去処理をCluster
ではなくControler
のgenerate()
メソッドとstartAndStop()
メソッドに置いた。
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 |
class Controler .... generate: (boidPopulation) -> @boidCluster.clearCreatures() for i in [1..boidPopulation] cr = @createBoidCreature() @boidCluster.addCreature(cr) # 個体発生後に画面をクリアして描画 @context.fillStyle = "#000080" @context.fillRect(0, 0, @canvas.width, @canvas.height) @boidCluster.draw(@context) # アニメーションの開始/停止の切り替え startAndStop: () -> if @isRunning clearInterval(@timer) @isRunning = false else @timer = setInterval => # 移動・再描画前に画面をクリア @context.fillStyle = "#000080" @context.fillRect(0, 0, @canvas.width, @canvas.height) @boidCluster.move(@intervalInSec) @boidCluster.draw(@context) return , @intervalInSec * 1000 @isRunning = true return |