前回の第37回「インスタンスの回転と重ね順」は、ふたつの面を前後に置いて、y軸で水平に回した。今回はさらに2面増やし、矩形の画像で四方を囲んで、最後は上下左右に回してみたい(図1)。前回に続き、面の重ね順をどう決めるかが課題だ。
z座標値を調べる仕組みづくり
4面を水平に回すだけなら、前回のスクリプト2と同じように、面が納められた容れ物のSpiteインスタンスの回転角で並べ替えることもできる。しかし、垂直にも回すとなると、ことはそう単純ではない。そこで、4つの面それぞれのz座標で重ね順を決めることにしよう。前回のスクリプト2に手を加えていくことにする。
まず、面のどの位置の座標を調べるかが問題となる。[ライブラリ]のビットマップ(BitmapDataインスタンス)は、Bitmapオブジェクトの基準点が左上角になるように配置された(第37回図2再掲の位置座標参照)。しかし、左上角のz座標値を比べたのでは、それぞれの面の前後はわからない。
比べるなら、それぞれの面の中央の座標がよいだろう。容れ物のSpriteインスタンスの基準点は四方を囲む面の中心なので、各面とxまたはz軸との交点で決めるということだ(図2)。ところが、BitmapDataクラスには位置座標のプロパティがない。つまり、面の左上角がBitmapオブジェクトの基準点であることは動かせない。
そのため、4つの面のBitmapオブジェクトは、それぞれSpriteインスタンスで包むことにする。そして、入れ子のBitmapオブジェクトの位置をずらすことによって、親Spriteインスタンスの基準点を面の中心に定める(図3)。そうすれば、面のSpriteインスタンスのz座標でそれらの前後が決められる。これで面のz座標値を調べる仕組みが整った。
面をつくって返す関数の修正
つぎは、前回のスクリプト2に具体的な手を加えていく。第1に、面のBitmapオブジェクトをつくって返していた関数xCreateFace()だ。これを、Bitmapオブジェクトが入れ子になったSpriteインスタンスで返すよう修正する。引数の宣言はそのままだ。ただし、Spriteインスタンスの基準点が面の中央になるので、関数を呼出すときに渡す座標値がつぎのように変わる。なお、面のSpriteインスタンスを納める変数名も改めた。
関数xCreateFace()には、以下のように手を加えた。戻り値のデータ型をSpriteで指定し、面のビットマップが納められたBitmapオブジェクトは、新たにつくるSpriteインスタンスで包む。Bitmapオブジェクトの位置は、面の中央にSpriteインスタンスの基準点がくるように、左上にずらす。そのうえで、引数値によりSpriteインスタンスの位置と回転角を定めた。
これで、前回のスクリプト2とムービーの見た目は変わらないものの、ふたつの面のSpriteオブジェクトがつくられ、容れ物の親Spriteインスタンス内に 配置される。この段階で[ムービープレビュー]を確かめる場合は、スクリプト中の面のSpriteオブジェクトの変数名を直す必要がある。また、面の重ね順を整える関数xSetOrder()は、前項で述べたとおり処理を大幅に書替えるので、一旦本体をコメントアウトしておこう。
面のz座標値で重ね順を入替える関数
第2に手を加える関数xSetOrder()は、面のSpriteインスタンスのz座標値により重ね順を入替えるよう定義し直す。ところが、ここで問題が生じる。面のインスタンスは親Spriteインスタンスに入れ子になっており、回すのはこの容れ物の親インスタンスだった。ということは、面のインスタンスの座標そのものは、1ピクセルたりとも動かないのだ。
このようなとき、インスタンスの基準とする座標空間を親インスタンス以外に変換することができる。それが、Transform.getRelativeMatrix3D()メソッドだ。インスタンスのDisplayObject.transformプロパティに対してこのメソッドを呼出すと、インスタンスのもつTransform.matrix3Dプロパティの値を、メソッドの引数に渡したDisplayObjectインスタンスから見た値に計算し直してくれる。
Transform.getRelativeMatrix3D()メソッドは、Matrix3Dオブジェクトを返す。その座標値が欲しいときは、Matrix3D.positionプロパティを調べればよい。xyzの各プロパティをもったVector3Dオブジェクトが得られる。z座標値はVector3D.zプロパティだ。
面のインスタンスをz座標値順に並べ替えるには、第24回「インスタンスの管理と配列の並べ替え」の「インスタンスの重ね順を管理する」で用いたArray.sort()メソッドが使える。そのためには、容れ物の親Spriteインスタンスから、面の子インスタンスを取出して配列に納めなければならない。親インスタンスの表示リストから、指定したインデックス番号の子インスタンスを取出すのはDisplayObjectContainer.getChildAt()メソッドだ。子インスタンスの数は、DisplayObjectContainer.numChildrenプロパティで調べられた。
定義し直した関数xSetOrder()は、以下のとおりだ。まず、容れ物である親インスタンスの表示リストから面の子インスタンスをすべて取出して、それらを新たな配列(faces_array)に納める[1]。つぎに、その配列エレメントとなった子インスタンスをArray.sort()メソッドで並べ替える。引数には比較関数(compare())を渡して、インスタンスのz座標値の順にする。
比較関数compare()は、渡される子インスタンスのメインタイムライン(this)から見たMatrix3DオブジェクトをTransform.getRelativeMatrix3D()メソッドにより求めて、Matrix3D.positionプロパティでz座標値を調べる。
ただし、z座標値は奥向きが正で、表示リスト内のインデックスは手前ほど値が大きい。比較関数からの戻り値を決めるとき、注意する必要がある。
前回のスクリプト2に上述の修正を加えたのが、以下のスクリプト1だ。この段階でのムービーの動きは、前回のスクリプト2と変わらない(第37回図6再掲)。しかし、中身は面のインスタンスの入れ子を使って、その重ね順をz座標により定めている。面を増やしたり、垂直回転を加える準備が整った。
4つの面を上下左右に回す
それでは、面はあとふたつ増やして四方を囲む4面とし、垂直の回転も加えて上下左右に回してみよう。実は、もう新たに学ぶべきことは何もない。ただ、処理を追加するだけだ。まず、面のSpriteインスタンスをふたつ増やし、それらを容れ物の親Spriteインスタンスに加える。
つぎに、回転のアニメーションをさせるリスナー関数xRotate()には、垂直に回す処理が加わる。これは、第36回「Matrix3Dクラスの後から加える変換」のスクリプト1とまったく同じだ。
前掲スクリプト1にこれらの修正を加えたのが、つぎのスクリプト2だ。[ムービープレビュー]を確かめると、四方を囲む4つの面がマウスポインタの位置に応じて上下左右に回る(前掲図1)。
2面から4面に増やし、垂直回転を加える作業はあっけなかった。この実習からわかるように、まず仕組みや構成を初めによく考えておくことが大切だ。そしてつぎに、1面から2面、そして4面というように、簡単なつくりから、順を追って複雑にすることである。
フォーラムなどで、いきなり6面体を上下左右に回そうとして、どうしたらいいかわかりません、というような質問がある。しかも往々にして、スクリプトはどこかのサンプルのコピー&ペーストで、中身がわかっていなかったりする。遠回りなようでも、理解と試行をひとつひとつ着実に積重ねていくことが王道だろう。
3次元空間の扱いはこの後インスタンスというモノでなく、座標を使ったメモリ上の処理に移る。しかしその前に旬のお題として、Flash Professional CS5から加わったText Layout Frameworkによる新しいテキストの扱いを解説しよう。
今回解説した次のサンプルファイルがダウンロードできます。