Inspectorの回転欄はX・Y・Zの3つの数字なのに、スクリプトで transform.rotation を触ると突然 x, y, z, w の4つの値が現れる——Unityの回転まわりで最初につまずくポイントです。「回転なんてX, Y, Z軸の角度で十分では?」と思いきや、その直感的な表現には ジンバルロック という落とし穴が待っています。
この記事では、Unityの回転を支える Quaternion(クォータニオン) の役割とオ イラー角との使い分け、初心者がやりがちな eulerAngles 直接操作の危険性、そして LookRotation や Slerp を使った実践的な回転制御までを解説します。
この記事でわかること
- 振り向き・追従カメラ・誘導弾——ゲーム開発でQuaternionが必要になる場面
- Quaternionが「軸と回転量」で回転を表す仕組み(中身の数学は不要)
- オイラー角との使い分けと、ジンバルロックが起きる理由
eulerAnglesを毎フレーム読み書きすると回転が暴れる理由Quaternion.LookRotationとSlerpによる実践的な回転制御
動作確認環境: Unity 6 (6000.x)
ゲーム開発のどこで必要になる?
先に結論から言うと、Quaternionは数学の教養ではなく、「回転を動かす」処理すべての土台です。たとえば、どれも定番のこんな場面で使います。
- 敵がプレイヤーの方へじわっと振り向く: 向くべき方向を
LookRotationで作り、Slerpで滑らかに回す - 追従カメラがキャラクターの背後に回り込む: カメラの向きを毎フレーム補間する
- 誘導ミサイルが弧を描いて曲がる: 急には曲がれない誘導を
RotateTowards(毎秒◯度まで)で作る

逆に言えば、Inspectorに数値を打ち込むだけ(回転を「置く」だけ)ならオイラー角の知識で足ります。回転を毎フレーム動かし始めた途端に、「カメラが突然裏返る」「振り向きがガクガクする」といった悩みが出てきて、Quaternionの理解が必要になります。
Quaternionとは何か?
Quaternionとは、回転を表現するための数学的な概念で、4つの要素(x, y, z, w)を持ちます。UnityではTransformコンポーネントのrotationプロパティがこの型で、Unityの回転はすべて内部的にQuaternionで管理されています。
オイラー角が「X軸に◯度、Y軸に◯度、Z軸に◯度」と3つの軸回転の組み合わせで表現するのに対し、Quaternionは「ある1つの軸の周りに、どれだけ回転した状態か」を表現します。1回の回転として表すため軸同士が干渉せず、回転をQuaternionのまま保持・合成・補間すれば、ジンバルロックを避けられます。

4つの値の意味を理解する必要はありません。実際の開発では「Quaternionの中身は直接触らず、専用のメソッドで作って渡す」だけで十分です。
専門用語の補足
- オイラー角 (Euler Angles): X軸(ピッチ)、Y軸(ヨー)、Z軸(ロール)それぞれの回転角度で表現する方法。Inspectorの回転欄はこれです。
- ジンバルロック (Gimbal Lock): オイラー角で特定の回転(UnityではX軸が±90度)に達したとき、残り2つの軸の回転が同じ方向に重なってしまい、1つの回転の自由度を失う現象です。

