Flash Player 10からMatrix3Dクラスが実装され*1、3次元座標空間の変換行列が扱えるようになりました。そこで、Matrix3Dクラスの変換行列を使い*2 、マウスポインタの位置に合わせて3次元座標空間で平面を回転させるサンプルスクリプトについて解説します。
*1 併せて、FumioNonaka.com「Matrix3Dクラス」をご参照ください。
*2 2次元座標空間を扱うMatrixクラスの変換行列については、「Matrixクラス - 変換行列」で解説しました。変換行列の意義については、この記事の01「変換行列(Matrixクラス)とは」をお読みください。
3次元座標空間でMovieClipなどの(DisplayObjectクラスまたはそのサブクラスの)インスタンスを回転するには、Matrix3D.prependRotation()メソッド*3 が使えます。メソッドのシンタックスは、つぎのとおりです。
prependRotation(度数:Number, 軸:Vector3D):void
第1引数には、加える回転角を度数で指定します。第2引数は、回転の軸をxyzからひとつ選びます*4 。Vector3Dクラスの定数で指定するのがよいでしょう。それぞれ、Vector3D.X_AXIS、Vector3D.Y_AXIS、Vector3D.Z_AXISとなります*5 。
では、メインタイムラインにMovieClipインスタンスmy_mcを置きます。そして、Matrix3Dインスタンスは、MovieClipインスタンスのDisplayObject.transformプロパティから、Transform.matrix3Dプロパティとして取得します。ところが、メインタイムラインのフレームアクションにつぎのようにスクリプトを記述すると、ランタイムエラーになります(図001)。
my_mc.transform.matrix3D.prependRotation(30,Vector3D.Y_AXIS);
そして、[出力]パネルに表示されるエラーメッセージはつぎのとおりです。
TypeError: Error #1009: null のオブジェクト参照のプロパティまたはメソッドにアクセスすることはできません。

「オブジェクト参照」がnullだと告げる。
これは、インスタンスの。DisplayObject.transformプロパティが、デフォルトではTransform.matrix3Dプロパティをもたず、nullを返すからです。このMatrix3Dインスタンスは、DisplayObject(またはそのサブクラス)のインスタンスに3次元座標の操作をしたとき、自動的に生成される仕組みになっています。そのためには、たとえばDisplayObject.zプロパティにデフォルト値0を設定するだけで足ります。
したがって、上記フレームアクションに、この操作を加えてつぎのように修正すれば、3次元座標空間においてインスタンスはy軸で水平方向に30度回転します(図002)。なお、Matrix3Dクラスを使った変換には、パースペクティブが適用されるので、インスタンスは回転角に応じて台形に変形されます。
my_mc.z = 0; my_mc.transform.matrix3D.prependRotation(30, Vector3D.Y_AXIS);

Matrix3D.prependRotation()メソッドに回転角30度とy軸を指定して、3次元座標空間でインスタンスを回転した。
それでは、マウスポインタの水平座標位置に合わせて、インスタンスをy軸で水平に回転させてみます。回転のアニメーションは、DisplayObject.enterFrameイベント(定数Event.ENTER_FRAME)にリスナー関数として登録します*6。関数名は、xRotate()としましょう。フレームアクションは、つぎのスクリプト001のようになります。
// タイムライン: メイン
// 第1フレームアクション
var nX:Number = my_mc.x;
var nDeceleration:Number = 0.3; // 回転スピードの調整係数
my_mc.z = 0; // 3次元座標のプロパティを操作
addEventListener(Event.ENTER_FRAME, xRotate);
function xRotate(eventObject:Event):void {
var nRotationY:Number = (mouseX - nX)*nDeceleration;
// 3次元座標空間においてy軸でインスタンスを回転
my_mc.transform.matrix3D.prependRotation(nRotationY, Vector3D.Y_AXIS);
}
回転角の大きさは、インスタンスの基準点から見たマウスポインタの水平座標値に、調整係数を掛合わせて決めています。したがって、インスタンスから水平方向にマウスポインタを離すほど、インスタンスが水平に回転するスピードは増します(図003)。

