前回の第31回「位置座標の相互作用で弾力を表す」では、ふたつの点を棒でつなぎ、つぎのjsdo.itのコードのように放物線状に落としてみた。複数のオブジェクトをつなげた弾力のある動きが、座標の移動と位置関係や動く範囲を定めるだけの四則演算で表せた。今回は、点と棒の数を増やして多角形の動きをつくりたい。
点と棒で組上げた三角形を落とす
前回、点と棒のオブジェクトは、それぞれのクラス(VerletPointとVerletStick)からつくった。これらふたつのクラスは、今回そのまま用いる。多角形になっても、クラスの仕組みは変わらないということだ。参照しやすいように、それぞれのクラスを定めた第31回のコード3とコード6を以下に改めて掲げておく。
そして、前回のコード5「ふたつの点を棒でつないで放物線状に落とす」を、以下の抜書きのように書き替える。まず、棒(VerletStickオブジェクト)も複数になるので、変数(_sticks)の配列に納めることにした。新たに加えた関数(makeSticks())で点(VerletPointオブジェクト)の数に応じてインスタンスをつくり、この配列に加える。なお、forループを抜けてから、終わりの点と初めの点を棒で結ばないと多角形が閉じないことに注意したい。
そして、多角形の頂点数が自由に決められるように、点をつぎの関数により生成して、やはり変数(_points)の配列に納めることにした。始めは、三角形をつくる。
多角形の頂点をつくるこの関数は、-π/2ラジアンつまり時計の12時の角度(angle)から、引数の頂点数に応じた中心角(theta)と引数の半径(radius)で点のインスタンスをつくって、配列(_points)に加えている。この計算の考え方については、第15回「Matrix2Dクラスで座標を回す」の「星形を頂点座標と直線で描く」が参考になるだろう。
Ticker.tickイベントのリスナー関数(draw())は、扱う棒のオブジェクトが複数になったので、更新と描画はそれぞれ新たに設けた関数(updateSticks()とrenderSticks())の呼出しにより行う。どちらの関数も、棒が納められた配列(_sticks)からオブジェクト(stick)を取り出して、それぞれに応じたメソッド(update()とrender())を呼び出している。
これらの書き替えを終えたJavaScriptが以下のコード1だ。3つの点と棒で三角形をつくって、放物線状に落としている。アニメーションはつぎのjsdo.itのコードで確かめてほしい。三角形になると、Canvasの下端でも弾むような動きが見られる。
4つの点から四角形をつくって落とす
三角形ができたら、つぎは四角形だ。もっとも、前掲コード1の頂点をつくる関数(makePoints())には、頂点数を引数で与えられるようにした。したがって、初期化の関数(initialize())内から呼び出すとき渡す頂点数の引数値を書き替えるだけで、四角形がつくれる。つくれるけれども、残念ながらでき上がるアニメーションは情けない。Canvas下端に落ちたときかたちを保てず、四角形がつぶれてしまうのだ(図1)。
問題は、4つの点の隣同士の間にしか棒が加えられていないことだ。それでは、いくら4つの棒の長さを保っても、つぶれてしまう。姑息な(ことばの意味を取り違える人が増えているそうなので辞書にリンクした)解決法として、「筋交い」(すじかい)を加えることが考えられる(図2)。前掲コード1の棒をつくる関数(makeSticks())に、以下のように1行書込めば済む。
しかし、これで四角形はかたちが保てても、さらに頂点数を増やせば、また筋交いの棒をどこに加えるか考えるはめになる。それでは、せっかく頂点数を関数(makePoints())の引数で自由に決められるようにした意味がない。そこで、すべてのふたつの頂点の組を棒で結ぶことにする。棒をつくる関数(makeSticks())のfor文をつぎのように二重にして、外側のループで始めの点を選び、内側のループは選んだ点のインデックスより後の点を順に棒でつないでいる。
この修正を前掲コード1に加えたのが、以下のコード2だ。動きを確かめるためにjsdo.itのサンプルも掲げよう。これで、頂点をつくる関数(makePoints())の引数で任意の多角形が定められる。もちろん、落ちてもかたちはつぶれない。
パラメータを変えて試してみよう
コードの解説は今回はここまでとする。後は、ぜひ関数の引数や変数の値など、パラメータをいろいろ変えて試してみてほしい。たとえば、以下のように頂点をつくる関数(makePoints())の引数さえ変えれば、すぐに四角形が六角形になる。さらに、2点の組を棒でつなぐ関数(makeSticks())から呼出すVerletStick()コンストラクタの引数により、棒の固さも変えてみよう[1]。
前回の「ふたつの点を棒でつなぐ」でご説明したとおり、固さ0.5というのは、棒に定められた長さとの誤差をふたつの点に半分ずつ割振って、直ちに補正するということだ。ところが、頂点が6つになると、誤差が一発では直せない。それどころか、その歪みが別の点にしわ寄せされる。続くアニメーションでは、その誤差がまた別の点に飛火する。その繰返しで、六角形は頂点があちこち乱れて動き、かたちが定まらなくなってしまうのだ(図3)。
そのため、VerletStick()コンストラクタでは、棒の固さのプロパティ(elasticity)のデフォルト値をもっと小さな数値(0.2)にしてある。すると、1回のアニメーションでは、誤差は一気になくならない。イージングの動きのように、何度か繰返すうちに、目的の値にたどり着くようになる。その場合、残った誤差の分、棒は弾力をもって伸び縮みするように見える。
たとえば、パラメータを以下のように書き替えてみる。頂点数を8に増やして八角形とし、棒の固さはデフォルト値よりさらに柔らかく(0.05)した。そして、重力に当たる垂直方向の力(velocityY)を和らげて(0.05)いる。落ち方がゆっくりになるとともに、弾力は空気の甘いボールのようになる。前掲コード2にこの書替えを加えて、ご参考までにjsdo.itのサンプルとして以下に掲げた。
jsdo.itには[Fork]という便利な機能がある(図4)。このボタンを押すと、コードのコピーがつくられ、自由に手が加えられる。そして、もとのコードと書き替わった行は、[View Diff]というボタンで比較表示して確かめられる。ぜひ、これらの機能を使って、試してみてほしい。次回は、もう少し処理を加えたうえで、お題を仕上げたい。