補足: ゲームでの典型例はFPSカメラです。オイラー角だけでカメラを制御すると、プレイヤーが真上(X=90度)を見た瞬間に「左右を向く操作」と「首をかしげる操作」が同じ回転になってしまい、視点が急にくるっと裏返るバグにつながることがあります。UnityのTransformが内部でQuaternionを使っているのは、まさにこれを防ぐためです。
オイラー角とQuaternionの使い分け
Unityではtransform.eulerAnglesというプロパティを通じてオイラー角にアクセスできますが、これは内部のQuaternionからその都度逆算された値です。
| 特徴 | オイラー角 (transform.eulerAngles) | Quaternion (transform.rotation) |
|---|---|---|
| 表現 | 3つの角度 (X, Y, Z) | 4つの要素 (x, y, z, w) |
| 直感性 | 高い(人間が理解しやすい) | 低い(中身は直接触らない) |
| 問題点 | ジンバルロック/同じ回転に複数の表現がある | 数値の意味が直感的でない |
| 用途 | Inspectorでの設定、角度の指定 | スクリプト内での計算、回転の補間 |
初心者が躓きやすいポイント:eulerAnglesの毎フレーム読み書き
eulerAnglesへの「代入」自体は問題なく動きます。危険なのは、毎フレーム読み取って加算するような使い方です(なお、transform.eulerAngles.x += 1f;と1行で書くとCS1612でコンパイルできないので、次のように一度変数に取り出す形になります)。
// 危険なコード例: eulerAnglesを毎フレーム読み取って加算
void Update()
{
Vector3 angles = transform.eulerAngles; // 内部のQuaternionから逆算された値
angles.x += 30f * Time.deltaTime;
transform.eulerAngles = angles;
}
同じ回転でもオイラー角の表現は一通りではありません(例:X=170, Y=0, Z=0 と X=10, Y=180, Z=180 は同じ姿勢)。逆算でどちらが返るかは保証されないため、読み取った値に加算し続けると、ある角度を境に回転が突然飛んだり震えたりします。
解決策: 角度で指定したいときは、加算元の角度を自分の変数で管理するか、Quaternion.Euler()で作った回転を掛け合わせます。
// 正しいコード例1: 角度を自分の変数で管理する
private float pitch = 0f;
void Update()
{
pitch += 30f * Time.deltaTime;
transform.rotation = Quaternion.Euler(pitch, 0f, 0f);
}
// 正しいコード例2: 回転の「差分」を掛け合わせる
void Update()
{
// 現在の回転に「X軸に少し回す」回転を合成する
transform.rotation *= Quaternion.Euler(30f * Time.deltaTime, 0f, 0f);
}
補足: Quaternion同士の
*は「回転の合成」です。順番によって結果が変わり、transform.rotation * 差分は今の向きを基準に(自分のローカル軸で)差分を加え、差分 * transform.rotationはワールドの軸で差分を加えます。
実践的なQuaternionの使い方とコード例
Quaternionの真価は、特定の回転状態を生成したり、回転と回転の間を滑らかに補間したりする際に発揮されます。
1. 特定の方向を見る回転を生成する (Quaternion.LookRotation)
キャラクターを特定のターゲットの方向へ向かせたい場合、Quaternion.LookRotationが非常に便利です。
using UnityEngine;
public class LookAtTarget : MonoBehaviour
{
public Transform target; // ターゲットのTransform
void Update()
{
if (target == null) return;
// 1. ターゲットへの方向ベクトルを計算
Vector3 direction = target.position - transform.position;
// 2. その方向へ向くためのQuaternionを生成
// 第二引数は 「どちらを上とするか」。横倒しは防ぐが、相手が高い所にいれば全身が上下に傾く(実践の砲塔で分ける)
Quaternion targetRotation = Quaternion.LookRotation(direction, Vector3.up);
// 3. 生成したQuaternionを現在の回転に適用
transform.rotation = targetRotation;
}
}
2. 滑らかな回転補間 (Quaternion.Slerp)
上のコードでは一瞬でターゲットの方を向いてしまいます。「じわっと振り向く」動きにするには、Quaternion.Slerp(球面線形補間)で現在の回転と目標の回転の間を補間します。
using UnityEngine;
public class SmoothRotation : MonoBehaviour
{
public Transform target;
public float rotationSpeed = 5.0f; // 振り向きの速さ
void Update()
{
if (target == null) return;
// 1. ターゲットへ向くための目標Quaternionを計算
Vector3 direction = target.position - transform.position;
Quaternion targetRotation = Quaternion.LookRotation(direction);
// 2. 現在の回転と目標の回転の間をSlerpで補間
// 毎フレーム目標へ一定割合ずつ近づくため、最後はゆっくりの自然な振り向きになる
transform.rotation = Quaternion.Slerp(
transform.rotation, // 現在の回転
targetRotation, // 目標の回転
Time.deltaTime * rotationSpeed // 補間率
);
}
}

