ActionScript 3.0で始めるオブジェクト指向スクリプティング

第36回Matrix3Dクラスの後から加える変換

前回の第35回Matrix3Dクラスによる座標変換では、先に加える変換となるprependがついたMatrix3Dクラスのメソッドを試した。しかし、インスタンスに加わる変換は、スクリプトに書かれたものだけではないというヒントを示して終わった。今回は、その変換の中身を明らかにし、後から加えるappendのついた変換のメソッドを試す。

DisplayObjectインスタンスに加わる変換

例によって、まずは簡単なサンプルで確かめよう。ステージ中央にMovieClipインスタンスmy_mcを置き、2次元平面で90度回してみる[1]⁠。これを初めの状態とする図1⁠。

図1 MovieClipインスタンスを2次元平面で90度回す
図1 MovieClipインスタンスを2次元平面で90度回す

MovieClipインスタンスを置いたタイムラインには、以下のフレームアクションを記述する。第1に注目してほしいのは、インスタンスのDisplayObject.transformプロパティから参照したTransform.matrix3Dに、Matrix3D()コンストラクタで生成した新規のオブジェクトを代入していることだ。これにより、インスタンスのもつ変換行列つまりMatrix3Dオブジェクトは、デフォルトに設定される。しかし第2に、インスタンスがもともともっていたMatrix3Dオブジェクトは、予め変数(myMatrix3D)にとっておいた。そして、インスタンスをクリックするとリスナー関数(setMatrix3D())によって、もとのMatrix3Dオブジェクトが改めて設定し直されるという流れだ。

// タイムライン: メイン
// タイムラインにMovieClipインスタンスmy_mcを配置
my_mc.z = 0;
var myMatrix3D:Matrix3D = my_mc.transform.matrix3D;
my_mc.transform.matrix3D = new Matrix3D();   // デフォルトのMatrix3Dオブジェクトを設定
my_mc.addEventListener(MouseEvent.CLICK, setMatrix3D);
function setMatrix3D(eventObject:MouseEvent):void {
  my_mc.transform.matrix3D = myMatrix3D;   // もとのMatrix3Dオブジェクトを設定
}

[ムービープレビュー]を確かめると、まずインスタンスはタイムラインの基準点つまりメインタイムラインなら左上隅に、回転していない(角度0)実寸(100%)で表示される図2左図⁠。つぎにインスタンスをクリックすると、前掲スクリプトのリスナー関数により、オーサリング時に設定したとおり、ステージ中央で90度横に回る図2右図⁠。

図2 デフォルトに戻ったインスタンスをクリックしてもとの状態にする
画像

実は、Flash Player内部でも、インスタンスはこのような手順で配置されている。つまり、まずタイムラインに置くべきデフォルト状態のインスタンスを取出し、その変換情報が納められたMatrix3Dオブジェクトを調べる。そして、そのMatrix3Dオブジェクトをインスタンスに適用して表示する。変換を前に加えるprependと後に加えるappendは、もとのMatrix3Dオブジェクトよりメソッドを適用するのが前か後かという意味なのだ。

動的に配置したビットマップをマウスポインタに応じて上下左右に回す

それでは改めて前回の課題だった、動的に配置したビットマップを、マウスポインタの動きに応じて上下左右に回してみよう。前回マウスポインタの動きとインスタンスの回る方向がちぐはぐだったのは、Matrix3D.prependRotation()メソッドを使ったからだ。

たとえば、インスタンスがy軸で横に回転していたとき図3上図⁠、マウスポインタを上に動かせば、インスタンスは横向きのまま垂直に回ることを期待する。ところが、Matrix3D.prependRotation()メソッドを用いると、インスタンスはデフォルトの正面向きで上に回転して、その後直前の状態の変換を加えることになってしまう図3下図⁠。

図3 Matrix3D.prependRotation()メソッドは正面向きのインスタンスを回すことになる

直前の状態

画像

デフォルト状態

画像

Matrix3D.prependRotation()メソッドを適用

画像

直前の変換を適用

画像

直前の状態に対して変換を加えたいなら、後から適用するappendのメソッドを用いる必要がある。平行移動や回転あるいは伸縮の変換を後から加えるには、prependのメソッドに対応するMatrix3D.appendTranslation()Matrix3D.appendRotation()Matrix3D.appendScale()といったメソッドをそれぞれ用いる。これらのメソッドは変換を後から加えるという違いがあるだけで、引数は変わらない。

Matrix3Dオブジェクト.appendTranslation(移動x座標値, 移動y座標値, 移動z座標値)
Matrix3Dオブジェクト.appendRotation(回転度数値, 回転軸)
Matrix3Dオブジェクト.appendScale(水平伸縮率, 垂直伸縮率, 奥行き伸縮率)

