3次元空間における(平行)移動や回転および伸縮は、Matrix3Dクラスを使って行うこともできる。Matrixというのは、数学の行列を意味する(あの映画のことではない)。3次元座標を変換するために、内部的に行列による演算が行われるためにこの呼び名がつけられた。もっとも、基本的な処理であれば、私たちがその計算を行う必要はない。座標変換の性質と特徴を知っておけば足りる[1]。
DisplayObjectインスタンスのもつMatrix3Dオブジェクトを操作する
DisplayObjectインスタンスの3次元空間における座標変換の情報をもつMatrix3Dオブジェクトは、PerspectiveProjectionオブジェクトと同じくTransformクラスのプロパティで、Transform.matrix3Dから得る。そして、Transformオブジェクトの参照は、DisplayObject.transformプロパティがもっていた(第33回図6、再掲)。
DisplayObjectインスタンスに平行移動や回転あるいは伸縮の変換を加えるには、Matrix3Dオブジェクトに対してそれぞれMatrix3D.prependTranslation(), Matrix3D.prependRotation(), Matrix3D.prependScale()といったメソッドが用いられる。なお、各メソッドの頭についているprependという語の意味は後で説明する。
それでは、タイムラインに置いたMovieClipインスタンスmy_mcを、y軸で水平に45度回転してみよう。Matrix3D.prependRotation()メソッドの第1引数には、回転する角度を度数で渡す。そして、第2引数の回転軸は、Vector3Dクラスの3つの定数Vector3D.X_AXIS, Vector3D.Y_AXIS, Vector3D.Z_AXISから指定する。
ところが、このフレームアクションを[ムービープレビュー]で試すと、以下のようなランタイムエラー#1009が示される。これは「オブジェクトを参照してプロパティにアクセスしようとしているステートメントで、実際にはオブジェクトが存在しない場合に起こる」エラーだった(第22回「MovieClipシンボルにクラスを定義する」の「フレームアクションをクラスに移行する」)。
実際、MovieClipインスタンスのDisplayObject.transformプロパティから取出したTransform.matrix3Dプロパティの値をtrace()関数で[出力]すると、nullが示される(図1)。なぜなら、タイムラインのインスタンスすべてに3次元座標の操作を加える必要はない。そして、インスタンスに3次元空間の情報をもたせれば、少なからず負荷が増える。そのため、DisplayObjectインスタンスは、デフォルトではMatrix3Dオブジェクトをもたないのである。
DisplayObjectインスタンスにMatrix3Dオブジェクトを与えるには、何かしら3次元座標空間の操作を加えればよい。スクリプトであれば、簡単なのはDisplayObject.zプロパティに0を設定する。z座標値はデフォルトが0なので、見た目は変わりない。しかし、3次元空間の操作が行われたことにより、インスタンスのDisplayObject.transformプロパティのもつTransform.matrix3DプロパティにMatrix3Dオブジェクトが生成される。
これで、インスタンスのMatrix3Dオブジェクトをメソッドで操作して、座標が変換できる。Matrix3D.prependRotation()メソッドにより、MovieClipインスタンスはy軸で水平に45度回転する(図2)。なお、回転は軸の正の方向に対して、右ネジを回す向きが正の角度になる。
動的に配置したビットマップをマウスポインタに応じて水平回転させる
それでは、前回のスクリプト2と同じくビットマップのインスタンスをタイムラインに動的に置いて、マウスポインタの水平位置に応じて水平に回してみよう(スクリプト1)。プロパティDisplayObject.rotationX/DisplayObject.rotationY/DisplayObject.rotationZがインスタンスの角度そのものを設定したのに対して、Matrix3D.prependRotation()メソッドは第1引数の角度を現在の角度に加えるということに注意してほしい。なお、[ライブラリ]のビットマップには[クラス]としてImage0を設定してある。
[ムービープレビュー]を確かめると、[ライブラリ]のビットマップがSpriteインスタンスの入れ子となって配置され、マウスポインタの水平位置に応じて水平に回転する(図3)。
インスタンスに垂直の回転を加えてみる
インスタンスが水平に回せたら、垂直にも回してみたくなるのが人情だ。おそらく、水平座標の扱いと同じ処理を垂直座標でも行えばよいと見当がつく。
[ムービープレビュー]を試すと、確かに水平にも垂直にも回る。しかし、おそらく納得のいく動きではないだろう。というのは、マウスポインタを動かした方向とインスタンスの回る向きが一致しないからだ(図4)。
こういうときは、もっとスクリプトを単純にして確かめるとよい。ステージの真ん中にMovieClipインスタンスmy_mcを置いてみよう(図5)。そのうえで、つぎのようなフレームアクションを試してみる。さて、インスタンスはどうなるか。
結果は下図6の左の図のようになる。おそらく、右の図を予想しただろう。実はこれが、メソッド名の頭につけられたprependの意味につながる。"prepend"は「前に加える」という意味だ。もっとも、英和辞典には載っていないらしい[2]。「後に加える」を意味する"append"(こちらは辞書に載っている)の反対語だ。つまり、前に紹介したMatrix3Dクラスのメソッドには、prependをappendに置換えたものが対で存在する。
問題はprependが何の前なのかということだ。それは、Matrix3Dクラスを使った座標変換の適用順序を指す。つまり、prependというのは、その変換を最初に加えるということになる。よって、前記フレームアクションでは、まずz軸による90度の回転を変換処理の先頭に置く。そして、つぎのステートメントでは、y軸による45度の回転をさらにその前につけ加えた。したがって、まずy軸で45度傾け、つぎにz軸で90度回すことになる。
では、インスタンスを上図6の右図のように回転したければ、前掲フレームアクションでふたつのMatrix3D.prependRotation()メソッドのステートメントの順序を入替えればよい。そうなると、マウスポインタの位置に応じて水平および垂直にインスタンスを回転するその前に試したスクリプトについても、水平と垂直の処理を逆の順序にすれば済むだろうか。
実は、インスタンスに加えられる座標変換は、スクリプトに書いたふたつの回転のメソッドの処理だけではない。そのため、このふたつのステートメントの順序だけ考えたのでは足りない。インスタンスに加わるすべての座標変換を知ったうえで、その順序はappendのメソッドで整えることになる。マウスポインタの位置に応じてインスタンスを自由に回転するお題は、次回が解決編となる。
今回解説した次のサンプルファイルがダウンロードできます。