サイトトップ

Director Flash 書籍 業務内容 プロフィール

Adobe Flash非公式テクニカルノート

インスタンスのy軸における回転角の値の範囲

ID: FN1008002 Product: Flash CS4 and above Platform: All Version: 10 and above/ActionScript 3.0

3次元空間における回転で、基本的にx軸とz軸回りは±180度(±π)の範囲で角度が扱われるのに対し、y軸回りは内部的に±90度(±π/2)の範囲に角度が計算し直されてしまうようです[*1]

[*1] DisplayObject.rotationX/rotationY/rotationZプロパティに値を設定する場合は、[ヘルプ]の記載とは異なり、その範囲の限定や値の換算は行われません(「DisplayObject.rotationX/rotationY/rotationZプロパティ」注[*1]参照)。


01 y軸回りの角度の値がどう変わるか
まずは、インスタンスのy軸回りの角度は値がどう変わるのか確かめておきましょう。以下のスクリプト001は、Matrix3Dオブジェクトによりインスタンス(my_mc)をy軸で回します。そして、そのインスタンスのDisplayObject.rotationYプロパティの値を、もうひとつのインスタンス(follower_mc)の同じプロパティに設定しています。

スクリプト001■y軸で回したインスタンスの角度を別のインスタンスに設定する
  1. var my_mc:MovieClip;
  2. var follower_mc:MovieClip;
  3. my_mc.z = 0;
  4. var myMatrix3D:Matrix3D = my_mc.transform.matrix3D;
  5. addEventListener(Event.ENTER_FRAME, xRotate);
  6. function xRotate(eventObject:Event):void {
  7.   myMatrix3D.prependRotation(10, Vector3D.Y_AXIS);
  8.   follower_mc.rotationY = my_mc.rotationY;
  9.   trace(Math.round(my_mc.rotationX),
         Math.round(my_mc.rotationY),
         Math.round(my_mc.rotationZ));
  10. }

スクリプト第7行目は、インスタンスmy_mcをy軸で回しているだけです。スクリプト第8行目で、そのDisplayObject.rotationYプロパティの値を設定したインスタンスfollower_mcは、my_mcと同じように水平に回ると考えるのが普通です。

ところが、[ムービープレビュー]を確かめると、follower_mcは90度まで回ったところで、回転の向きが逆になります(図001)。さらに、そのまま-90度まで回ると、また向きが逆転するのです。つまり、follower_mcは±90度の間を行ったり来たり回転するのです。

図001■水平に回るふたつのインスタンス
cross product cross product
cross product cross product

スクリプト第9行目は、もとのインスタンスmy_mcの回転角の値を、xyz軸回りそれぞれについて3つずつ(x, y, z)の順に[出力]します。その結果は、以下のとおりです。y軸回りの角度が90度を超えると減っていく代わりに、x軸とz軸回りにともに180度回転していることがわかります。そして、y軸回りの角度が-90度まで下がると、値は増加に転じ、xz軸回りの角度はどちらも0度に戻ります。

0 10 0
0 20 0
0 30 0
0 40 0
0 50 0
0 60 0
0 70 0
0 80 0
0 90 0
180 80 180
180 70 180
180 60 180
180 50 180
180 40 180
180 30 180
180 20 180
180 10 180
180 0 180
(右の列につづく)

 

180 -10 180
180 -20 180
180 -30 180
180 -40 180
180 -50 180
180 -60 180
180 -70 180
180 -80 180
0 -90 0
0 -80 0
0 -70 0
0 -60 0
0 -50 0
0 -40 0
0 -30 0
0 -20 0
0 -10 0
0 0 0

x軸とz軸でそれぞれ180度回すと、y軸で180度回転したのと同じく、水平に後ろを向きます(図002)。これが、上記[出力]の左列最後の(180, 0, 180)の状態です。インスタンスが裏返った状態でy軸回りの角度を定める場合は、y軸の正方向に対して反時計回りが正になります。したがって、y軸回りの回転角をマイナスすることで、もともとのy軸の180度以上の角度になります(上記[出力]右列上段)。

図002■x軸とz軸でともに180度回転する

もとのインスタンス

x軸で180度回転

z軸で180度回転

上記[出力]からわかるのは、y軸回りの±90度の範囲は普通どおり角度が示されます。しかし、それを超える角度は、x軸z軸ともに180度回転して裏向きの状態からy軸回りに±90度まで角度を変えて、都合±180度の回転を表しているということです。つまり、x軸とz軸の180度の回転を組合わせることにより、y軸は±90度の範囲の回転角で1周が定まります[*2]

