【Unity】三人称視点を滑らかに追従処理|Lerp(位置)とSlerp(回転)

当サイトで紹介する商品・サービス等の外部リンクは、アフィリエイト広告を含む場合があります。
スポンサーリンク
本記事を読むと以下の実行ができます

プレイヤーの後ろから滑らかに追従する、カクつきのない三人称カメラの実装

3人称視点(TPS/TPP)のゲームを作っていると、カメラをプレイヤーにそのまま追従させただけではカクついたり、キャラクターの動きに機械的に張り付いてしまったりして、なめらかさに欠ける映像になってしまうことがあります。

本記事では、位置はVector3.Lerp、回転はQuaternion.Slerpという2つの異なる補間関数を組み合わせて、自然な三人称カメラの追従処理を実装する方法を解説します。

followSpeedやrotationSpeedなどのパラメータ調整方法や、カメラの動きが不自然になったときのチェックポイントまで、実際に手を動かしながら理解できる内容になっています。

本記事は次の人におすすめ
  • Unityで三人称視点(TPS/TPP)のカメラを実装したい人
  • Unityのカメラ追従を実装したがカクつく・不自然な動きになって困っている人
  • Vector3.LerpとQuaternion.Slerpの違いや使い分けを知りたい人
  • followSpeedやrotationSpeedなどのパラメータをどう調整すればいいか分からない人
スポンサーリンク

三人称カメラの追従処理とは?

三人称カメラ(TPS/TPPカメラ)とは、プレイヤーが操作するキャラクターの背後や斜め上から映すカメラのことです。アクションゲームやオープンワールドゲームなど、多くの3Dゲームで採用されている視点です。

三人称カメラの追従処理を作るとき、意識すべきポイントは大きく分けて2つあります。

要素内容
位置追従カメラのキャラクターを一定距離後ろに保ちつづける処理
回転の追従カメラ向きをcharacter(注視点)へ向け続ける処理

transform.positionをそのままキャラクターの位置に代入するだけでは、カメラが一瞬でキャラクターの真後ろにワープしたような、機械的で不自然な映像になってしまいます。

そこで、位置と回転それぞれに補間(インターポレーション)をかけることで、カメラがワンテンポ遅れてついてくるような、滑らかな追従感を演出します。

このとき重要なのが、位置の補間にはVector3.Lerp、回転の補間にはQuaternion.Slerpという異なる関数を使うという点です。

位置は直線的な移動なので線形補間(Lerp)で十分ですが、回転は「角度」を扱うため、球面線形補間(Slerp)を使わないと不自然な回転になってしまいます。

この違いについてはコードを解説するセクションで詳しく説明します。

Vector3.LerpとQuaternion.Slerpで実装する三人称カメラ

三人称カメラの追従処理は、以下の3ステップで実装できます。

実装までの流れ
  • step1
    オブジェクトの配置

    カメラをキャラクターの子オブジェクトにせず、独立したオブジェクトとして配置する

  • step2
    カメラ追従

    LateUpdate内で、キャラクター位置+オフセットへVector3.Lerpでカメラを追従させる

  • step3
    回転によるカメラ向き合わせる

    LateUpdate内で、注視点をQuaternion.LookRotationで計算し、Quaternion.Slerpでカメラの向きを滑らかに合わせる

カメラをキャラクターの子オブジェクトにしてしまうと、キャラクターの回転がそのままカメラの回転に伝わってしまい、独立した滑らかな動きを付けられなくなります。

そのため、カメラはシーン上の独立したオブジェクトとして配置し、スクリプトで位置・回転を毎フレーム計算する構成にします。

ソースコード

using UnityEngine;

public class CameraFollow : MonoBehaviour
{
    [SerializeField] private Transform target;
    [SerializeField] private Vector3 offset = new Vector3(0f, 3.2f, -7f);
    [SerializeField] private float followSpeed = 5f;
    [SerializeField] private float rotationSpeed = 5f;
    [SerializeField] private float lookHeight = 1.5f;

