【Unity】オブジェクトをターゲットの方向に回転させる

キャラが対象物を見続けるようにする方法ってないの~?

Unityの便利メソッドで楽に実装できるわ!

Unityには、特定のターゲットに回転させる便利メソッドがいくつか用意されています。

本記事では、以下動画のように特定の対象物にオブジェクトを回転させる方法を紹介します。

この作品はユニティちゃんライセンス条項の元に提供されています

Transform.LookAt()メソッドを使う

オブジェクトをターゲットの方向に向かせる動作は、Transform.LookAt()メソッドの呼び出しだけで実現できます。

メソッドの形式は次の通りです。

public void LookAt(Transform target);
public void LookAt(Transform target, Vector3 worldUp = Vector3.up);

第1引数のtargetには、振り向く対象となるターゲットのTransformを指定します。
第2引数のworldUpには、ワールド空間における上方向のベクトルを指定します。第2引数は省略可能で、省略した場合はVector3.upが指定されます。 [1]

サンプルスクリプト

ターゲットに振り向くスクリプトは次のようになります。

LookAtTarget.cs
using UnityEngine;

/// <summary>
/// ターゲットに振り向くスクリプト
/// </summary>
internal class LookAtTarget : MonoBehaviour
{
    // 自身のTransform
    [SerializeField] private Transform _self;
    
    // ターゲットのTransform
    [SerializeField] private Transform _target;

    private void Update()
    {
        // ターゲットの方向に自身を回転させる
        _self.LookAt(_target);
    }
}

オブジェクトをサッと切り替えて試せるように、自身とターゲットをそれぞれインスペクタから指定できるようにしました。 [2]

このスクリプトを適当なゲームオブジェクトにアタッチし、Selfに回転させたいオブジェクト、Targetに振り向く対象のオブジェクトを指定します。

実行結果

ゲームを実行すると、次のような結果になります。

頭の向きがおかしいね…

頭のボーンの座標系が想定と異なっているみたいね。

Transform.LookAt()メソッドは、回転させるオブジェクトの前方をz軸正方向(青矢印方向)として計算します。

この条件に合致しているオブジェクトなら問題ありませんが、デモのユニティちゃんのボーンのように、y軸負方向(緑矢印の反対方向)を前方として扱いたい場合があるかもしれません。勿論、それ以外の方向の場合も考えられるでしょう。

このような場合は、Transform.LookAt()メソッドは使えず、ベクトルとクォータニオンを使って自前で計算処理を実装する必要があります。

ベクトルとクォータニオンで回転させる

ずれた向きの分の回転(回転補正)をした後に、振り向き回転を行うことで解決できます。

処理の流れは、

ターゲットへの向き計算→回転補正→振り向き回転→回転の反映

となります。

ターゲットへの向きの計算

自分自身のゲームオブジェクトからターゲットへのゲームオブジェクトの向きは、次式で計算できます。

向きの計算式
向き = ターゲットのワールド座標 – 自身のワールド座標

座標(位置)はワールド座標です。 [3]

得られる向きはベクトルです。向きベクトルは長さ1に正規化するのが一般的ですが、今回はその必要がないため正規化せずに向きとして扱うことにします。

スクリプトは以下のようになります。

var dir = _target.position - _self.position;

ターゲットの方向に向く回転の計算

向きの回転は次のクォータニオンで計算できます。

public static Quaternion LookRotation(Vector3 forward, Vector3 upwards= Vector3.up);

第1引数forwardには、向きたい方向のベクトルを指定します。今回は、自身からターゲットへの向きを指定すればOKです。
第2引数upwardsには、ワールド空間における上方向のベクトルを指定します。省略した場合は、Vector3.upが指定されます。

Transform.LookAtと引数フォーマットが一緒です。

戻り値はクォータニオンとなります。

戻り値をそのままTransform.rotationに代入すればTransform.LookAt()メソッドを実行するのと同じ挙動になります。

計算は以下のようになります。

var lookAtRotation = Quaternion.LookRotation(dir, Vector3.up);

向きの回転補正

Quaternion.LookRotation()またはTransform.LookAt()メソッドが想定している前方はz軸の正方向すなわちVector3.forwardです。 [4]

想定していない向きの場合は、その向きからVector3.forwardへ回転するクォータニオンで回転補正を行えば解決します。

ある向きからもう一方の向きに回転するためのクォータニオンとして、次のメソッドが用意されています。

public static Quaternion FromToRotation(Vector3 fromDirection, Vector3 toDirection);

第1引数fromDirectionには、開始となる向きを指定します。
第2引数toDirectionには、目標となる向きを指定します。
戻り値はクォータニオンです。

したがって、回転補正処理は次のようになります。

var offsetRotation = Quaternion.FromToRotation(current, Vector3.forward);

サンプルスクリプト

ここまでの計算処理を踏まえて一連の処理を実装すると、次のようになります。

LookAtTargetOffset.cs
using UnityEngine;

/// <summary>
/// ターゲットに振り向くスクリプト(オフセット考慮版)
/// </summary>
public class LookAtTargetOffset : MonoBehaviour
{
    // 自身のTransform
    [SerializeField] private Transform _self;

    // ターゲットのTransform
    [SerializeField] private Transform _target;

    // 前方の基準となるローカル空間ベクトル
    [SerializeField] private Vector3 _forward = Vector3.forward;

    private void Update()
    {
        // ターゲットへの向きベクトル計算
        var dir = _target.position - _self.position;

        // ターゲットの方向への回転
        var lookAtRotation = Quaternion.LookRotation(dir, Vector3.up);
        // 回転補正
        var offsetRotation = Quaternion.FromToRotation(_forward, Vector3.forward);

        // 回転補正→ターゲット方向への回転の順に、自身の向きを操作する
        _self.rotation = lookAtRotation * offsetRotation;
    }
}

最初のサンプルスクリプトに加えて、任意のベクトルを前方として指定できるように_forwardフィールドを用意しました。

このスクリプトも最初のサンプル同様にアタッチし、同じ要領でインスペクタに値を設定します。

実行結果

これで顔面を前方としてターゲットの方向に回転できるようになりました!

さいごに

今回は、特定のターゲットにオブジェクトを回転させる方法を紹介しました。

本記事で述べている回転は、ターゲットに瞬時に回転させるもので、徐々に(滑らかに)回転させるものではありません。

また、ユニティちゃんのようなモデルを回転させる場合は、首の可動範囲に制限を設けたり、IKを設定したりする必要がありますが、今回は割愛しています。

この辺の話は、別の記事として紹介していきたいと思います。

参考サイト