前回の第13回「モーションブラーと弾むアニメーション」で、ひとまずお題の表現はできた(第13回コード2)。マウスポインタの動きに合わせて、つぎつぎにつくられるスプライトアニメーションのオブジェクトが落ちて弾み、それらの残像にモーションブラーがかかる(図1)。今回はこのアニメーションにふたつ手を加えたい。表題のとおり、ひとつはオブジェクトを使い回すようにすること。そしてもうひとつは、スプライトシートアニメーションの素材を差し替えてみる。
ガベージコレクションを減らす
第13回コード2「ステージ下端まで落ちたオブジェクトを弾ませる」では、Stage.stagemousemoveイベントのリスナー関数(addInstance())で、マウスを動かすたびにSpriteインスタンスがSprite.clone()メソッドによりひたすらつくられる。そのために、メモリが費やされたり、処理が重くなったりしないだろうか。
もちろん、インスタンスをつくりっ放しではない。アニメーションさせるTicker.tickイベントのリスナー関数(animate())では、ステージから見えなくなったオブジェクトは、Container.removeChildAt()メソッドでStageオブジェクトの表示リストから除いている。ただ、ことさらメモリから消すという操作はしていない。
実は、JavaScriptは、要らなくなったオブジェクトを自動的にメモリから消し去ってくれる。この仕組みを「ガベージコレクション」という[1]。自動的にゴミを探して吸い取ってくれるロボット掃除機のようだ。もっとも、楽なのはスクリプティングする人間の側だけで、掃除機(「ガベージコレクタ」と呼ばれる)にとっては手間のかかる仕事になる。
プログラマはメモリの扱いはとくに気にすることなく、JavaScriptのコードを書けばよい。けれど、JavaScriptはその処理の合間をぬって、メモリの片づけをしなければならない。なお、オブジェクトが要らなくなったということは、その参照が残っているかどうかで確かめている。
人気料理店のランチタイムを思い浮かべてもらうとよい。注文をとって料理を運ぶのが店員の仕事だ。しかし、食べ終えた客の皿を片づけなければつぎの客が入れられない。つまり、無駄なメモリが費やされる。片づけに追われると、料理を運ぶのが遅れがちになる。処理の負荷が上がってしまうのだ。
熱狂的なファンをもつある人気ラーメン店では、食べ終わった客が丼を片づけてテーブルを拭くという。それなら、店員の手間が省ける。このアイデアをいただこう。ただし、協力してもらうのはつぎの客だ。要らなくなったオブジェクトは、消さずにとっておく。オブジェクトが必要になったら、とっておいたオブジェクトがあればそれを使ってもらい、新たにはつくらない。
こうすれば、ガベージコレクタの手は煩わせずに済む。それだけではない。一般に、オブジェクトを新たにつくるより、すでにあるオブジェクトの設定をし直す方が負荷は低い。第13回コード2をそのように書直してみよう。
つくったオブジェクトを使い回す
まず、要らなくなったSpriteオブジェクトは、変数(sprites)に定めた配列に納めることにする。つぎに、SpriteインスタンスはSprite.clone()メソッドで直ちにつくらず、とっておいたオブジェクトがあったらそれを使い回す。そうしてオブジェクトを返す関数(getClone())は新たに定めよう。また、アニメーションの関数(animate())では、使い終えたSpriteインスタンスをStageオブジェクトの表示リストから除いて、使い回す配列に入れる。
オブジェクトを使い回す関数(getClone())は、とっておいたオブジェクトが配列(sprites)にあればそれを取り出し、なければそのときはSprite.clone()メソッドでつくって返す。ここまではよかろう。気をつけなければならないのは、使い回そうとして取出したオブジェクトがまだ前のままということだ。新たに用いるために、プロパティを設定し直さなければならない。Sprite.cloneProps()メソッドは、引数のSpriteオブジェクトのプロパティを参照するオブジェクトの値に改める。
以上の手を加えたスクリプト全体がつぎのコード1だ。jsdo.itのサンプルも添えた。アニメーションの表現そのものは第13回コード2と同じだ。また、残念ながら体感の動きもあまり差がない。ステージが小さいため、つくられるオブジェクトの数がさほど多くないからだろう。かといって、大きくすればモーションブラーの負荷が上がるので、やはり違いは感じにくい。たとえば、シューティングゲームの銃弾などに使えるテクニックとして覚えておくとよい。
Sprite.cloneProps()メソッドについて、ひとつ補っておく。リファレンスでメソッドを見ると、[protected]の表記がある(図3)。これは、一般的に使うものではなく、おもに内部的に用いられることを示す。そのため、説明は少ない。
リファレンスでこうした項目を探すときは、クラスのページ冒頭にある[protected]のチェックボックスをクリックしておくことも、併せて覚えておこう(図4)。
スプライトアニメーションを差替える
ペンギンが歩くアニメーションもよいが、このお題では少し鬱陶しい。素材を差し替えるだけで、表現の印象はかなり変わる。ダウンロードした「EaselJS-release_v0.7.0」ライブラリにはサンプル(「examples」)が納められており(図5上)、その素材(「assets」)としてひとつ小さなスプライトシート(「sparkle_21x23.png」)がある(図5下)。
このスプライトシートはきらめく星のアニメーションだ(図6。背景が透明だと見にくいので表示上青に変えた)。ファイル名に添えられた数字(「21x23」)がひとコマの大きさを表している。前掲コード1のスプライトシートをこちらに差し替えてみよう。
まず、初期設定の関数(initialize())で、読込むファイルの名前(パス)を書替える。スプライトシートアニメーションをつくる関数(createAnimation())では、スプライトシートを定め、SpriteSheet()コンストラクタに渡すオブジェクト(data)のプロパティが変わる。framesプロパティには、新たなスプライトシートの大きさを与える。フレームはただ頭から順に再生するだけなので、animationsプロパティは要らなくなる。
つぎに、アニメーション名(animationsのプロパティ)がなくなったので、再生はSprite.gotoAndPlay()でなくSprite.play()メソッドを用いる。
そして、Canvasの背景色はmidnightblue(#191970)にしよう。忘れていけないのは、Canvasにモーションブラーをかけて、フェードアウトさせる関数(fadeAndBlur())だ。残像をフェードアウトさせるために塗り重ねる色は、背景色(0x191970)に合わせなければならない。
前掲コード1についてこれらを書替えれば、新たなスプライトシートに差し替わる。スクリプト全体は、つぎのコード2のとおりだ。たちまち、ファンタスティックなアニメーションになった(図7)。jsdo.itのコードも掲げておく。
次回から、また新たなお題に取り組む。CreateJSの新バージョンに加わったメソッドを使ってみたい。今のところ、このような3次元表現を考えている。