    private void LateUpdate()
    {
        if (target == null)
        {
            return;
        }
        Vector3 desiredPosition = target.position + offset;
        transform.position = Vector3.Lerp(transform.position, desiredPosition, followSpeed * Time.deltaTime);

        Vector3 lookPoint = target.position + Vector3.up * lookHeight;
        Quaternion desiredRotation = Quaternion.LookRotation(lookPoint - transform.position);
        transform.rotation = Quaternion.Slerp(transform.rotation, desiredRotation, rotationSpeed * Time.deltaTime);
    }
}

解説

  • 位置の追従処理(Vector3.Lerp)
Vector3 desiredPosition = target.position + offset;
transform.position = Vector3.Lerp(transform.position, desiredPosition, followSpeed * Time.deltaTime);

まずtarget.position + offsetで、「カメラが本来いるべき理想の位置(desiredPosition)」を計算します。

offsetは(0, 3.2, -7)のように、キャラクターから見て「上に3.2、後ろに7」といった相対位置を指定するベクトルです。

次にVector3.Lerp(現在位置, 理想位置, 割合)で、現在のカメラ位置から理想位置へ少しずつ近づけます。第3引数のfollowSpeed * Time.deltaTimeが「1フレームでどれだけ理想位置に近づくか」の割合です。

Time.deltaTimeを掛けることで、フレームレートが変わっても同じ速度で追従するようになります。

Vector3.Lerpは2点間を直線的に補間する関数なので、位置の移動には自然にマッチします。
これが、いきなりtransform.position = desiredPositionと代入せず、Lerpを使う理由です。

  • 注視点の回転処理(Quaternion.Slerp)
Vector3 lookPoint = target.position + Vector3.up * lookHeight;
Quaternion desiredRotation = Quaternion.LookRotation(lookPoint - transform.position);
transform.rotation = Quaternion.Slerp(transform.rotation, desiredRotation, rotationSpeed * Time.deltaTime);

回転の処理も基本的な流れは位置と同じで、「理想の回転(desiredRotation)」を計算してから、現在の回転を少しずつ近づけます。

ただし、回転を補間する際はVector3.LerpではなくQuaternion.Slerp(球面線形補間)を使う点に注意してください。

回転は「角度」という性質上、直線的な補間(Lerp)をそのまま当てはめると、回転速度にムラが出たり、経由地点でおかしな姿勢を取ったりすることがあります。

Quaternion.Slerpは球面上を一定の角速度で補間するため、回転が常に自然な速さで進みます。位置はLerp、回転はSlerpという使い分けは、Unityのカメラ制御において基本となる考え方です。

Vector3を使った補間・イージングの考え方を紹介しています。
  • LookRotationで注視点を計算する仕組み

Quaternion.LookRotation(方向ベクトル)は、指定した方向を向く回転(Quaternion)を返す関数です。

ここでは、lookPoint – transform.position(カメラから注視点への方向ベクトル)を渡すことで、「カメラから見て、注視点の方向を向いた回転」を計算しています。

lookPointはtarget.position + Vector3.up * lookHeight、つまりキャラクターの位置から少し上にずらした点です。

キャラクターの足元をそのまま注視点にすると、カメラが常に下向きになり圧迫感のある画になってしまうため、lookHeightで見上げるような角度にならないよう調整しています。

  • LateUpdateを使う理由

このスクリプトでは、UpdateではなくLateUpdateを使っています。これは三人称カメラの実装において非常に重要なポイントです。

Unityでは、シーン内の複数のUpdateがどの順番で実行されるかは保証されていません。もしキャラクターの移動処理と、カメラの追従処理が両方ともUpdateに書かれていた場合、カメラのUpdateがキャラクターのUpdateより先に実行されてしまうことがあります。

それにより、カメラは「1フレーム前のキャラクター位置」を基準に追従することになり、映像がカクつく原因になります。

LateUpdateは、シーン内のすべてのUpdateが終わった後に必ず呼ばれることが保証されています。そのため、キャラクターの移動処理が確実に終わった後の最新位置を使ってカメラを更新でき、カクつきのない滑らかな追従が実現できます。

カメラ制御では「移動処理はUpdate、カメラ追従はLateUpdate」という役割分担を徹底しましょう。

実装

