前回の第23回「クラスのデザインとループ処理」では、EllipticMotionクラスのデザインを将来の拡張も考えて整理し、タイムラインのフレームアクションでは繰返し処理により複数のインスタンスを配置した(図1)。
今回の課題は、まずインスタンスの回転するスピードを、マウスポインタの位置によって変えることにする。つぎに、インスタンスの重ね順を、3次元風の表現に対応するよう修正しよう。
マウスポインタの位置に応じてインスタンスの回転スビードを変える
まずは、インスタンスが回転するスピードを、マウスポインタの位置によって変えるため、前回のスクリプト2で修正定義したEllipticMotionクラスにもう少し手を加えよう。
今のところ、インスタンスのアニメーションは、コンストラクタメソッドEllipticMotion()の本体で、インスタンスごとのDisplayObject.enterFrameイベント(定数Event.ENTER_FRAME)にリスナー関数rotate()を登録して処理している。
しかし、同じマウスポインタの座標値を、すべてのインスタンスからいちいち確かめるのは効率がよくない。また、のちにインスタンスの重ね順をコントロールする際には、それぞれの仮想の奥行きの値をメインタイムラインから調べる必要がある。
そうだとすれば、タイムラインに配置したインスタンスは、タイムラインのフレームアクションで管理することにしたい。そのうえで、EllipticMotionクラスのrotation()メソッドはタイムラインから呼出し、マウスポインタの座標に応じた回転角の値をその引数として渡そう。
Flashムービー(FLA)ファイルのフレームアクションとして作成した前回のスクリプト3には、タイムラインに配置するEllipticMotionインスタンスが納められるべき配列を新たに変数として宣言し、初期化する。そして、続くforループのコードブロック{}の中で、生成したインスタンスを表示リストに入れるとともに、この配列にも加えることにする。
これで、タイムラインのフレームアクションで、いつでも配列 (instances_array)からEllipticMotionインスタンスが取出せる。つまり、そのインスタンスを参照して EllipticMotionクラスのrotation()メソッドが呼出せるようになった。そこで、新たな関数rotate()をフレームアクションに定義して、これをタイムラインのDisplayObject.enterFrameイベント(定数Event.ENTER_FRAME)にリスナーとして登録しよう。
rotate()関数本体では、マウスポインタのステージ中央からの水平位置に応じた回転角(nSpeed)を求めたうえで、forループの処理によりEllipticMotionインスタンスを配列(instances_array)から順にすべて取出し、その回転角の値を各インスタンスのrotate()メソッドに渡して呼出している[1]。
以上ふたつの修正を加えたフレームアクションが、次のスクリプト1だ。
この変更にともなって、EllipticMotionクラスにも大きくふたつ修正を加える必要がある。
まずは、すでに触れたとおり、EllipticMotionクラスのコンストラクタメソッドにおけるDisplayObject.enterFrameイベント(定数Event.ENTER_FRAME)へのリスナー登録を削除することだ。
続いて、EllipticMotionクラスのrotate() メソッドが、引数として回転するスピードを受取るようにしなければならない。メソッドはもはやリスナー関数ではなくなるので、引数にイベントオブジェクトは受取らない。替わりに、フレームアクションから渡される回転角の数値を引数とするのである。そして、受取った数値はクラスのインスタンスプロパティ degreeに設定する。さらに、タイムラインから呼出すには、rotate()メソッドのアクセス制御の属性はpublicで指定する必要がある。
以上のふたつの修正を加えたのが、次のスクリプト2である。これでメソッドrotate()はリスナー関数ではなくなり、メインタイムラインから呼出せるようになった。なお、インスタンスプロパティとして宣言されていたフレーム当たりの回転角speedは、もはや不要なので削除した。
[ムービープレビュー]を試すと、マウスポインタのステージ中央からの水平位置によって、インスタンスの回転する方向と速さが変わる(図3)。
インスタンスの重ね順を管理する
インスタンスの重ね順を3次元風の表現に合わせて変えるには、まずインスタンスを正しい重ね順に並べ替える必要がある。メインタイムラインのフレームアクション(スクリプト1)では、EllipticMotionインスタンスを配列に納めた。幸いなことに、配列は並べ替えのメソッドArray.sort()を備えている。このメソッドは、以下のように引数なしで配列に対して呼出せる。trace()関数による[出力]の結果を見ると、エレメントの整数が昇順に並べ替えられている。
このArray.sort()メソッドには、ふたつ注意がある。1つは、並べ替えた結果の配列が返されるのではなく、参照した配列そのもののエレメントの順序が変わる。もう1つは、その並べ替えの順序はデフォルトでは、文字列のように頭からひと桁ずつ比較したうえで決められる。たとえば、つぎのスクリプトの結果を見てほしい。
Array.sort()メソッドには、引数として比較関数が指定できる。比較関数というのは、エレメントの並べ替えの順序を指定する関数だ。つまり、比較関数を定義すれば、配列エレメントの並べ順は自由に決められる。
Array.sort()メソッドに比較関数を指定すると、メソッドが配列エレメントを並べ替えるとき、エレメントをふたつずつ取出してはその関数には引数として渡す。比較関数は、そのふたつの引数のエレメントの大小、つまり並べ順を評価して、その結果を次表1のような数値として返す。
表1 比較関数の引数の大小と戻り値
引数の大小 | 戻り値 |
引数1 < 引数2 | -1 |
引数1 = 引数2 | 0 |
引数1 > 引数2 | 1 |
たとえば、数値のエレメントを数値として比べて並べ替えるには、比較関数(compare)をつぎのように定義したうえで、Array.sort()メソッドの引数に指定すればよい。trace()関数の[出力]結果を見ると、今度は数値の小さい順に並べ替えられたことが確かめられる。
ところで、Array.sort()メソッドの引数には、次表2のようなArray定数も指定できる。数値の小さい順に並べ替えるのであれば、Array.sort()メソッドの引数に定数Array.NUMERICを指定する方がたやすい[2]。
表2 Array.sort()メソッドに指定できるArrayクラスの定数
定数 | 説明 |
CASEINSENSITIVE | 英字の大文字小文字を区別せずに並べ替える。 |
DESCENDING | 並べ替えの順序を、降順(大きい順)に指定する。 |
NUMERIC | エレメントが数値の配列を、その値の大きさで順序づける。 |
RETURNINDEXEDARRAY | ターゲットの配列は変更せず、並べ替えた結果の整数インデックスをエレメントとした配列が返される。 |
UNIQUESORT | エレメントに重複がない配列のみを並べ替える。重複があれば、0を返す。 |
本題に戻って、EllipticMotionインスタンスの重ね順の処理を考えよう。インスタンスの仮想3次元の奥行きは、getIndexZ()メソッドで調べられた。したがって、配列 (instances_array)に入ったインスタンスの順序を、インスタンスに対するgetIndexZ()メソッドの戻り値で並べ替える。そのための比較関数compare()の定義は、次のようになる。
仮想3次元の奥行きの順に配列(instances_array)内のEllipticMotionインスタンスを並べ替えても、まだ実際の表示上の重ね順には反映されない。 EllipticMotionインスタンスの表示リスト内の順序を、この配列内の新たな並べ順に合わせる必要がある。
表示リスト内におけるインスタンスのインデックスの位置を変えるのは、DisplayObjectContainer.setChildIndex()メソッドだ。つぎのシンタックスで、親(DisplayObjectContainer)インスタンスの表示リストに含まれている子のDisplayObjectインスタンスを、指定したインデックスに移動する。
ここでメインタイムラインのフレームアクションに、新たな関数 setOrder()を定義する。この関数は、まず前掲の比較関数compare()で配列instances_array内の EllipticMotionインスタンスを、仮想3次元の奥行きの順に並べ替える。つぎに、EllipticMotionインスタンスの表示リスト内の順序を、配列instances_arrayのエレメントの順序に合わせてDisplayObjectContainer.setChildIndex()メソッドにより設定している。
以上、2つの関数を追加したフレームアクションが、次のスクリプト2だ。なお、関数setOrder()の呼出しは、DisplayObject.enterFrameイベント(定数Event.ENTER_FRAME)のリスナー関数rotate()の最後に加えている。
このまま[ムービープレビュー]を確かめると、 EllipticMotionクラスのgetIndexZ()が「アクセスできないメソッド」であるという[コンパイルエラー]が表示される(図4)。これは、フレームアクションに定義した関数compare()からインスタンスのgetIndexZ()メソッドを呼出している、次の2行のステートメントに対するエラーだ。
これは前掲スクリプト2でEllipticMotionクラスのrotate()メソッドについて、そのアクセス制御の属性を修正したのと同じ理由だ。スクリプト2では、getIndexZ()メソッドにはprivate属性が指定されている。しかし、タイムラインのフレームアクションからメソッドを呼出すには、アクセス制御の属性としてつぎのようにpublicを指定しなければならない。
スクリプト2に追加する修正はこの点のみなので、 EllipticMotionクラスの定義全体を再掲することは控えよう。サンプルファイルをダウンロードして確かめてほしい。今度は[ムービープレビュー]がエラーなく行え、インスタンスの重ね順もつねに仮想3次元の奥行きに合わせて表示される(図5)。
今回解説した次のサンプルファイルがダウンロードできます。