[*2] もちろん、3次元空間における扱いは、3つの座標軸について対称です。y軸ともうひとつの軸を±180度の範囲に定めれば、残った軸の範囲が±90度になります。ただ、ActionScript 3.0は、y軸の範囲を±90度に決めたようです。


02 y軸回りの角度を±180度の範囲に計算し直す
y軸回りの角度が±90度の範囲になる理由がわかったとしても、そのままの値では使えない場合があります。たとえば、前掲スクリプト001の例です。そのときは、3つの軸回りの角度を計算し直すことになります。

具体的には、x軸とz軸回りの角度は-180度戻し、y軸回りの180度裏返ったところから数えている角度は正面向きからの値にします。その計算を関数(recalcRotations())として定めたのが、つぎのスクリプト002です。引数にはDisplayObjectインスタンスを渡し、計算し直したxyz3つの軸ごとの角度はVector3Dオブジェクトで返されます。

スクリプト002■インスタンスのxyz軸回りの角度を±180度のVector3Dオブジェクトに納めて返す
  1. function recalcRotations(myDisplayObject:DisplayObject):Vector3D {
  2.   var rotations:Vector3D = new Vector3D();
  3.   var nY:Number = myDisplayObject.rotationY
  4.   if (nY > 0) {
  5.     rotations.y = 180 - nY;
  6.   } else {
  7.     rotations.y = -180 - nY;
  8.   }
  9.   rotations.x = xGetDegrees(myDisplayObject.rotationX - 180);
  10.   rotations.z = xGetDegrees(myDisplayObject.rotationZ - 180);
  11.   return rotations;
  12. }
  13. function xGetDegrees(nDegrees:Number):Number {
  14.   nDegrees += 180;
  15.   nDegrees %= 360;
  16.   nDegrees += 360;
  17.   nDegrees %= 360;
  18.   nDegrees -= 180;
  19.   return nDegrees;
  20. }

なお、関数xGetDegrees()は、引数に渡された任意の実数の角度を±180の範囲の値にして返します[*3]。注意しなければならないのは、一般にxyzの3つの軸の回転角度を見ただけでは、xz軸で180度反転したかどうかはわからないということです。y軸回りの角度を計算し直す必要があるかどうかは、その処理内容に応じて判別の仕方を考えなければなりません。

ただ、y軸でしか回していない場合なら、x軸とz軸の回転角度を調べればわかります。たとえば前掲スクリプト001なら、x軸とz軸の回転角が約0度から約180度に変わったとき、2軸の反転が行われ、y軸の回転角度を計算し直す必要があるということになります。前掲スクリプト002の関数(recalcRotations())を、つぎのように用いればよいでしょう。

var my_mc:MovieClip;
var follower_mc:MovieClip;
my_mc.z = 0;
var myMatrix3D:Matrix3D = my_mc.transform.matrix3D;
addEventListener(Event.ENTER_FRAME, xRotate);
function xRotate(eventObject:Event):void {
  myMatrix3D.prependRotation(10, Vector3D.Y_AXIS);
  if (my_mc.rotationX > 90) {   // x軸で反転したかどうか
  var rotations:Vector3D = recalcRotations(my_mc);
    follower_mc.rotationY = rotations.y;
    trace(Math.round(rotations.x),
       Math.round(rotations.y),
       Math.round(rotations.z));
  } else {
    follower_mc.rotationY = my_mc.rotationY;
    trace(Math.round(my_mc.rotationX),
       Math.round(my_mc.rotationY),
       Math.round(my_mc.rotationZ));
  }
}

なお、x軸で反転したかどうかは、その回転角が0か180かで分けられます。ただし、実際の値はわずかな誤差を含むので、上記スクリプトでは中間の値90と比べて判別しています。

[*3] 角度の値を±180の範囲に換算する関数について詳しくは、「ActionScript 3.0で始めるオブジェクト指向スクリプティング」第34回「3次元空間における回転」をご参照ください。


03 数学的な考察
前述01「y軸回りの角度の値がどう変わるか」で調べたところでは、y軸回りに90度を超えるか-90度より小さい角度が設定されると、下表001のようにx軸とz軸が180度回転し、y軸回りの角度が変更されました。この変換について、少し数学的に考えてみます。まず、角度の範囲が(正の場合と負の場合の)ふたつに分かれると手間がかかるので、1周を360度として90 <θ < 270の角度で考えます(下表001の括弧書き)。

