Vector2.SignedAngle()は反時計回り

Vector2.SignedAngle() result is counter-clockwise

2022/9月追記:改めてドキュメントを見てみたら、新しいドキュメントではちゃんと「counter-clockwise(反時計回り)」に書き換えられていました。というわけでこのページはもう時代遅れです
(このページの概要は「昔はドキュメントで「時計回り」と書いてあったけど、その当時はドキュメントが間違っていて実際は結果は反時計回りで返ってくるよ」というだけの話です)

▼ 以下の内容は、マニュアルが間違っていた時代に書かれたものです。


2Dで符号付きの角度を求めたかったのでVector2.SignedAngle()を使っていたのですが(※Vector3のほうのSignedAngle()じゃないですよ)、これを使ってる上で混乱したことがあったので同じことで悩むことがあるかもしれない人のために書き残しておきます。

Vector2.SignedAngle()はドキュメントによれば

"The angle returned is the signed acute clockwise angle between the two vectors. "となっています。(※上述の通り、今は修正済み)
つまり2つのベクトル間の角度を符号付き鋭角の時計回りで結果を返してくれるそうです。

しかし、上ベクトルと右ベクトルの組み合わせをこのメソッドに投げてみると、結果は-90となります(一方で、左ベクトルとの結果は90度に)。

float r = Vector2.SignedAngle(Vector2.up,Vector2.right);  //結果:-90

float l = Vector2.SignedAngle(Vector2.up,-Vector2.right); //結果:90

結果 Vector2.SignedAngle() results

ピンク色の数字は、紫のtoベクトルの方をその角度にしたときの結果値
(水色のfromベクトルは0のまま)

これはむしろ…反時計回りなのでは?

メソッドの定義もpublic static float SignedAngle(Vector2 from, Vector2 to); と第一引数がfromになっているので、引数を渡す順番を間違えているというわけでもなさそうです。


3D空間的には正しい

Unityは左手座標系であり、回転軸の定義はDirect3Dと同じ「軸先から原点を見た方向に対して反時計回り」のようです。

つまり例えば飛行機のオブジェクトをZ軸で回転させる場合、「Z+から原点を見たベクトル[Vec(0, 0, -1)]に対して時計回り」となるので、その飛行機を後ろから見ている場合だと、反時計回りに回るように見えます。

(なので後ろから見ている場合、transform.rotation.zを増やすとオブジェクトは反時計回りに角度を深くしていきます)

そして2D空間は原点からZ+方向を見ているカメラがデフォルトなので、仮想的なカメラを基準にイメージした場合においては、反時計回りになるのは3D空間的にはある意味で正しいです。

ただ2D空間的に考えると、符号付きで時計回りというと直感的にX+側が正軸になると考えてしまいそうですが・・・。


符号を反転させて使う

Unityフォーラムのスレッドでも「これはドキュメントが間違ってるのでは?」という感じの質問があります。

https://forum.unity.com/threads/vector2-signedangle.507058/

まあドキュメントとはところどころ間違っているものですが、使うときは要注意、という話でした。

直感的なイメージと合致させたいときはSignedAngle()の結果は符号を反転させて使うといいです。


ただこういうのって「切り口を変えれば結局は正しい」とか「もっと深いレベルの知識がついたら実はこっちの方が厳密には正しかったと分かった」的な錯誤が個人的には今まで結構ありまして、これがそういうパターンでないとは言い切れない気もするのですが(「時計回りという単語自体には符号の定義は含まれないので別に間違ってないよ」とかいう可能性とか)・・・うーんいやでもやっぱりこれは普通にドキュメント的には「反時計回り」と書いてあるほうが合ってる気が・・・