もっとも、Matrix3D.prependRotation()メソッドをMatrix3D.appendRotation()に変えただけでは、インスタンスを上下左右に回すことは思惑どおりにいかない。MovieClipインスタンスmy_mcをステージ中央に置いて、Matrix3D.appendRotation()メソッドによりy軸で単純に水平に回してみよう。フレームアクションはつぎのとおりだ。

// タイムライン: メイン
// タイムラインにMovieClipインスタンスmy_mcを配置
my_mc.z = 0;
var myMatrix3D:Matrix3D = my_mc.transform.matrix3D;
addEventListener(Event.ENTER_FRAME, xRotate);
function xRotate(eventObject:Event):void {
  myMatrix3D.appendRotation(5, Vector3D.Y_AXIS);
}

[ムービープレビュー]を確かめると、確かにインスタンスは水平に回る。ただし、回転の軸がインスタンスの基準点ではない。実は、行列による座標変換は、原点がつねにインスタンスの置かれた親タイムラインの基準点になる。メインタイムラインなら、水平回転のy軸はステージ左端だ図4⁠。そして、この原点は動かせない。

図4 メインタイムラインは水平回転のy軸はステージ左端になる
図4 メインタイムラインは水平回転のy軸はステージ左端になる

Matrix3D.prependRotation()メソッドではインスタンスの基準点を軸に回ったのは、デフォルト状態つまり座標(0, 0)ではインスタンスと親タイムラインの基準点が一致するためだ。後から変換を加えるMatrix3D.appendRotation()メソッドでは、インスタンスと親タイムラインの基準点の位置は通常異なる。

とはいえ、対処法はさほど難しくない。インスタンスの角度は変えずに、回転の軸だけずらせばよい。つまり、インスタンスの座標のみ親タイムラインの基準点に移動すれば、直前の角度からインスタンスの基準点で回転できる。その後忘れずに、座標はもとの位置に戻す。つまり、平行移動・回転・平行移動を組合わせるということだ。

前回つくった動的に配置したビットマップをマウスポインタに応じて水平回転させるのスクリプト1に、垂直の処理を加えたのが以下のスクリプト1だ。垂直方向の回転角を計算したうえで、インスタンスは親タイムラインの基準点に移動してから回転し、改めてもとの位置に戻した。

スクリプト1 インスタンスの基準点からのマウスポインタの位置に応じてインスタンスを水平・垂直回転
// タイムライン: メイン
// [ライブラリ]のビットマップに[クラス]としてImage0を設定
var mySprite:Sprite = new Sprite();
var myBitmap:Bitmap = new Bitmap(new Image0(0, 0));
var nX:Number = stage.stageWidth / 2;
var nY:Number = stage.stageHeight / 2;
var nDeceleration:Number = 0.3;
mySprite.z = 0;
var myMatrix3D:Matrix3D = mySprite.transform.matrix3D;
mySprite.x = nX;
mySprite.y = nY;
myBitmap.x = -myBitmap.width / 2;
myBitmap.y = -myBitmap.height / 2;
addChild(mySprite);
mySprite.addChild(myBitmap);
addEventListener(Event.ENTER_FRAME, xRotate);
function xRotate(eventObject:Event):void {
  var nRotationY:Number = (mouseX - nX) * nDeceleration;
  var nRotationX:Number = (mouseY - nY) * nDeceleration;
  myMatrix3D.appendTranslation(-nX, -nY, 0);   // 親タイムラインの基準点に移動
  myMatrix3D.appendRotation(nRotationY, Vector3D.Y_AXIS);   // 水平回転
  myMatrix3D.appendRotation(nRotationX, Vector3D.X_AXIS);   // 垂直回転
  myMatrix3D.appendTranslation(nX, nY, 0);   // もとの位置に戻る
}

[ムービープレビュー]を確かめると、今度は直前の状態からインスタンスの中心を軸にマウスポインタの向きに回る図5⁠。2回にわたって見てきたように、単純な変換であれば前に加えるメソッドの方が簡単に書ける。しかし、いくつかの変換を組合わせてアニメーションするような場合には、後から加えるメソッドの方がステートメント数は増えるものの、細かく処理できる。

図5 マウスポインタの動きとインスタンスの回転する方向が一致する
図5 マウスポインタの動きとインスタンスの回転する方向が一致する

次回は、回す面の数を増やし、その重ね順について解説したい。

今回解説した次のサンプルファイルがダウンロードできます。

おすすめ記事

記事・ニュース一覧