回転:オイラー角、Quaternion、触っていいのはどちらか
オブジェクトを回転させるとは? Quaternion とは?
欲しい角度が決まっていれば Quaternion.Euler(x, y, z)、何かを向かせたいなら Quaternion.LookRotation に RotateTowards や Slerp を合わせます。transform.rotation に直接足し算は禁止——角度ではない 4 つの数だからです。
位置もスケールも、そこに足し算できる三つの数字です。回転は Inspector で三つの数字に見えるものの、実際はそうではありません。誰もが同じ道で気づきます:transform.rotation を変数にコピーし、y に 1 を足して書き戻すと、物体が言葉にできない挙動を見せるのです(C# は transform.rotation.y += 1 を一行で許してくれません——この property はコピーを返すだけです)。
Inspector に見えているのは、実際に格納されているものではない
Unity は回転を Quaternion として保存します。x、y、z、w の四つの数字が組み合わさって、一つの軸とその周りの回転量を表すものです。四つのいずれも度数法の角度ではありません。Inspector は表示のためにこれを身近な三つの Euler 角へ変換し、入力した値はその逆に読み直しています。
角度が決まっている回転を設定する
// Face 90° to the right, level, no roll.
transform.rotation = Quaternion.Euler(0f, 90f, 0f);
// Local rotation instead: relative to the parent, not to the world.
transform.localRotation = Quaternion.Euler(0f, 90f, 0f);
// Add 90° to whatever it is now. Quaternions multiply, they do not add.
transform.rotation *= Quaternion.Euler(0f, 90f, 0f);何かの方を向いて回転する
実際に出番が多いのはこのケースです:敵はプレイヤーの方を向く、タレットは目標を追う、キャラは歩いている向きに体を変える。手順は二つだけ——欲しい回転を計算し、そこから毎フレーム少しずつ近づけることです。
using UnityEngine;
public class FaceTarget : MonoBehaviour
{
public Transform target;
public float turnSpeed = 360f; // degrees per second
void Update()
{
if (target == null) return;
Vector3 to = target.position - transform.position;
// Flatten it, or the enemy tips forward to look down at a short player.
to.y = 0f;
// LookRotation complains about a zero vector: standing exactly on the target.
if (to.sqrMagnitude < 0.0001f) return;
Quaternion wanted = Quaternion.LookRotation(to);
transform.rotation = Quaternion.RotateTowards(
transform.rotation, wanted, turnSpeed * Time.deltaTime);
}
}RotateTowards か Slerp か
RotateTowards は毎秒何度かで一定に回り、ちゃんと到達します。回る速さそのものが設計の一部である場合——すぐには振り向けないタレットなど——にはこちらを。
Slerp は近づくほど減速し、最後まで届かず、速さも残りの角度に左右されます。カメラなど、機械的ではなく柔らかく感じさせたいものに合っています。
Slerp を Slerp(a, b, speed * Time.deltaTime) と書くとフレームレートに依存します。修正法は位置の場合と同じで、1f - Mathf.Exp(-speed * Time.deltaTime) です。
何かを回し続ける
// Around its own up axis: a coin spinning on the spot.
transform.Rotate(Vector3.up * 90f * Time.deltaTime, Space.Self);
// Around the world's up axis: orbiting behaviour, unaffected by tilt.
transform.Rotate(Vector3.up * 90f * Time.deltaTime, Space.World);Gimbal lock と、自分の角度を持つべき理由
Euler 角は回転を、順に適用される三つの回りで表します。真ん中の回りが 90° に達すると、一番目と三番目が同じ軸の周りを回ることになり、自由度が一つ消えます。これが gimbal lock です。実際、一人称視点のカメラが真上を向いた瞬間に暴れる原因は、だいたいこれです。
using UnityEngine;
public class MouseLook : MonoBehaviour
{
public float sensitivity = 2f;
// Your own numbers, in degrees, never read back from the transform.
float yaw, pitch;
void Update()
{
yaw += Input.GetAxis("Mouse X") * sensitivity;
pitch -= Input.GetAxis("Mouse Y") * sensitivity;
// Clamping works because pitch is a plain float you control.
pitch = Mathf.Clamp(pitch, -80f, 80f);
transform.rotation = Quaternion.Euler(pitch, yaw, 0f);
}
}これを作るレッスン
三人称の旋回カメラ
この記事はそれだけで完結するレシピです。コースでは同じものを、5 つのタブを貫くひとつのプロジェクトの一部として作ります — カメラと入力 のレッスン 02。