質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.51%
Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

Q&A

解決済

1回答

5441閲覧

回転系のメソッドに関して。

退会済みユーザー

退会済みユーザー

総合スコア0

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

0グッド

0クリップ

投稿2017/12/08 11:06

編集2017/12/08 11:21

###前提・実現したいこと
回転系のメソッドがいろいろあって、よくわかってないのですが、
インスペクタでTransformのRotationの値を設定することと関係のあるメソッドについて知りたいです。

###試したこと

C#

1 this.transform.rotation = Quaternion.Euler(0, 30, 0); 2 this.transform.Rotate(0, 30, 0);

上記、試してみたのですが、

・Quaternion.Eulerは、インスペクタのTransformのRotationで、引数の値を設定するのと同じ。 ・transform.Rotateは、インスペクタのTransformのRotationで、既存の値の後ろに+や-で連結して引数の値を入力するのと同じ。 (インスペクタでTransformのRotationで、既存の値に対してtransform.Rotateの引数の値を加算や減算するのと同じ。)

という認識で合っていますか?
また、他にインスペクタでTransformのRotationの値を設定するのと同じ挙動のメソッドがあれば教えていただきたいです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

マニュアルによると、インスペクタ上のTransformコンポーネントのPosition、Rotation、Scaleは親に対する相対値だそうですので、インスペクタのRotationに値を設定することと対応するのは、「localRotationにQuaternion.Eulerで作った回転を代入する」ということになるでしょうか。

transform.Rotateですが、実際に適当なオブジェクトに下記のようなスクリプトをアタッチして試してみてはいかがでしょう。オブジェクトはあらかじめ適当に回転して傾けておくと動きが分かりやすいかと思います。

C#

1using UnityEngine; 2 3public class TestScript : MonoBehaviour 4{ 5 private void Update() 6 { 7 if (Input.GetKeyDown(KeyCode.Space)) 8 { 9 transform.Rotate(0.0f, 30.0f, 0.0f); 10 } 11 } 12}

おそらく、インスペクタ上の回転角に値を加減算するのとは異なった結果になるのではないでしょうか?transform.Rotateで回転すると、シーンビュー上でオブジェクトから突き出た3色のギズモを軸に回転するはずです。

Self

また、transform.Rotate(0.0f, 30.0f, 0.0f, Space.World);に変えてみると、今度はワールド座標の3軸周りに回転し、こちらもやはりインスペクタへの加減算とは微妙に異なるはずです(オブジェクトを別のオブジェクトの子にして、親オブジェクトもあらかじめ適当に傾けておくと違いが明確になるでしょう)。

World

[コメントを受けて追記]
localEulerAnglesを取得して値を加減算し、ふたたびlocalEulerAnglesに書き戻す手が考えられるかと思います。

C#

1using UnityEngine; 2 3public class TestScript : MonoBehaviour 4{ 5 private void Update() 6 { 7 if (Input.GetKeyDown(KeyCode.Space)) 8 { 9 var localEuler = transform.localEulerAngles; 10 localEuler.y += 30; 11 transform.localEulerAngles = localEuler; 12 } 13 } 14}

プレビュー1

ただし、リファレンスの注意書きにあるように、このようにすると意図通りにならない場合があります。たとえば上記のlocalEuler.y += 30;localEuler.x += 30;にすると...

プレビュー2

Unity内部では回転をクォータニオンで持っており、オイラー角が必要になればそのクォータニオンからオイラー角が導出されますが、あるクォータニオンと同じ回転を表すオイラー角は一通りではないという特徴があるため、xだけを操作しているつもりなのにy、zも変化してしまい、最終的にめちゃくちゃな回転になってしまうかもしれません。

オイラー角からクォータニオンへ...の一方通行ならこの問題を回避できるかと思います。
コードをこのようにすると...

C#

1using UnityEngine; 2 3public class TestScript : MonoBehaviour 4{ 5 private Vector3 localEuler; 6 7 private void Start() 8 { 9 this.localEuler = transform.localEulerAngles; 10 } 11 12 private void Update() 13 { 14 if (Input.GetKeyDown(KeyCode.Space)) 15 { 16 this.localEuler.x += 30; 17 transform.localEulerAngles = this.localEuler; 18 } 19 } 20} 21

途中のインスペクタの表示ではやはりx、y、zが一斉に変化しており、回転が狂ってしまっているように見えるかもしれませんが、ちゃんと等価な回転になっており、12回の回転で元の向きに戻ります。

プレビュー3

[追記]
エディタ上だとRotationに値を入れると(たとえ±360°を超えるようなものでも)、インスペクタを表示しなおしても勝手に変な値に書き換わることなく、その値が維持されるようです。編集する上では直感的で便利な機能だと思いますが、どうやらUnityは内部的にユーザが入力した、あるいはエディタ上で回転させたオイラー角をクォータニオンを経由させず、そのままの状態で保管しているみたいですね。
How are Editor Transform.Rotation values calculated? : Unity3Dを参考に下記のようにしてみると、インスペクタ上に表示されているのと同じ値を見ることができました。