シーン上に独立して配置したカメラオブジェクトにアタッチします。

  • STEP1
    カメラをキャラクターの子オブジェクトにせず、シーン上の独立したオブジェクトとして配置する

  • STEP2
    カメラオブジェクトにスクリプトをアタッチする

  • STEP3
    InspectorのTargetに、追従したいキャラクターのTransformをドラッグ&ドロップで設定する

offset・followSpeed・rotationSpeed・lookHeightはデフォルト値のままでも動作しますが、キャラクターのモデルサイズやゲームのジャンルに応じて調整が必要になることがほとんどです。

値の目安は次の「パラメータ調整ガイド」を参照してください。

パラメータ調整ガイド

スクリプトには4つの調整用パラメータがあります。それぞれの値を変えたときに、どのような体感の変化があるかをまとめました。

パラメータ役割値を大きくすると値を小さくすると
followSpeed位置の追従の速さキャラクターにピッタリ張り付くワンテンポ遅れてついてくる(重厚感)
rotationSpeed回転の追従の速さ向きの切り替えが機敏になるゆったりと視線が追いつく
lookHeight注視点の高さ見下ろすような画角になる水平に近い、圧迫感のある画角になる
offsetカメラの距離・画角引きの画(広い視界)寄りの画(迫力のある視界)
キリのいいパラメータ値に吸着させる機能を紹介しています。

followSpeed(追従の重さ)

followSpeedは、カメラがキャラクターの理想位置にどれだけ速く近づくかを決める値です。値が大きいほど追従が速く(キャラクターに張り付くような動き)、値が小さいほど追従が遅く(ワンテンポ遅れてついてくる、重厚感のある動き)なります。

目安として、アクション性の高いゲームではfollowSpeed = 8〜10程度、じっくり見せたい探索系のゲームではfollowSpeed = 3〜5程度から調整を始めるとよいでしょう。

極端に小さくしすぎると、キャラクターが素早く動いたときにカメラが置いていかれ、画面外にキャラクターが消えてしまうことがあるので注意してください。

rotationSpeed(回転の追従感)

rotationSpeedは、カメラの向きが理想の回転にどれだけ速く追いつくかを決める値です。この値がfollowSpeedと極端に違うと、後述する「不自然な動き」の原因になりやすいため、基本的にはfollowSpeedと近い値からスタートするのがおすすめです。

lookHeight(注視点の高さ)

lookHeightは、カメラがキャラクターのどの高さを見つめるかを決める値です。キャラクターの頭上あたり(1.5前後)に設定すると自然な見下ろし角度になり、値を大きくしすぎるとカメラが必要以上に見下ろす形になります。

逆に0に近づけると、キャラクターの足元付近を注視点にすることになり、水平に近い圧迫感のある画角になります。

offset(カメラ距離・画角)

offsetはキャラクターから見たカメラの相対位置で、(左右, 上下, 前後)のベクトルで指定します。Y成分を大きくするとカメラの高さが上がり、Z成分(マイナス方向)を大きくするとキャラクターとの距離が離れて引きの画になります。

壁際でカメラがめり込まないようにする「衝突回避」は本記事のスコープ外としていますが、offsetの距離を調整するだけでも、狭い通路でのめり込みはある程度軽減できます。

まとめ

本記事では、Unityで三人称カメラの追従処理を実装する方法を解説しました。

  • 位置の追従にはVector3.Lerp、回転の追従にはQuaternion.Slerpを使う(直線補間と球面補間の使い分け)
  • Quaternion.LookRotationで注視点への方向を計算し、lookHeightで見上げ角度を調整する
  • カメラの更新処理はUpdateではなくLateUpdateに書き、実行順序によるカクつきを防ぐ
  • followSpeedとrotationSpeedは近い値に揃えることで、首振りのような不自然な動きを防げる

まずは本記事のパラメータの目安値から始めて、実際にゲームを動かしながらfollowSpeed・rotationSpeed・lookHeight・offsetの4つを少しずつ調整し、自分のゲームに合った「気持ちいい」カメラワークを探ってみてください。

壁のめり込み対策やCinemachineの活用など、さらに踏み込んだカメラ制御は次のステップとして挑戦してみるとよいでしょう。

タイトルとURLをコピーしました