【Unity】Cinemachineカメラを直線移動に制限する

こじゃらこじゃら

Cinemachineのカメラを直線上で移動させたい場合はどうすればいいの~?

このはこのは

移動範囲を制限するExtensionを作ればできるわ。

Cinemachineで制御するカメラの移動範囲を直線上に制限する方法の紹介です。

本記事の内容を実践すると、次のように直線上をバーチャルカメラが移動するカメラワークが実現できます。

動画は、移動軸をx軸のみに固定して動かす例です。

Cinemachineのバーチャルカメラには、このような移動範囲を制限する機能はないため、計算後の位置をExtension(機能拡張)を使って書き換える必要があります。

本記事では、直線または線分に移動範囲を制限するExtensionを作り、移動範囲を制限する方法を紹介します。

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

動作環境
  • Unity2021.1.23f1
  • Cinemachine2.7.8

スポンサーリンク

前提条件

事前にCinemachineパッケージがインストールされ、バーチャルカメラがセットアップされているものとします。

ここまでの手順がわからない方は、以下記事をご覧ください。

Extensionとは

Cinemachineのバーチャルカメラの動きを拡張するための機能です。

参考:Extension | Cinemachine | 2.6.0

本記事では、移動範囲を制限するExtensionを自作することで目標の動きを実現させます。Extensionの実装方法の詳細は、以下記事で解説しています。

移動範囲を直線上に制限する計算方法

次のように、カメラ位置を指定された直線上に投影することで、移動範囲を制限することができます。

直線は指定された位置から指定された向きに伸びるレイとして定義することとします。反対向きも移動範囲に含むものとします。

このような直線を表すレイは、Ray構造体を使うと楽です。

レイに投影した座標は、Vector3.Project()メソッドを使うと簡単に計算できます。

public static Vector3 Project(Vector3 vector, Vector3 onNormal);

これは、あるベクトルを別のベクトル上に投影するメソッドです。第1引数に投影対象のベクトル、第2引数に投影後のベクトルを指定します。

今回の場合は、Rayの原点を基準にして、カメラ位置(位置ベクトル)をRayの方向ベクトルに投影すれば良いです。

したがって、直線上に制限したカメラ座標を求めるまでのコードは次のようになります。

Vector3 origin;
Vector3 direction;
Vector3 cameraPosition;

・・・(中略)・・・

// レイの定義
var ray = new Ray(origin, direction);

// レイの原点を基準にカメラ位置を投影
cameraPosition -= ray.origin;
cameraPosition = Vector3.Project(cameraPosition, ray.direction);
cameraPosition += ray.origin;

レイの原点を基準にベクトル投影する必要があるため、投影処理の前後でレイの原点を足し引きする必要があることにご注意ください。

移動範囲を直線上に制限するExtensionの実装

CinemachineExtensionクラスの継承クラスを実装することで、Cinemachine側に自作のExtensionを認識させることができます。

参考:Class CinemachineExtension | Cinemachine | 2.8.0

以下、Cinemachineバーチャルカメラの移動範囲を直線上に制限するExtensionのサンプルです。

CinemachineLineLimit.cs
using Cinemachine;
using UnityEngine;

public class CinemachineLineLimit : CinemachineExtension
{
    // 直線が通る点
    [SerializeField] private Vector3 _origin = Vector3.up;

    // 直線の向き
    [SerializeField] private Vector3 _direction = Vector3.right;

    // Extensionコールバック
    protected override void PostPipelineStageCallback(
        CinemachineVirtualCameraBase vcam,
        CinemachineCore.Stage stage,
        ref CameraState state,
        float deltaTime
    )
    {
        // カメラ移動後のみ処理を実行することとする
        if (stage != CinemachineCore.Stage.Body)
            return;

        // レイを定義
        var ray = new Ray(_origin, _direction);
        // 計算されたカメラ位置
        var point = state.RawPosition;

        // レイ上に投影したカメラ位置を計算
        point -= ray.origin;
        point = Vector3.Project(point, ray.direction);
        point += ray.origin;

        // 投影点をカメラ位置に反映
        state.RawPosition = point;
    }

    #region DrawGizmos

    private const float GizmoLineLength = 1000;

    // 移動範囲をエディタ上で表示(確認用)
    private void OnDrawGizmos()
    {
        if (!isActiveAndEnabled) return;

        var ray = new Ray(_origin, _direction);

        Debug.DrawRay(
            ray.origin - ray.direction * GizmoLineLength / 2,
            ray.direction * GizmoLineLength
        );
    }

    #endregion
}

パラメータの調整がしやすいように、移動範囲をGizmoで表示するようにしました。

上記スクリプトをCinemachineLineLimit.csとしてエディタに保存します。すると、バーチャルカメラのExtension > Add Extensions項目を選択したときに、一覧にCinemachineLineLimitが表示されるようになります。

CinemachineLineLimitを選択すると、該当のExtensionが追加されます。

Originに直線が通る点の座標、Directionに線の向きを指定して移動範囲を調整します。

実行結果

バーチャルカメラが直線に沿って追従するようになりました。

さいごに

Cinemachineのバーチャルカメラの移動範囲を直線や線分上に制限する方法を紹介しました。

このようにExtensionを用いることで、バーチャルカメラの計算結果を書き換えて移動範囲を制限するカメラワークが作れます。

Cinemachine側でも、このような移動範囲をコライダーにより制限するExtensionが用意されています。これらのExtensionの使い方は以下記事で解説しています。

ご参考にしていただけると幸いです。

参考サイト

スポンサーリンク