今回のお題は、ランダムに動く粒子の間に相互作用を加えたアニメーションだ。ランダムな動きにオブジェクト同士のインタラクションを交えると、有機的なアニメーションになってくる。Jared Tarbell氏がかつて「Node Garden」という作品を発表された。Keith Peters氏はそのノード間にバネ運動を加えて、著書『ActionScript 3.0アニメーション』で解説している。本稿はその表現をCreateJSで書いてみようと思う。
ステージのランダムな位置に、ランダムな方向と速さで動く小さなオブジェクトをたくさん置く。それらのオブジェクトの間に引っぱり合うバネのような力を与え、さらに互いを線で結ぶ(図1)。できあがりのコードは、jsdo.itに上げてある(例によって後で書き直すかもしれないが)。
ランダムに置いたオブジェクトをランダムな向きと速さで動かす
取りあえず、オブジェクトの互いの間にかかる力は脇に置いておこう。ステージ上のランダムなところから、ランダムな向きと速さで等速に動かす。まず、予め定めた数のオブジェクトをステージ領域のランダムな位置につくるのが、次のscript要素だ。
body要素のonload属性で呼出す初期化の関数(initialize())は、for文であらかじめ定めた数(ballCount)のインスタンス(ball)をステージ領域のランダムな位置に置く。円形のShapeインスタンスをつくる関数(createBall())は別に定め、引数に半径と色、およびxy座標を渡している。また、インスタンスのShape.graphicsプロパティに円を描くのも別関数(drawBall())とした。引数には、Graphicsオブジェクトと半径、および色を渡す。
つぎに、オブジェクトをランダムな向きと速さで動かす。つくったオブジェクトは変数の配列(balls)に入れ、Ticker.tickイベントのリスナー関数(move())でアニメーションさせる。xy軸方向に進む向きと速さは、インスタンスごとに変えるので、速度のプロパティ(velocityXとvelocityY)としてもたせる。そのため、インスタンスをつくる関数(createBall())の引数に、これらの値を加えた。
Ticker.tickイベントのリスナーに定めたアニメーションの関数(move())は、オブジェクト(ball)の速度のプロパティ値(velocityXとvelocityY)をそれぞれの座標に加えて動かす。ただし、座標値がステージの端を超えたら反対側の端に戻るよう、周期化する関数(roll())で補正している。
これらのsctipt要素をまとめたのが、次のコード1だ。ステージにランダムに散らばったオブジェクトは、ランダムな向きと方向に等速で動き続ける(図2)。オブジェクトがステージの外に出ると、反対側の端から表れる。オブジェクトの間には、とくに相互作用はない。
オブジェクト同士の間にバネのような引合う力を加える
いよいよ、オブジェクト同士がバネでつながれているように、互いに引合う力を加えよう。そのためには、バネにかかる力の性質を知らなければならない。もっとも、その法則はきわめて単純だ。バネの力は伸びに比例する。これを「フックの法則」と呼ぶ。
力は物体の加速度となる。したがって、ふたつのオブジェクトの間の距離に比例した加速度を、それぞれのオブジェクトの速度(velocityXとvelocityY)に加えればよい。そのための関数(spring())を次のように定めた。引数は、加速度を与えるふたつのオブジェクトだ。アニメーションの関数(move())からfor文で、すべてのオブジェクトの組合わせを引数にして呼出す。
速度(velocityXとvelocityY)と同じく、加速度(accelXとaccelY)もxy座標に分けて計算できる。比例係数はグローバルな変数(ratio)に宣言した。オブジェクト同士が引合う力なので、加速度の正負はふたつのオブジェクトで逆にしている。
加速度はオブジェクト同士の総当たりで決まるので、互いに打ち消したり、強めたりする。ただ全体としては、ひとつの固まりに集まっては、離れることの繰返しになる(図3)。手を加えたJavaScriptコードの全体は、次のコード2のとおりだ。
この動きは、それなりに面白い。ただ、しばらく放っておくと、多くのオブジェクトが激しく行き来するようになる。それは、仲間から外れたオブジェクトが、より大きな加速度を得るからだ。ひとたび仲間外れになると、集まりには戻りにくくなる。そうして、次第にはぐれ者が増えてしまうのだ。
そこで次回は、はぐれ者をむやみに増やさないよう、加速度が加わる範囲をかぎる。そして、オブジェクト同士を線で結びつけてみよう。
まだ未完成ではあるが、ここまでのコードを参考までにjsdo.itに掲げておく。