回転スピードは、インスタンスの基準点から見たマウスポインタの水平座標値に比例させた。
*3 Matrix3D.prependRotation()メソッドには、オプションの(省略可能な)第3引数としてVectorインスタンスで中心点を指定できます。デフォルトは、インスタンスの基準点になります。
*4 インスタンスの回転する方向は、x軸なら垂直で、y軸では水平になることに注意しましょう。
*5 [ヘルプ]の[Matrix3D]クラスの解説中には、「_」(アンダースコア)のないVector3D.XAXIS、Vector3D.YAXIS、Vector3D.ZAXISと誤って記載されていることがあります。
*6 EventDispatcher.addEventListener()メソッドを使ったイベントリスナーの登録の仕方について、詳しくはGihyo.jp連載「ActionScript 3.0で始めるオブジェクト指向スクリプティング」の第5回「イベントリスナーを使う」をお読みください。
前掲スクリプト001に、y軸の水平回転だけでなく、x軸による垂直の回転も加えてみましょう。以下のフレームアクションのように、y座標についてもx座標と同じ処理を加えればよさそうに思われます。
var nX:Number = my_mc.x;
var nY:Number = my_mc.y;
var nDeceleration:Number = 0.3;
my_mc.z = 0;
addEventListener(Event.ENTER_FRAME, xRotate);
function xRotate(eventObject:Event):void {
var nRotationY:Number = (mouseX - nX)*nDeceleration;
var nRotationX:Number = (mouseY - nY)*nDeceleration;
my_mc.transform.matrix3D.prependRotation(nRotationY, Vector3D.Y_AXIS);
my_mc.transform.matrix3D.prependRotation(nRotationX, Vector3D.X_AXIS);
}
上記のステートメント追加により、確かにインスタンスが垂直方向に回転するようになります。しかし、その回転の軸に違和感があります。マウスポインタを垂直に動かしたとき、ステージ上のインスタンスの状態から垂直に回転しません。いわばインスタンスが正面を向いた状態で垂直に回り、配置されたタイムラインのx軸でなく、インスタンス自身のx軸で回転しているように見えます(図004)。

マウスポインタを垂直に動かしたとき、配置されたタイムラインのx軸でなく、インスタンス自身のx軸で回転する。
これは変換を適用する順序に原因があります。変換をまったく加えていないデフォルト状態のインスタンスは、位置が親タイムラインの基準点にあり、拡大・縮小は行わない比率1の実寸、回転角も0の状態です(図005)。

位置は親タイムラインの基準点、拡大・縮小は比率1、回転角0。
そのインスタンスをオーサリング時のステージ上でも、あるいはランタイム時にスクリプトによってでも、移動したり、拡大・縮小したり、回転したりすると、変換が加えられたことになり、DisplayObject.transformプロパティのTransform.matrix3Dプロパティにその変換行列が設定されます*7。
Matrix3Dで移動(「平行移動」とも呼ばれます)や拡大・縮小、回転を行うメソッドには、下表001のように変換を前に(prepend)適用するものと、後に(append)適用するものとがあります。そして、前に適用するメソッドは、インスタンスに設定されている現在のTransform.matrix3Dプロパティつまり変換行列を一旦デフォルトに戻し、メソッドの変換を先に加えてから、もとの変換行列(Transform.matrix3Dプロパティ)を後から適用するのです。
| 変換 | 変換を前に適用するメソッド | 変換を後に適用するメソッド | 変換結果 |
|---|---|---|---|
| 平行移動 | Matrix3D.prependTranslation() |
Matrix3D.appendTranslation() |
![]() |
| 拡大・縮小 | Matrix3D.prependScale() |
Matrix3D.appendScale() |
![]() |
| 回転 | Matrix3D.prependRotation() |
Matrix3D.appendRotation() |
![]() |
先ほど試したフレームアクションでは、Matrix3D.prependRotation()メソッドによりx軸で回転しました。そのため、インスタンスは一旦デフォルトに戻り、回転を何も加えていない正面向きになります。それをx軸で回す訳ですから、つねに正面向きからの回転にしかならないのです。変換行列は、適用の順序が変わると、その結果が変わることにご注意ください*8 。これは、フィルタを適用する順番が変わると、その効果が変わってくるのに似ています。
したがって、インスタンスの現在の状態を回転しようというときには、後から変換するMatrix3D.appendRotation()メソッドを使うべきだということになります。ただし、この場合ひとつ気をつけることがあります。変換行列は、原点を基準として座標を変換します。この原点は、親タイムラインの基準点になります。つまり、インスタンスの変換は、配置された親タイムラインの基準点を中心に加えられるのです。ちょうど[自由変形ツール]で、中心点を親タイムラインの基準点に設定した状態と考えればよいでしょう(図006)。

