概要
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 |