LerpとSlerpの違い
Vector3.Lerp(線形補間)を回転に使うと、途中の角速度が不均一になり不自然になることがあります。Quaternion.Slerpは球面上の弧に沿って補間するため、回転として常に自然です。回転の補間にはSlerpを使いましょう。補間関数の使い分けは LerpとSlerpの記事 で詳しく解説しています。
実践:砲塔が水平にだけ敵を追い、砲身だけ上下を向く
仕上げに、ここまでの道具を全部使う定番ギミックを完成させます。タワーディフェンスの砲台、TPSの固定機銃、宇宙STGの自動タレット——どれも共通する要件は「 台座は水平にだけ旋回し、砲身だけが上下を向く 」ことです。LookRotationに方向を丸ごと渡すだけでは、敵が高台に登った瞬間に台座ごと仰け反ってしまいます。
Hierarchyの組み方(回転を役割ごとの階層に分けるのがコツです):
Turret(このスクリプトを付ける)
└─ BasePivot(台座。Y軸だけ回る)
└─ BarrelPivot(砲身の付け根。ローカルX軸だけ回る)
└─ 砲身メッシュ(Z+が砲口の向き)
// ファイル名: TurretController.cs
using UnityEngine;
public class TurretController : MonoBehaviour
{
public Transform target; // 追う相手(プレイヤーなど)
public Transform basePivot; // 台座(Y軸だけ回る)
public Transform barrelPivot; // 砲身(ローカルX軸だけ回る)
public float turnSpeed = 90f; // 旋回速度(度/秒)
void Update()
{
if (target == null) return; // 対象消失時は静かに待機(毎フレーム警告を出さない)
Vector3 toTarget = target.position - basePivot.position;
// --- 台座: 高さ成分を捨てた「水平方向」だけを追う ---
Vector3 flatDirection = new Vector3(toTarget.x, 0f, toTarget.z);
// 敵が真上や同位置に来ると方向が作れない。長さ0をLookRotationに渡さない
if (flatDirection.sqrMagnitude > 0.0001f)
{
Quaternion baseGoal = Quaternion.LookRotation(flatDirection);
basePivot.rotation = Quaternion.RotateTowards(
basePivot.rotation, baseGoal, turnSpeed * Time.deltaTime);
}
// --- 砲身: 上下(ピッチ)だけを向く ---
// 照準は砲身の付け根から測る(台座から測ると、付け根の高さの分だけずれる)
// 水平距離と高低差から仰角を計算する。上を向くほどローカルXはマイナス
Vector3 fromBarrel = target.position - barrelPivot.position;
float horizontal = new Vector2(fromBarrel.x, fromBarrel.z).magnitude;
float pitch = -Mathf.Atan2(fromBarrel.y, horizontal) * Mathf.Rad2Deg;
Quaternion barrelGoal = Quaternion.Euler(pitch, 0f, 0f);
barrelPivot.localRotation = Quaternion.RotateTowards(
barrelPivot.localRotation, barrelGoal, turnSpeed * Time.deltaTime);
}
}
ポイントは2つ。 台座に渡す方向からは高さ成分を捨てる(yを0にする)ことと、 砲身は台座の子としてlocalRotationのX軸だけを動かす ことです。台座が回れば砲身も一緒に水平旋回するので、砲身は「上下」という自分の仕事だけに集中できます。回転速度をRotateTowards(毎秒◯度)で制限しているため、敵が背後に回り込んでも砲塔は「間に合わずに撃ち遅れる」というゲーム的に美味しい隙が生まれます。

仕上げに、敵を砲塔のまわりでぐるぐる走らせて眺めてみてください。台座は水平に旋回するだけ、敵が高台に登れば砲身だけがすっと持ち上がる——この2つの動きが見えれば分業は成功です。敵を真上に置いてもConsoleが静かなら、ガードもきちんと仕事をしています。
おまけ:先に知っておくと良いこと
Quaternionの基本を押さえたら、次はこのあたりが役立ちます。
- オイラー角を補 間すると遠回りする: 旋回を
Mathf.Lerp(currentY, goalY, t)のような角度の補間で書くと、350度→10度のとき、本来20度で済む旋回を340度分逆回りします。QuaternionのRotateTowards/Slerpは常に最短経路を通るので、この問題は起きません。 - 「Look rotation viewing vector is zero」の警告:
LookRotationに長さ0の方向を渡すと出ます。実践の砲塔では、敵が真上に来たときの水平方向がこれに当たるので、sqrMagnitudeのガードで飛ばしています。 - 補間の使い分けを深める: 「なぜ目標に近づくと減速するのか」「等速で回すには
RotateTowards」といった補間の実践は LerpとSlerpの記事 で詳しく扱っています。 - 物理オブジェクトの回転は
Rigidbody経由で: Rigidbodyが付いたオブジェクトをtransform.rotationで回すと物理演算と衝突します。rb.MoveRotation(targetRotation)を使うのが正解です。Rigidbodyの記事 が参考になります。 - 手軽に回すだけなら
transform.Rotate(): 毎フレーム一定量回すだけの用途なら、transform.Rotate(0f, 30f * Time.deltaTime, 0f)という簡易APIもあります。内部でQuaternionの合成をやってくれています。
まとめ
本記事では、Unity開発における回転制御の要であるQuaternionについて解説しました。
- Quaternionは「1つの軸とその周りの回転量」で回転を表し、ジンバルロックを回避できます。Unityの回転の内部表現はすべてQuaternionです。
- オイラー角はInspectorや角度指定に便利ですが、毎フレームの読み取り+加算は回転が暴れる原因になります。角度は自分の変数で管理しましょう。
- 角度から回転を作るときは
Quaternion.Euler()、方向から作るときはQuaternion.LookRotation()を使います。 - 滑らかに振り向かせたいときは
Quaternion.Slerp()、等速で回したいときはQuaternion.RotateTowards()です。 - Quaternion同士の
*は回転の合成です。中身の4つの値を直接触る必要はありません。
「作るのはEuler/LookRotation、補間はSlerp、合成は*」——この3点セットを覚えれば、Quaternionはもう怖くありません。