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

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

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

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

Q&A

解決済

1回答

7092閲覧

Unity-オブジェクトをなめらかに90ずつ回転させる

退会済みユーザー

退会済みユーザー

総合スコア0

Unity

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

0グッド

0クリップ

投稿2017/10/25 18:34

3Dゲームを製作しています。

マウスのスワイプに応じて画面上のオブジェクトを滑らかに90度ずつ回転させたいです。
いくつか思いつく方法を試したのですが、どれもうまくいきませんでした。
瞬間的でなく滑らかに、目標の角度と小さい誤差で回転させる方法をご教授願いたいです。

<試したこと>

IEnumerator RotateAxisY()
{

var currentRotation = rotatingAxis.transform.localEulerAngles; var newRotation = currentRotation + new Vector3(90, 0, 0); Debug.Log(currentRotation); Debug.Log(newRotation); for (float t=0;t<0.5f;t+=Time.deltaTime) { Vector3 rotation = Vector3.Lerp(currentRotation, newRotation, t * 2); //rotatingAxisは回転させるオブジェクト rotatingAxis.transform.localEulerAngles = rotation; yield return null; } //ちょうど90度刻みになるように調整 rotatingAxis.transform.localEulerAngles = newRotation; }

上記のコルーチンをスワイプ入力時に呼び出す方法では、3回転目以降に回転方向が逆になってしまい、うまくいきませんでした。
差し込んであるDebug.Logで回転前と回転後予定のrotation値を見たところ、3回転目までは(0,0,0)、(90,0,0)、(180,0,0)と進んでいたのが、3回転目で急に(0,180,180)となっていたのでこれが原因だと思います。
今回のような実装ではオイラー角を使うべきではないんでしょうか?

iTweenのRotateAddも試したのですが、なぜか回転のたびに無視できない大きさの誤差が生じてしまいました。
これについては以前teratailで質問させてもらったのですが、原因はわからずでした。
ーーー

目標の角度まで滑らかに回転させるには、Update内やコルーチンで秒数をカウントする必要があり、それだと現実時間と違ってゲーム内時間はフレームごとに断続的に進む以上どうしても小さくない誤差が生じてしまいます。
その誤差をできるだけ小さくするには結局Transformに目標の角度を代入する行程が必須だと思うのですが、上記の通り、オイラー角の仕様?にひっかかってうまくいきません。

そもそものスクリプトの書き方や実装方法の認識に間違いがあれば指摘していただけると助かります。
回答よろしくお願いします。

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

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

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

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

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

guest

回答1

0

ベストアンサー

「X軸周りに180°回転」も「Z軸周りに180°回転してX軸周りに0°回転してY軸周りに180°回転」も同じ姿勢を表現するので、プログラマーの意図とは違っていてもUnityとしては正しくオイラー角を返していることになるのでしょう。
マニュアルには下記のようなオイラー角使用時の注意書きがありました。

ただし、スクリプトでオイラー角を使用したほうがよい場合があります。そのような場合は、角度を変数に代入し、オイラー角として回転に適用するためにだけ使用することが重要です。オイラー角をクォータニオンから取得することは可能ですが、取得、変更、再適用すると、問題が発生します。

表現がややこしいですが、今回のようなケースでは、現在の回転角をlocalEulerAnglesで毎回取得するのはやめて、現在の角度を表す変数を別途用意しておき、回転のたびにその変数に対して角度を加減算するようにして、その結果を回転したいオブジェクトに適用する形にしろということかと思います。もしこの方式に変えるとしたら、回転対象ごとにそれぞれ現在の角度を保存する変数を追加することになり、少々面倒かもしれません。

他の手としては、新しい角度の設定部分でvar newRotation = currentRotation + new Vector3(Mathf.Approximately(currentRotation.z, 0.0f) ? 90 : -90, 0, 0);といった風に、他の軸の回転状態を見てX回転量の正負を切り替える手もあるかもしれませんが、クォータニオン主体で回転させた方が余計な条件判定がいらず、シンプルでいいような気がします。

ご提示のコードのオイラー角部分をクォータニオン方式に置き換えるとこんな感じでしょうか。

C#

1 IEnumerator RotateAxisY() 2 { 3 4 var currentRotation = rotatingAxis.transform.localRotation; // localEulerAnglesの代わりにlocalRotationを取得 5 var newRotation = currentRotation * Quaternion.AngleAxis(90, Vector3.right); // currentRotationを(1, 0, 0)軸周り90°回転したものをnewRotationとする 6 7 Debug.Log(currentRotation.eulerAngles); 8 Debug.Log(newRotation.eulerAngles); 9 10 for (float t=0;t<0.5f;t+=Time.deltaTime) 11 { 12 Quaternion rotation = Quaternion.Slerp(currentRotation, newRotation, t * 2); // 中間の回転を求めるのにSlerpを使いましたが、より高速なLerpを使ってもほとんど違和感はないかと思います 13 14 //rotatingAxisは回転させるオブジェクト 15 rotatingAxis.transform.localRotation = rotation; 16 17 yield return null; 18 } 19 20 //ちょうど90度刻みになるように調整 21 rotatingAxis.transform.localRotation = newRotation; 22 23 }

投稿2017/10/25 21:07

Bongo

総合スコア10807

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問