中心点に設定した親タイムラインの基準点を中心に、変形が加えられる。
それでは、改めてx軸で回す処理をスクリプト001に加えます。具体的に変換行列をどのように適用すればよいでしょう。
インスタンスを親タイムラインの基準点に置かないかぎり、回転はインスタンスの基準点でなく、その親タイムラインの基準点を原点として適用されてしまいます。かといって、インスタンスを完全にデフォルトに戻してしまうと、意図した回転ができませんでした。だとすれば、必要な値だけを個別にデフォルトに戻すのです。
回転について問題になっているのは、インスタンスの位置です。したがって、インスタンスを親タイムラインの基準点に平行移動すれば、インスタンスを自身の基準点で回すことができます。そして、回転し終えたら、位置をまた平行移動してもとに戻せばよいのです。
平行移動を後から適用するメソッドは、Matrix3D.appendTranslation()です(前掲表001)。シンタックスはつぎのとおりです。
appendTranslation(x座標:Number, y座標:Number, z座標:Number):void
Matrix3D.appendTranslation()メソッドの3つの数値の引数には、移動する座標値つまり現在の座標に加算するxyz座標値を指定します。なお、後から回転を加えるメソッドはMatrix3D.appendRotation()で、シンタックスはMatrix3D.prependRotation()メソッドと基本的に同じです。これらのメソッドを使って前掲スクリプト001は、つぎのように修正されます(スクリプト002)。
// タイムライン: メイン
// 第1フレームアクション
var nX:Number = mySprite.x;
var nY:Number = mySprite.y;
var nDeceleration:Number = 0.3;
mySprite.z = 0;
addEventListener(Event.ENTER_FRAME, xRotate);
function xRotate(eventObject:Event):void {
var nRotationY:Number = (mouseX - nX)*nDeceleration;
var nRotationX:Number = (mouseY - nY)*nDeceleration;
// 3次元座標空間でインスタンスを回転
mySprite.transform.matrix3D.appendTranslation(-nX, -nY, 0); // 親タイムラインの基準点に移動
mySprite.transform.matrix3D.appendRotation(nRotationY, Vector3D.Y_AXIS);
mySprite.transform.matrix3D.appendRotation(nRotationX, Vector3D.X_AXIS);
mySprite.transform.matrix3D.appendTranslation(nX, nY, 0); // 位置を戻す
}
まず、予め取得しておいたインスタンスのxy座標値をともにマイナスにして引数とし、変換行列(Transform.matrix3Dプロパティ)にMatrix3D.appendTranslation()メソッドを適用します。すると、インスタンスの位置は親タイムラインの基準点に移動します。しかし、位置以外の回転などの変換は、もとのインスタンスと同じ状態が保たれます。
そこで、Matrix3D.appendRotation()メソッドにより、y軸とx軸の回転を後から加えます。そのうえで、改めてインスタンスのもとの位置のxy座標を引数として、Matrix3D.appendTranslation()メソッドを後から適用することにより、インスタンスの位置を戻します。これで、ステージ上のインスタンスの状態をもとに、マウスポインタの位置に応じてインスタンスがx軸およびy軸で回転します(図007)。

ステージ上のインスタンスの状態から、マウスポインタの位置に合わせて、インスタンスが水平・垂直に回転する。
*7 前述のとおり、インスタンスはデフォルトではTransform.matrix3Dプロパティをもたず、プロパティ値はnullになりますので、インスタンスに3次元座標空間の操作が加えられたものとします。
*8 変換行列の適用は、数学的には行列の乗算になります。変換行列の数学的な捉え方については、FumioNonaka.com「変換行列を数学的に捉える」をお読みください。