C#

1using UnityEngine; 2 3[ExecuteInEditMode] 4public class TestScript : MonoBehaviour 5{ 6 private Vector3 localEuler; 7 8 private void Start() 9 { 10 this.localEuler = transform.localEulerAngles; 11 #if UNITY_EDITOR 12 var serializedObject = new UnityEditor.SerializedObject(transform); 13 var serializedEulerHint = serializedObject.FindProperty("m_LocalEulerAnglesHint"); 14 Debug.Log(serializedEulerHint.vector3Value); 15 #endif 16 } 17 18 private void Update() 19 { 20 if (Input.GetKeyDown(KeyCode.Space)) 21 { 22 this.localEuler.x += 10; 23 this.localEuler.y += 10; 24 this.localEuler.z += 10; 25 transform.localEulerAngles = this.localEuler; 26 } 27 } 28}

インスペクタの回転表示

実際の実行時には使えないでしょうが、エディタスクリプトでインスペクタ上の角度を意図通りに加減算させたいというようなことがあれば、このあたりをいじることになりそうです。

投稿2017/12/08 21:30

編集2017/12/10 07:50
Bongo

総合スコア10807

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2017/12/10 03:04 編集

ご回答ありがとうございます。 localRotationにQuaternion.Eulerを代入すれば、インスペクタでRotationを設定するのと同じになるのですね。 transform.Rotateの件、検証してみました。 質問時は、キューブを傾けてなかったので気づきませんでしたが、キューブを傾けてtransform.Rotateしたら、確かにインスペクタ上の回転角に値を加減算するのとは異なることが確認できました。 さらに質問で恐縮ですが、インスペクタ上の回転角に値を加減算する処理をコードで実現する方法はご存知でしょうか?(そもそもコードではできないものなのでしょうか?) this.transform.localRotation = this.transform.localRotation * Quaternion.Euler(0, 20, 0); を試してみましたが、これもインスペクタ上で加減算するものとは異なりました。 上記コードは、transform.Rotate(0, 20, 0)と同じ処理ですか?
Bongo

2017/12/10 06:13 編集

試していないですが、コメントのthis.transform.localRotation = this.transform.localRotation * Quaternion.Euler(0, 20, 0);なら、画面上のオブジェクトの向きについては多分大丈夫だろうと思います。 インスペクタ上の表示がおかしくなったとしたら、それはクォータニオンとオイラー角の変換に関わる問題のような気がします。このへんは仕方ないものとして妥協するべきかもしれません... [追記] すいません、this.transform.localRotation = this.transform.localRotation * Quaternion.Euler(0, 20, 0);だと回転順が逆でしたね。this.transform.localRotation = Quaternion.Euler(0, 20, 0) * this.transform.localRotation;でどうでしょう? [さらに追記] たびたびすみません! Y軸周り回転の増減だけならthis.transform.localRotation = Quaternion.Euler(0, 20, 0) * this.transform.localRotation;、Z軸周り回転の増減だけならthis.transform.localRotation = this.transform.localRotation * Quaternion.Euler(0, 0, 20);でいけますが、X軸周りおよびこれらの複合には対応できませんでしたね。 結局ローカルオイラー角をとってきてどうこうしてやるのが妥当な方法なのかもしれません。
退会済みユーザー

退会済みユーザー

2017/12/10 09:18

ご回答ありがとうございます。 すみません、試してみた所、ローカルオイラー角をとってくる方法は、transform.Rotateと同じ処理結果になるように見えるのですが、差異が現れるのを検証するにはどうすればよいでしょうか?
Bongo

2017/12/10 10:32

localEulerAnglesの書き換えでだめでしたか?何が違ったんでしょうか...回転って案外やっかいですね。 私の試した場合では、親オブジェクトのRotationはX 20、Y 30、Z 40とし、キューブのRotationはX 0、Y 10、Z 20とした状態でスタートして、まず「StartでlocalEulerAnglesを保存し、Updateで保存した角度にX+10、Y+10、Z+10したのちlocalEulerAnglesに代入」のパターンですと、36回の回転で元の向きX 0、Y 10、Z 20に戻ったのに対し、「Startでは何もせず、Updateでtransform.Rotate(10, 10, 10);」では回転中のインスペクタ上の角度は非整数になり、36回の回転後の位置もずれていました(同じ位置にキューブを複製して異なる色を着け、それぞれに異なる回転方法のスクリプトをアタッチすると比較しやすいと思います)。
退会済みユーザー

退会済みユーザー

2017/12/10 12:13 編集

ご回答ありがとうございます。 ご提示いただいた内容で検証できました、確かにlocalEulerAnglesをStartで保存するパターンで元に戻り、transform.Rotateでは元に戻らないことが確認できました。 まとめると、『インスペクタ上の回転角に値を加減算する回転をコードで処理したい場合は、「StartでlocalEulerAnglesを保存し、Updateで保存した角度にX,Y,Zそれぞれのプロパティに加減算したのちlocalEulerAnglesに代入する」方法が1番近い処理となるが、その方法でも、インスペクタ上の回転角に値を加減算する処理と完全に一致するわけではない』ということでしょうか?
Bongo

2017/12/10 20:58

そうですね、回答に追記したように、インスペクタ上の角度は特別扱いされているようなので、完全一致は難しそうですね。ですがlocalEulerAnglesの方法なら、インスペクタの表示はともかく、向き自体はインスペクタへの加減算と同じにできるのではないかと思います(回答のスクリーンショットにあるように、「90.000...」のような浮動小数点数の精度によると思われる微妙なずれはあるようですが、これは仕方なさそうですね)。
退会済みユーザー

退会済みユーザー

2017/12/11 12:52

ご回答ありがとうございます。 ご丁寧な解説ありがとうございました、理解できました。 ありがとうございます。 ところで、transform.Rotateで回転を加えるメリットって何なのでしょうか? 検索すると、回転を加えるのにtransform.Rotateで行っている記事をよく見かけます。 今回のlocalEulerAnglesの方法ならば、インスペクタの回転角の加減算に近い処理ができて、一周回ったときに必ず元に戻るので扱いやすいと思うんですが、 transform.Rotateの場合は、インスペクタの回転角の加減算のような回転でもないし、一周回ったときに元に戻らずズレが生じる場合もあるので扱いづらさしか感じないのですが。 試してみた所、transform.Rotateでx,y,zのどれか一つのプロパティだけで一周回したら、元の位置に戻るような挙動になりました(全てのパターンでそうなるのかはわかりませんが)。 極論ですが、x,y,zのどれか一つのプロパティだけで回転する場合のみ、transform.Rotateで回転を加えるメリットがあるのかなと思いました。 また質問で申し訳ないですが、transform.Rotateでx,y,zのどれか一つのプロパティだけで一周回した場合は必ず元の位置に戻ると言えますか?
Bongo

2017/12/11 22:02

インスペクタの回転角の加減算ですと親オブジェクトの座標系を意識しなければなりませんが、transform.Rotateならば「transformの現在のx、y、z軸を基準に回転」できるので、便利な場面が多いのだろうと思います。 たとえば飛行機の姿勢を変えることを想像しますと、transform.Rotateでx軸周り回転させれば機首を上げ下げ、y軸なら機首を左右に、z軸ならローリング(https://ja.wikipedia.org/wiki/%E3%83%AD%E3%83%BC%E3%83%AA%E3%83%B3%E3%82%B0)となり、直感的ではないでしょうか。 transform.Rotateで回したときに元の位置に戻るかどうかですが、これは回転軸周りの回転量が「360/回転回数」であるかどうかによるはずです。一つのプロパティを10°や20°、30°ずつ回す場合はこの条件に当てはまるので元の位置に戻ったと考えられます。 先に試したtransform.Rotate(10, 10, 10);は回転軸ベクトルが(0.6444025, 0.5407179, 0.5407179)、回転角が16.78646°でした。ですので、回転角10°の場合を逆算すると、transform.Rotate(6.168753f, 5.725674f, 5.725674f);ならば、もっと直感的にはRotateの軸・角度指定版を使ってtransform.Rotate(new Vector3(0.6444025f, 0.5407179f, 0.5407179f), 10);とすれば36回の回転で元の位置に戻るはずです。
退会済みユーザー

退会済みユーザー

2017/12/12 02:00 編集

ご回答ありがとうございます。 なるほど、transform.Rotateは親オブジェクトに関係なく軸周りで回転させたい場合に直感的な回転ができて便利なのですね。 >先に試したtransform.Rotate(10, 10, 10);は回転軸ベクトルが(0.6444025, 0.5407179, >0.5407179)、回転角が16.78646°でした。 すみません、こちらの回転軸ベクトルと回転角はどのように算出されたのでしょうか?
Bongo

2017/12/12 03:12

transform.Rotate(10, 10, 10);での回転はtransform.localRotation *= Quaternion.Euler(10, 10, 10);と同等のようです。そこで、 float angle; Vector3 axis; Quaternion.Euler(10, 10, 10).ToAngleAxis(out angle, out axis); Debug.LogFormat("{0:F7}, {1:F7}, {2:F7}, {3:F7}", angle, axis.x, axis.y, axis.z); で調べられるかと思います。 もし手計算するとしたら、クォータニオンの定義をもとに逆計算することになるでしょうか。 https://qiita.com/kenjihiranabe/items/945232fbde58fab45681#quaternion などがご参考になりそうです。
退会済みユーザー

退会済みユーザー

2017/12/12 16:53 編集

ご回答ありがとうございます。 transform.Rotateから、回転軸ベクトルと回転角を取得する方法がわかりました。 すみません、理解力がなくて今頃気づいたのですが、 インスペクタの回転角は、自身のオブジェクトの座標軸を基準とした回転角ではなく、 親オブジェクトの座標軸を基準とした回転角なのですね。 (ただし、親オブジェクトがないルートオブジェクトの場合は、ワールドの座標軸を基準とした回転角) なのでオブジェクトを傾けて、親オブジェクトの座標軸もしくはワールドの座標軸からずらした状態で Rotateで自身のオブジェクトの座標軸を基準に回転させると、 インスペクタの回転角の加減算の回転ではなくなるのですね。 なので、Rotateの引数の角度の値が、インスペクタの回転角の加減算の値と一致しなくなるということなのですね。 この認識で合っていますか?
Bongo

2017/12/12 20:13 編集

ええ、そういうことになるでしょう。 補足として、昨日コメントを拝見したときにlocalRotationからオイラー角がとれるか...?といった疑問をお持ちのご様子でした。ご解決済みかもしれませんが、実際のところlocalEulerAnglesが返しているのはlocalRotation.eulerAnglesのようですね。 eulerAnglesが内部でやっているオイラー角を求める部分は、マネージドコードではないようで正確なところは分かりませんでしたが、「クォータニオンからオイラー角を求めてみる - Qiita」(https://qiita.com/edo_m18/items/5db35b60112e281f840e)で紹介されている方法とほとんど同じではないかと思います。 記事の中に「これで無事、各値が求まった・・と思いきや、特殊なケースが存在します...」「そこで、適当にθy = 0と置くと...」というくだりがありますが、どうもUnityはθyの代わりにθzを0にしているらしい...?こと、cos(θx)が0だと判定するしきい値は±0.00000208くらいらしい...?ことや、求めたオイラー角を0°〜360°におさめる処理(どういう理由か、0.0001ラジアンだけマイナス側に振れるようになっているようですが)を加えているらしい...?といった多少の違いがあるようで、UnityのeulerAnglesと完全一致はしないかもしれませんが、向き自体は同等なはずですので、問題にはならないかと思います。
退会済みユーザー

退会済みユーザー

2017/12/13 13:41 編集

ご回答ありがとうございます。 ご確認ありがとうございます、理解できました。 いえ、まだ解決してなかったのでご回答いただきありがとうございます。 下記で検証して理解できました。 //下記2行は同じ意味のコード。 Debug.Log (transform.localEulerAngles); //インスペクタの回転角のVector3。 Debug.Log (transform.localRotation.eulerAngles); //インスペクタの回転角のVector3。 また、localRotation.eulerAnglesにはVector3でオイラー角を代入することはできないみたいですね。 transform.localEulerAngles = new Vector3 (10, 20, 30); //設定できる。 transform.localRotation.eulerAngles = new Vector3 (10, 20, 30); //エラー。 //localRotation.eulerAnglesプロパティは読み取り専用なので(?)、 //localRotationでオイラー角を設定する場合は、下記の方法しかない(?)。 this.transform.localRotation = Quaternion.Euler(10, 20, 30); //設定できる。 また、Calc()メソッドを検証してオイラー角がとれてくるのを確認しました。 正直、理論的なことは全然わかりませんが、内部でやっていることのイメージがわかってよかったです。 また、今までの内容を見返してるうちに気づいたのですが(常識的なことなのかもしれませんが)、 オイラー角に回転を加える場合は足し算で、クォータニオンに回転を加える場合は掛け算なのですね。
Bongo

2017/12/13 14:01

そうですね。さらにQuaternionとVector3の掛け算なんてものも用意されていて、Vector3をQuaternionで回転させることもでき便利です。 本当は単なる掛け算というよりも、*記号で回転操作を簡潔に書けるようにしている、と考えた方がいいかもしれません。 お時間のある時にでも、先に挙げたhttps://qiita.com/kenjihiranabe/items/945232fbde58fab45681をご覧いただけますと、実際には3次元座標をクォータニオンに直したり、逆クォータニオンを逆方向から掛けたり...と、何やらややこしいことをしている様子がお分かりいただけるかと思います。
退会済みユーザー

退会済みユーザー

2017/12/13 16:35

なるほど、QuaternionとVector3の掛け算、検索して記事を見つけました。 http://tsubakit1.hateblo.jp/entry/2014/08/02/030919 なるほど、ベクトルの掛け算(*)は、掛ける順序によって回転が変わるのですね。 たくさんの質問にご回答いただきありがとうございました。 ご提示いただいたURLも勉強していこうと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.51%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問