【Unity】Cinemachineカメラを平面上で移動させる

Cinemachineのカメラを平面に沿って動かしたい場合はどうすればいいの~?

平面に移動範囲を制限するExtensionを作れば可能だわ。

Cinemachineで制御するバーチャルカメラを平面に限定して移動させる方法の紹介です。

結論を述べると、カメラ位置を平面上に制限するExtension(機能拡張)を自作して適用すれば実現できます。

Extensionを用いると、バーチャルカメラに様々な効果を加えることが可能です。

【Unity】Cinemachineをスクリプトで機能拡張する
Cinemachineはスクリプト無しでも様々なカメラワークが作れるのが特徴ですが、独自のスクリプトで機能拡張することで、更に凝ったカメラワークを作成する事も可能です。 これはExtension(拡張機能)としてコンポー ...

本記事では、このようなExtensionを自作して、Cinemachineのバーチャルカメラを平面に沿って移動させる方法を解説します。

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

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

前提条件

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

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

【Unity】滑らかに(遅れて)追従するカメラを簡単に実装する方法
Cinemachineを用いてキャラクターを追従するカメラワークを実装する方法の解説記事です。 キャラクターを追従するカメラワークはスクリプトでも実装できますが、Cinemachineを使うことで次のメリットが得られます ...

Extensionとは

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

参考:Extensions | Cinemachine | 2.8.0

例えば、バーチャルカメラがコライダーにめり込まないように制御したりする動きを加えたい場合などに使います。

このExtensionは自作することもできます。

本記事では、バーチャルカメラの移動範囲を制限するExtensionを自作することで、平面に沿ってカメラが移動するような動きを実現していきます。

平面に移動範囲を制限する計算処理

バーチャルカメラ側で計算されたカメラ位置を平面上に投影させた位置に書き換えることで、カメラが平面に沿って移動するような動きを実現できます。

平面への点投影は、Plane構造体を使うと簡単です。

【Unity】平面の計算を楽できるPlane構造体の使い方
Unityには、平面に関する計算を楽に扱えるようにしたPlane構造体が存在します。 このPlane構造体を用いると、次のような計算を楽に行うことが可能です。 これらの計算をPlane構造体のメソッドで行うことにより、数 ...

Plane.ClosestPointOnPlane()メソッドで投影点を取得できます。

public Vector3 ClosestPointOnPlane(Vector3 point);

引数には投影したい点の位置を指定します。
戻り値から投影後の位置が得られます。

平面に投影したカメラ位置を求めるコードは次のようになります。

Vector3 planeNormal;
Vector3 planePosition;
Vector3 cameraPosition;

・・・(中略)・・・

// 平面を定義
var plane = new Plane(planeNormal, planePosition);

// 平面への投影点を求める
cameraPosition = plane.ClosestPointOnPlane(cameraPosition);

カメラ位置を平面上に制限するExtensionの実装

先述の投影点計算処理を、Cinemachineのバーチャルカメラに適用していきます。

Extensionの実装は、CinemachineExtensionクラスを継承したクラスを実装することで行います。これにより、自作のExtensionをCinemachine側に認識させることができます。

以下、バーチャルカメラ位置を指定された平面に制限するExtensionのサンプルです。

CinemachinePlaneLimit.cs
using Cinemachine;
using UnityEngine;

public class CinemachinePlaneLimit : CinemachineExtension
{
    // 平面が通る点
    [SerializeField] private Vector3 _planePosition = Vector3.up;

    // 平面の法線ベクトル
    [SerializeField] private Vector3 _planeNormal = Vector3.up;

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

        // 平面を定義
        var plane = new Plane(_planeNormal, _planePosition);
        // 計算されたカメラ位置
        var point = state.RawPosition;

        // 平面上に投影したカメラ位置を計算
        point = plane.ClosestPointOnPlane(point);

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

#if UNITY_EDITOR

    #region DrawGizmos

    private Mesh _gizmoPlaneMesh;

    private void OnDrawGizmos()
    {
        if (!isActiveAndEnabled) return;

        if (_gizmoPlaneMesh == null)
        {
            _gizmoPlaneMesh = CreateGizmoMesh();
        }

        Gizmos.DrawWireMesh(
            _gizmoPlaneMesh,
            0,
            _planePosition,
            Quaternion.FromToRotation(Vector3.up, _planeNormal)
        );
    }

    private Mesh CreateGizmoMesh()
    {
        return new Mesh()
        {
            vertices = new[]
            {
                new Vector3(-10, 0, -10),
                new Vector3(10, 0, -10),
                new Vector3(-10, 0, 10),
                new Vector3(10, 0, 10),
            },
            normals = new[]
            {
                Vector3.up,
                Vector3.up,
                Vector3.up,
                Vector3.up,
            },
            triangles = new[]
            {
                0, 2, 1,
                2, 3, 1,
            },
        };
    }

    #endregion

#endif
}

カメラの移動範囲の平面を、簡易的なGizmoで表示するようにしました。

上記スクリプトをCinemachinePlaneLimit.csとしてUnityエディタに保存します。すると、バーチャルカメラのExtension > Add Extensions項目からCinemachinePlaneLimitが選択できるようになります。

CinemachinePlaneLimitを選択すると、該当のExtensionがコンポーネントとして追加されます。

Plane Positionに平面を通る位置、Plane Normalに平面の法線ベクトルを指定して調整します。

実行結果

指定された平面上でカメラが移動できるようになりました。

さいごに

Cinemachineで制御するカメラを平面上などに移動範囲を制限したい場合、Extensionにより実現できます。

移動範囲をコライダーにより制限したい場合、最初から用意されているCinemachine ConfinerやCinemachine ColliderのExtensionを用いると楽です。

【Unity】Cinemachineカメラの移動範囲をコライダー内に制限する
Cinemachineには、バーチャルカメラの移動範囲を特定のコライダー内に制限するCinemachineConfinerという機能拡張(Extension)が存在します。 CinemachineConfinerを用いる ...
【Unity】Cinemachineカメラが障害物にめり込まないようにする
Cinemachineで制御するカメラが障害物にめり込まないようにしたい場合があります。 このような場合、Cinemachine Colliderという機能拡張(Extension)を使えば実現できます。 障害物の回避は ...

本記事で紹介した方法を応用すると、例えば直線上で移動範囲を制限するカメラワークも実現できます。

【Unity】Cinemachineカメラを直線移動に制限する
Cinemachineで制御するカメラの移動範囲を直線上に制限する方法の紹介です。 本記事の内容を実践すると、次のように直線上をバーチャルカメラが移動するカメラワークが実現できます。 Cinemachineのバーチャルカ ...

CinemachineのExtensionは、様々な付加的なカメラワークを追加できる便利な機能ですので、様々な場面で活用できるでしょう。

参考サイト