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

こじゃらこじゃら

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

このはこのは

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

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

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

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

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

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

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

スポンサーリンク

前提条件

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

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

Extensionとは

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

参考:Extensions | Cinemachine | 2.8.0

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

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

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

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

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

平面への点投影は、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を用いると楽です。

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

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

参考サイト

スポンサーリンク