Transform.RotateとQuaternion.Eulerでオブジェクト回転
ゲームを作っているとオブジェクトを回転させたい時など出てきたりします。
例えばルーレットとか、方位をコンパス。またはプレイヤーがやられた時に回転させるなどでしょうか。
今回はシンプルにゲームオブジェクトを回転させてみたいと思います。
Unity2Dでやってみます。
こんな感じでクルクル回してみましょう。
回転の仕組み
そもそも回転はどのようにして行われるのか確認してみます。
ゲームオブジェクトのInspectorを見るとTransformコンポーネントにRotationというものがあります。
この値を変化させると回転出来そうですね。
ではx,y,xを順番に変化させていってみましょう。
x,y,zの回転
適当なオブジェクトを配置してそれを回してみましょう。
まずはxから変化させてみます。
続いてyをやってみます。
ケバブ屋さんのクルクル回っているやつみたいです。
最後にZの値を変化させてみます。
このような感じで回転しました。
この値はX、Y、Zそれぞれどれを軸にして回転させるかというものです。
3Dにしてみるとわかりやすいと思ったので図にしました。
それぞれの軸を回転させると先程のような回転になります。
私は最初Xを回転させたら左右に、Yを回転させたら上下に回るだろうと勘違いしていました。
そしてどうしてZを回転させるとクルクル回るのかがイマイチ理解出来ませんでした。
それぞれの軸を中心に回ると言うことを理解したら納得できました。
すんなり理解できる方は良いのですが、私は理解力が乏しいので図にしてみたら理解できました。
今回は2Dのオブジェクトを回転させますので、正面を向いた状態で回すにはZ軸の値を変化させてあげれば良いということになります。
では早速やっていきましょう。
一定の速度で回転させてみる
まず回転させたいゲームオブジェクトを配置し、回転用のC#スクリプトをアタッチしておきましょう。
私は今回ArrowというゲームオブジェクトにRotateControllerというスクリプトをアタッチしました。
RotateContorllerには回転の速度と回転の角度をInspectorから設定したいので以下のようにしておきました。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RotateController : MonoBehaviour
{
public float rotateSpeed,rotateAngle;
続いてUpdate関数内でゲームオブジェクトのRotationの値を変化させてみましょう。
Transform.Rotate
Rotationの値を変化させるにはいくつか方法がありますが、まずは直感的にわかりやすいTransform.Rotate関数をUpdate内で使ってみます。
Update
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RotateController : MonoBehaviour
{
/* 略 */
void Update()
{
transform.Rotate(0, 0, rotateAngle * Time.deltaTime * rotateSpeed );
}
こんな感じです。引数はそれぞれx,y,zの順番となっています。
今回はz軸を回転させるので第三引数のみに値を入れています。
RotateSpeedを10,RotateAngleを1にして実行してみます。
このようにゆっくり動きました。
これはrotateAngleが1度の角度、それにTime.deltaTime(前のフレームからの経過時間)を掛けて1秒間に1度の回転をさせ、さらにrotateSpeedの10を掛けています。
もっと早く回してみましょうか。RotateSpeedは10にしたままで、RotateAngleの方を100にしてみました。
1秒間に100度回転する10倍ということになります。
こんな感じで早く回りました。
Quaternion.Euler
先ほどはTransform.Rotateで回転させましたが、これ以外にも Quaternion.Eulerというメソッドで回転させることも出来ます。
どのように使うのかまず実際にコードを書いてみましょう。
Update
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RotateController : MonoBehaviour
{
/* 略 */
void Update()
{
transform.rotation *= Quaternion.Euler(0, 0, rotateAngle * Time.deltaTime * rotateSpeed);
}
ここで出てくるQuaternion.Eulerは、オブジェクトを回転させる別の方法です。この方法は少し複雑に見えるかもしれませんが、実際にはただ回転させたい角度を設定しているだけです。
引数もTransform.Rotateと同じくx,y,zの順番です。
QuaternionとかEulerってなんなの?ってなると思います。
簡単ですがそれぞれについて触れておきます。
Quaternion
Quaternion(クォータニオン)は、3D空間での回転を表現する数学的なツールです。
普通の数字とは違い、回転をより複雑に、でも正確に表すことができる特別な数値のセットです。
回転の「ねじれ」をうまく扱い、予期しない動き(ジンバルロックと呼ばれる問題)を防ぎます。
これは、空間内で物体がどう回転するかを正確に伝えるために非常に便利だそうです。
Euler
Euler角(オイラー角)を計算するメソッドです。
X軸、Y軸、Z軸の周りの回転を示す3つの数値で、より直感的に回転を理解できる方法です。
例えば、「上を向いて(X軸)、右に傾いて(Y軸)、ぐるっと回って(Z軸)」というような回転を表します。
しかし、Euler角を使うと、特定の状況で回転がおかしくなることがあります(ジンバルロックと呼ばれるそうです)。
だから、内部的にはQuaternionが使われることが多いみたいです。
詳しく知りたい方は以下にリンクを貼っておきます。
読んでみても難しいです。
とりあえずこんなふうに回転させることもできると覚えておいてください。
それではどうなるか実際に動かしてみましょう。
先ほどと同じようにクルクル回りだしました。
特定の角度に回転させてみる
今まではUpdate関数内でクルクル回していましたが、次は特定の角度に回転させることを実験してみます。
試しに90度傾ける回転を実装してみます。
目標の角度を設定するためのfloat型のtargetAngle、そして現在の角度を保持しておくcurrentAngleというフィールドを用意します。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RotateController : MonoBehaviour
{
public float rotateSpeed,rotateAngle, targetAngle;
private float currentAngle = 0.0f;
/* 略 */
ではまずTransform.Rotateを使ってやってみましょう。
Transform.Rotateで目標の角度に回転させる
考え方として現在の角度がターゲットの角度に達するまで繰り返し処理をすれば良さそうに思います。
それをコードに書いてみましょう。
/* 略 */
void Update()
{
//目標に達していない場合は回転させる
if(currentAngle < targetAngle)
{
//このフレームで回転させる角度を取得
float rotateAmount = rotateAngle * Time.deltaTime;
//現在の角度に足して更新する
currentAngle += rotateAmount;
//現在の角度がターゲット以上になってしまった場合
if(currentAngle > targetAngle)
{
//回転させる量から差分を引く
rotateAmount -= currentAngle - targetAngle;
}
//オブジェクトを回転させる
transform.Rotate(0, 0, rotateAmount);
}
}
実行してみるとこのように90度で止まります。
今回は左回りですが、右回りにしたい場合はどうなるでしょうか。
現在の角度がターゲットの角度より大きい場合に現在の角度から毎フレーム回転させる量をマイナスしていけば良さそうです。
/* 略 */
void Update()
{
if(currentAngle > targetAngle)
{
float rotateAmount = -rotateSpeed * Time.deltaTime;
currentAngle += rotateAmount;
if(currentAngle < targetAngle)
{
rotateAmount += targetAngle - currentAngle;
}
transform.Rotate(0, 0, rotateAmount);
}
}
先程の逆ですね。
プラスでもマイナスでも対応したいのであればtargetAngleの値をマイナスかどうかif文でチェックし、左回り、右回りの処理を書いてあげればいいと思います。
それでは実際に動かしてみます。
プラスでもマイナスでも動くようになりました。
Quaternion.Lerpを使って回転させてみる
では次にQuaternionを利用して同じように回転させてみましょう。
こちらのほうがコードは簡潔になります。
public class RotateController : MonoBehaviour
{
public float rotateSpeed,rotateAngle, targetAngle;
Quaternion targetRotation;//ターゲット回転用の変数
void Start()
{
//目標とする回転を設定。
targetRotation = Quaternion.Euler(0, 0, targetAngle);
}
// Update is called once per frame
void Update()
{
//現在の回転から目標の回転に徐々に移行。
transform.rotation = Quaternion.Lerp(transform.rotation, targetRotation, rotateSpeed * Time.deltaTime);
//LerpではなくSlerpのほうがより自然な動くをする。但し処理のコストは高くなるみたい
//transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, rotateSpeed * Time.deltaTime);
//現在の回転と目標の回転が0とほぼ等しいかどうかをチェック
if (Mathf.Approximately(Quaternion.Angle(transform.rotation, targetRotation), 0f))
{
//等しいと判断されたらターゲットの回転にする
transform.rotation = targetRotation;
}
}
この様になりました。
StartでQuaternion型のtargetRotationを設定し、Update内でLerp(またはSlerp)で現在の回転と目標の回転の値を近づけていく(2つのクォータニオン値の間を補間)ことをしています。
回転終了に近づくにつれzの値の変化がすごく小さくなっていきますので、Mathf.Approximatelyを使ってほぼ同じ値かどうかを判断して最終的にターゲットの角度にしています。
実際に動かしてみます。
このように回転させることが出来ました。
Transform.RotateとQuaternion.Lerpのどちらを使うかはケースによって異なると思います。
力を加えて回転させてみる
では最後にゲームオブジェクトに力を加えて回転させてみようと思います。
この記事の冒頭にあった画像のような感じですね。
Rigidbody2Dコンポーネント追加
力を掛けて回すということは物理演算をする必要があります。
そこでゲームオブジェクトにRigidbody2Dを追加して必要な設定していきましょう。
赤い線のところが重要です。
Angular Dragは回転抗力という値で、この値はオブジェクトの回転を減速させるために使います。
初期値では0.05となっていますが、これだと回転がなかなか終わらないので1にしています。
次にConstraintsです。
ゲームオブジェクトが動かないようにFreeze PositionのXとYにチェックを入れておきます。
今回はYだけでも良さそうですが、Xに動かす事も無いので両方にチェックしてあります。
ここまで出来たら続いてスクリプトの方を書いていきます。
Rigidbody2D.AddTorque
Start時に回転の力をオブジェクトに加えてクルクル回しましょう。
Rigidbody2DのAddTorqueメソッドを使います。
このような感じになりました。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RotateController : MonoBehaviour
{
public float rotateSpeed,rotateAngle, targetAngle;
Rigidbody2D rb2d;// Rigidbody2D コンポーネント
float threshold = 0.01f;//停止判断の閾値
bool isStop;//停止状態のフラグ
void Start()
{
//Rigidbody2D コンポーネントを取得
rb2d = GetComponent();
// 初期トルクを追加
rb2d.AddTorque(rotateSpeed, ForceMode2D.Impulse);
}
void Update()
{
// 回転速度が閾値以下になったら停止
if (Mathf.Abs(rb2d.angularVelocity) < threshold && !isStop)
{
isStop = true;// 停止フラグを立てる
Debug.Log("stop");// コンソールに停止をログ出力
//回転を完全に止める
rb2d.angularVelocity = 0f;// 角速度を0に設定
rb2d.velocity = Vector2.zero;// 速度を0に設定
}
}
}
こんな感じでしょうか。
Start時にAddTorqueで回転させています。
ForceMode2D.Impulseは一瞬で大きな力をオブジェクトに加えるときに使用します。
これは、ボールをパンチする、バットでボールを強く打つ、またはロケットが打ち上げられる瞬間のような、短い時間で大きな力が作用する状況をシミュレートするのに適しています。
「一瞬でぐっと押す力」のようなものだと思ってください。
Mathf.Abs(rb2d.angularVelocity)はangular velocity(角度速)の絶対値を取得しています。Mathf.Absは値の正負にかかわらず、その数を取得します。(Mathf.Abs(-1)だったら1みたいな感じ)
シンプルに言うと「オブジェクトがどれだけ速く回転しているか」を示す値であり、その値が小さければ小さいほど、オブジェクトの回転は遅いことを意味します。
その値とthresholdの値を比較して、まだ止まっていない場合は止める処理に入る感じです。
では実際に動かしてみましょう。
まずはrotateSpeedが小さい値です。
続いて値を大きくしてみます。
このように早く回転するようになりました。
以上簡単ではありますがUnityでゲームオブジェクトを回転させる方法の紹介でした。
ゲームオブジェクトの移動とは若干異なりますが、慣れるとそんなに難しくないと思います。
理論を突き詰めると大変そうですが。。
この内容をGitHubに公開しておきますのでよかったら参考にしてみてください。
GitHub GameObjectRotate
関連記事
TextMeshProでリンクを作成する方法とそのバグの解消法
今回はTMPのリンクを生成するLinkタグについて紹介したいと思います。
ちょっとしたハマりポイントもあるのでそちらについても触れていきたいと思います。
UnityでCSVを活用したメッセージウィンドウの作り方
メッセージの内容を変更したい時や、新しいメッセージを追加したい時にCSVファイルを使えば、メッセージの編集や追加が簡単になり、開発がスムーズになります。
Unity2D ドット絵の簡単なアニメーションを作ってみる
Unity2Dで簡単なドット絵のアニメーションを作成します。スプライトを切り替えることでプレイヤーが歩いているように見えるアニメーションを作成してみます。
この方法を応用することでキャラクターに色んな動きや表示を付け加えることができると思います。
ChatGPTの指示通りUnityゲームを作ってみる
最近ではChatGPTを始め様々な大規模言語モデルの対話型AIが公開されていますね。
そこで今回はChatGPTの指示に従ってゲームを作ってみる実験をしてみます。
Unityで9-sliceテクニックを使った効果的なUI作成方法
UI作成時に非常に便利なテクニック9-sliceをやってみます。
とても簡単に使える技です。
Unity2Dで背景の無限スクロールを実装する
色んなゲームで背景がスクロールする仕組みは使われています。
今回はタイル状の背景を並べて上下左右に無限にスクロールする仕組みを作ってみたいと思います。
最後までご覧頂いてありがとうございました。