表001■y軸回りに設定したい角度と実際に設定されるxyz各軸回りの角度
y軸回りに設定したい角度 実際に設定されるxyz各軸回りの角度
x y z
90
100
110
120
130
140
150
160
170
180
-170 (190)
-160 (200)
-150 (210)
-140 (220)
-130 (230)
-120 (240)
-110 (250)
-100 (260)
-90 (270)
0
180
180
180
180
180
180
180
180
180
180
180
180
180
180
180
180
180
0
90
80
70
60
50
40
30
20
10
0
-10
-20
-30
-40
-50
-60
-70
-80
-90
0
180
180
180
180
180
180
180
180
180
180
180
180
180
180
180
180
180
0

すると、y軸回りに設定したい角度θは、実際にはθ - 180度に変わっていることがわかります。また、3次元空間におけるxyz各軸による回転は、つぎの3次正方行列で表されます(シンタックス001)。

シンタックス001■xyz各軸による回転行列
z軸による回転
cosθ   -sinθ   0 x = x cosθ - y sinθ
sinθ   cosθ   0 y x sinθ + y cosθ
0   0   1 z z
x軸による回転
1   0   0 x = x
0   cosθ   -sinθ y y cosθ - z sinθ
0   sinθ   cosθ z y sinθ + z cosθ
y軸による回転
cosθ   0   sinθ x = z sinθ + x cosθ
0   1   0 y y
-sinθ   0   cosθ z z cosθ - x sinθ

まず、x軸とz軸でともに180度回転する行列を求めます。x軸とy軸でともにθ回転する行列は、つぎのようにふたつの回転行列の積で表されます[*4]

cosθ   -sinθ   0 1   0   0
sinθ   cosθ   0 0   cosθ   -sinθ
0   0   1 0   sinθ   cosθ

ここで、cos180° = -1、sin180° = 0です。したがって、xz両軸による180度の回転行列は、以下のように求められます。またこの場合には、x軸とz軸の回転の順序を入替えても、変換の結果は変わりません。

-1   0   0 1   0   0  =  1   0   0 -1   0   0  =  -1   0   0
0   -1   0 0   -1   0 0   -1   0 0   -1   0 0   1   0
0   0   1 0   0   -1 0   0   -1 0   0   1 0   0   -1

つぎに、y軸による角度θ - 180度の回転行列を考えます。これは前掲シンタックス001より、つぎのような3次正方行列になります。

cos(θ - 180°)   0   sin(θ - 180°)
0   1   0
-sin(θ - 180°)   0   cos(θ - 180°)

ここで、三角関数のsinとcosには、以下のシンタックス002のような関係式が成立ちます。すると、cos(θ- 180°)とsin(θ- 180°)は、つぎのように変形できます。

cos(θ- 180°) = cos(180°-θ) = -cosθ
sin(θ- 180°) = -sin(180°-θ) = -sinθ
シンタックス002■三角関数の関係式
三角関数sinとcosの関係式
sin(-θ) = -sinθ cos(-θ) = cosθ
sin(180°- θ) = sinθ cos(180°- θ) = -cosθ

したがって、y軸によるθ - 180度の回転行列は、つぎのようになります。

cos(θ - 180°)   0   sin(θ - 180°)  =  -cosθ   0   -sinθ
0   1   0 0   1   0
-sin(θ - 180°)   0   cos(θ - 180°) sinθ   0   -cosθ

x軸とz軸でともに180度回転したうえでy軸回りにθ - 180度回転するには、前に求めたxz両軸による180度の回転行列を右から乗じます。

cos(θ - 180°)   0   sin(θ - 180°) -1   0   0
0   1   0 0   1   0
-sin(θ - 180°)   0   cos(θ - 180°) 0   0   -1
 =  -cosθ   0   -sinθ -1   0   0
0   1   0 0   1   0
sinθ   0   -cosθ 0   0   -1
 =  cosθ   0   sinθ
0   1   0
-sinθ   0   cosθ

導かれた最後の正方行列は、y軸による角度θの回転行列そのものです(前掲シンタックス001参照)。つまり、y軸で角度θ回すのは、xz両軸で180度回転したうえでy軸回によりθ - 180度回すのと同値だということになります。逆にいえば、y軸回りの角度を±90度の範囲にかぎらないと、回転行列からxyz各軸の回転角が一意に定まらないということです(なお、前掲注[*2]参照)[*5]。そのため、ActionScript 3.0は、y軸による回転角の範囲をこのように定めたものと考えられます。

[*4] 行列による乗算については、「変換行列を数学的に捉える」をお読みください。

[*5] たとえば、Matrix3D.decompose()メソッドが返すVectorオブジェクト(Vector3Dベース型)の2番目(インデックス1)のVector3Dエレメントは、デフォルトではxyz各軸の回転角を納めています。



作成者: 野中文雄
作成日: 2010年8月24日


Copyright © 2001-2010 Fumio Nonaka.  All rights reserved.