第18回から前回の第21回にかけて、カスタムクラスの定義の仕方を学んだ。今回は、クラスをMovieClipシンボルに対して定義してみたい。
MovieClipシンボルにクラスを定義すると、シンボルのタイムラインや配置されたエレメントなどのアセットが、そのクラスに関連づけられる。すると、MovieClipシンボルを[ライブラリ]からステージにドラッグ&ドロップするだけで、クラスに定義した動作が実現されることになる。コンポーネントのようなパーツをつくることも可能だ。
MovieClipシンボルに設定するスクリプトとしては、第17回「3D風に回転するアニメーション」で作成したフレームアクションを利用しよう(図1)。サンプルファイルは、第17回の3ページからダウンロードできる。
MovieClipシンボルに設定するクラス
3D風に回転させるMovieClipシンボルに定義するカスタムクラスは、楕円運動を意味するEllipticMotionと名づけよう。しかし、いきなりアニメーションの動作を書き始める前に、MovieClipシンボルに設定するクラス定義が最低限満たすべきスタイルを確かめておきたい。
第1に、MovieClipシンボルに設定するクラスには、アクセス制御の属性としてpublicを指定しなければならない[1]。MovieClipインスタンスは、配置されたタイムライン上で操作される。したがって、タイムラインからのアクセスを許す必要があるからだ。
第2に、MovieClipシンボルに設定するクラスには、基本的にextends定義キーワードでMovieClipクラスを継承させる。「継承」とは、「スーパークラスの機能(プロパティやメソッド)がサブクラスで使えるプログラミングの仕組み」だった(第9回「座標の天動説と地動説」「インスタンスとマウスポインタの座標」)。
MovieClipシンボルにカスタムクラスを設定すると、そのインスタンスはカスタムクラスのオブジェクトとなり、そのままではMovieClipクラスのプロパティやメソッドがまったく使えない。たとえば、xy座標を動かすことさえできないのだ。よって、MovieClipクラス(およびそのスーパークラス)の機能を使えるようにするためには、その継承が必要になる。
第3は、import宣言だ。もっとも、このステートメントの記述が必要なのは、MovieClipシンボルに設定するクラスにかぎったことではない。ActionScriptに定義済みのクラスのほとんどは、パッケージに属している。たとえば、MovieClipクラスなら、[ActionScript 3.0言語およびコンポーネントリファレンス]の説明冒頭の記載から、パッケージはflash.displayだとわかる(図2)。
すると、クラス名の前にパッケージを添えたflash.display.MovieClipがクラスの正式な名前となる。クラスの正式な名前は「完全修飾クラス名」と呼ばれる。カスタムクラスの定義でMovieClipクラスを用いるときには、importディレクティブで完全修飾クラス名を宣言する必要があるのだ。
では、まずは空のカスタムクラスEllipticMotionを定義して、MovieClipシンボルにそのクラスを設定してみよう。EllipticMotionクラスの定義は、以下のようになる。コンストラクタメソッド内には、確認のためにtrace()ステートメントを記述して、クラスが設定されたインスタンスのxy座標値とインスタンス自身の参照を[出力]することにした。なお、クラス定義ファイルは、それを設定するMovieClipシンボルが[ライブラリ]に納められているFlashムービー(FLA)ファイルと同階層に保存する。
このままではまだクラスEllipticMotionとMovieClipシンボルの間には何の関わりもない。このふたつを関連づけるには、[ライブラリ]のMovieClipシンボルを選び、[シンボルプロパティ]のダイアログボックスで[クラス]フィールドにカスタムクラス名"EllipticMotion"を入力する必要がある[2]。
クラスの設定されたMovieClipシンボルのインスタンスは、スクリプトで動的につくることにしよう。ステージには何も置かず、MovieClipシンボルが納められたFlashムービー(FLA)ファイルに以下のフレームアクションを記述する。このステートメントは、クラスEllipticMotionのコンストラクタメソッドを呼出し、インスタンスを生成している。
[ムービープレビュー]を行うと、コンストラクタ内でtrace()関数に渡したインスタンスのDisplayObject.xとDisplayObject.yプロパティ値、ならびにインスタンスのthis参照が[出力]パネルに表示される。しかし、インスタンスがステージに表れない(図4)。
これはインスタンスを作成しただけではまだそれはメモリ上の存在で、いずれのタイムラインにも属していないからだ。Flash Playerのステージは、Stageインスタンスを頂点としたインスタンスのツリー構造でできあがっている(図5)。ステージ上に表示されるためには、いずれかのインスタンスの子として、このツリー構造に加わらなければならない。
そのためには、親とすべきインスタンスを参照して、DisplayObjectContainer.addChild()メソッドを呼出し、その引数に子として追加したいインスタンスを渡す。なお、そうして加えられた子インスタンスは、親インスタンスの「表示リスト」という容れ物に納められる[3]。
では、フレームアクションにDisplayObjectContainer.addChild()メソッドの呼出しをステートメントとして加える。ターゲットはスクリプトを記述しているメインタイムラインなので、参照は省略した。[ムービープレビュー] してみると、今度はインスタンスがステージ上に表示される(図6)。なお、インスタンスのxy座標も、ステージ中央に設定した。
フレームアクションをクラスに移行する
これで必要な知識は学んだので、第17回「3D風に回転するアニメーション」で作成したスクリプト3を、フレームアクションからカスタムクラスの定義に書替えてみよう。フレームアクションに最小限の修正をしてクラス定義に直したのが以下のスクリプト1だ。
第1に、import宣言は、MovieClipクラスだけでなく、Event(flash.events.Event)およびBlurFilter(flash.filters.BlurFilter)クラスについても必要になる。第2に、クラス定義本体に記述するのはvar宣言したプロパティとfunctionで定義するメソッドのみなので、イベントリスナーを設定するステートメントは、コンストラクタメソッド内に置いた。第3に、アクセス制御の属性は、コンストラクタメソッドをpublicとした以外は、すべてprivateで指定した[4]。
上記スクリプト1のクラスEllipticMotionを設定したMovieClipインスタンスは、まずはステージに予め置いて試してみよう(ダウンロードしたサンプルを利用している人は、フレームアクションは削除しておくことを忘れないように)。[ムービープレビュー]を見ると、フレームアクションと同じように、インスタンスが3D風に回転する(図7)。
それではつぎに、MovieClipインスタンスをステージからは消して、スクリプトで動的に作成してみる。インスタンスをコンストラクタメソッドで生成し、DisplayObjectContainer.addChild()メソッドに渡せばよかった。
ところが、以下のランタイムエラーが発生してしまう。このエラーは、オブジェクトを参照してプロパティにアクセスしようとしているステートメントで、実際にはオブジェクトが存在しない場合に起こる[5]。しかも、たちの悪いことに、何行目のステートメントが問題なのかを具体的に知らせはてくれない。
結論からいうと、原因はクラスEllipticMotionにおけるプロパティのvar宣言で、DisplayObject.stageプロパティにアクセスしている点にある(図8)。[ヘルプ]の[ActionScript 3.0言語およびコンポーネントリファレンス]で「DisplayObject.stageプロパティ」の項を見ると、つぎのような注意書きがある。
前掲のフレームアクションで、EllipticMotionクラスのコンストラクタを呼出してインスタンスを作成しただけでは、まだStageオブジェクトを頂点とするツリー構造の表示リスト(前掲図5)には加えられていない。その状態では、インスタンスのDisplayObject.stageプロパティに値がなく(null)、Stageインスタンスにアクセスできないのだ。
対処の方法は、いくつか考えられる。今回は、 EllipticMotionクラスに宣言した楕円軌道の中心座標をもつインスタンスプロパティの値は、クラスの外つまりフレームアクションから渡すことにしよう。具体的には、楕円軌道の中心座標とxy半径の値をコンストラクタメソッドの引数とする。さらに、複数のインスタンスを生成して配置したいので、角度の初期値も引数に加えよう。
中心や半径の値は、xとyの値の組になっている。これらをばらばらに指定すると引数の数が増えてしまうし、ひと組として扱うべきものはひとつの値で指定できると便利だ。そこで、xy座標の値をひとつのインスタンスとして扱うPointクラスを利用しよう。Pointクラスのコンストラクタメソッドには、xとyの値を引数に渡す。そして、それぞれの値には、Point.xおよびPoint.yプロパティとしてアクセスすればよい。
以上の修正をクラスEllipticMotionに加えたのが、以下のスクリプト2だ。第1に、Pointクラスの完全修飾クラス名flash.geom.Pointをimport宣言した。第2に、楕円軌道の中心座標およびxy半径を納めるプロパティ値は、コンストラクタメソッドに渡されるので、var宣言で初期値は与えない。第3に、コンストラクタが受取る角度と中心座標、およびxy半径の引数を、メソッド本体でブロパティ値に設定している。後のふたつはPointインスタンスで渡されるので、xyそれぞれの値はPoint.xおよびPoint.yプロパティとして取出した。なお、この機会にプロパティとメソッドの名前から接頭辞は除いた。
これで、EllipticMotionクラスのコンストラクタに角度と中心座標、およびxy半径を引数で指定して、インスタンスを配置することができる。試しに、4つのインスタンスを生成し、90度間隔で回転させてみよう。フレームアクションは、つぎのようになる。
前述のとおり、楕円軌道の中心座標とxy半径は、 EllipticMotionクラスのコンストラクタにPointインスタンスで渡す。[ムービープレビュー]を確かめると、4つのインスタンスが90度間隔で配置され、楕円軌道を描いてアニメーションする(図9)。前記フレームアクションでは、4つのインスタンスの生成と表示リストへの追加が、似たようなステートメントをただ羅列した処理になっている。このような繰返し処理のすっきりした書き方や、EllipticMotionクラスのさらなるリファインについては、次回に譲ろう。
今回解説した次のサンプルファイルがダウンロードできます。