【Unity】Cinemachineと自作のカメラスクリプトを共存させる

こじゃらこじゃら

自分で作ったカメラ制御スクリプトとCinemachineを一緒に使うことは可能なの~?

このはこのは

可能だわ!カメラスクリプトをバーチャルカメラとして振舞わせることで実現できるわ。

自作のカメラ制御スクリプトとCinemachineを共存させる方法の紹介です。

結論を述べると、何もしないダミーのバーチャルカメラを配置し、これを自作スクリプトから制御させることで実現可能です。

参考:Cinemachine External Camera | Cinemachine | 2.6.0

既に自作スクリプトでカメラ制御している場合、下図のようにカメラワークシステムを変更する形になります。

カメラワークをCinemachine側で管理させることによって、次のメリットが得られます。

Cinemachineを使うメリット
  • 複数カメラワークとのブレンド処理ができる
  • カメラワーク切り替えが楽になる
  • カメラワークの調整や機能拡張が楽になる
  • 大規模で複雑なカメラワークの実装に向いている

特に、自作スクリプトだけでカメラワークを管理する場合、複数のカメラ切り替え管理などが非常に大変になりがちですが、この問題はCinemachine側で吸収してくれます。

本記事では、このような自作のカメラワークスクリプトとCinemachineを共存させる方法について解説していきます。

UCL UCL

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

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

スポンサーリンク

前提条件

既に自作のカメラスクリプトが適用されているシーンを想定します。

ここにCinemachineを適用していきます。

Cinemachineのインストール

まず、Cinemachineを扱うためにCinemachineパッケージのインストールが必要になります。

UnityプロジェクトにCinemachineパッケージがインストールされていない場合は、パッケージマネージャよりCinemachineをインストールしてください。

インストールの仕方がわからない方は、以下記事をご覧ください。

Cinemachineの適用

自作スクリプトとCinemachineを共存させるために、シーンにCinemachineを適用していきます。

注意点として、正しく適用するまではカメラが一時的に動かなくなります。そのため、予めシーンのバックアップを取っておくことをお勧めします。

バーチャルカメラの配置

メニューのGameObject > Cinemachine > Virtual Cameraの順に選択し、シーン上にバーチャルカメラを配置します。

すると、CM vcam1という名前でバーチャルカメラがシーンに追加されます。

このバーチャルカメラは、自作スクリプトとCinemachineとの橋渡しをするために使います。

バーチャルカメラの設定

バーチャルカメラオブジェクトのCinemachineVirtualCameraコンポーネントのBodyAim項目をDo nothingに選択します。Do nothingは何もカメラワーク制御をしないという意味の設定です。

これで、何もしないバーチャルカメラが出来上がります。

シーンに配置したバーチャルカメラは、ゲームを実行していないときもTransformの値を書き換えるため意図しない位置や向きになっていることがあります。

この場合、バーチャルカメラオブジェクトのTransformのPositionとRotationを調整しておくと良いでしょう。

ゲームを実行してみる

ここまでの手順を実施してゲームを実行すると、大抵の場合カメラが全く動かない状態になるでしょう。自作スクリプトやエディタからTransformを変更しても値が書き換わりません。

これは、Cinemachineのカメラワークを制御するCinemachineBrainによってカメラオブジェクトのTransformが常に上書きされ続けているためです。

バーチャルカメラのTransformを弄ると、バーチャルカメラのTransform値がそのままメインカメラに転送されます。

このように、CinemachineはバーチャルカメラのTransform等の情報をメインカメラ側に転送する仕組みを使ってカメラワークの切り替えやブレンドなどを実現しています。

この何もしないバーチャルカメラを自作スクリプトから制御させることで、まるで独自制御されているバーチャルカメラが存在しているかのようにCinemachineに認識させることができます。

こじゃらこじゃら

スクリプトから制御する対象のカメラをUnity標準のものからバーチャルカメラに差し替えればできるということね~

このはこのは

そう。ただし、スクリプトによっては一部修正が必要な場合もあるから、次はそこを確認していくね。

スクリプトの修正(必要ならば)

バーチャルカメラを自作スクリプトから制御させることで、Cinemachineと連携できるところまでは分かりました。しかし、Cameraコンポーネントを直接参照している場合、CinemachineVirtualCameraコンポーネントを参照する形に修正する必要があります。

実際にスクリプトの例を示しながら修正方法を提示していきます。なお、実際にどのような修正が必要かは元のカメラ制御スクリプトの実装に依存しますので、必ずご自身のスクリプトの実装を確認した上で判断してください。

重要なのは、CameraからCinemachineVirtualCameraに参照先を置き換えることです。

修正が必要なスクリプトの例

例えば、次のような自作スクリプトは修正が必要です。

MyCameraMover.cs
using UnityEngine;

// TODO : ここをバーチャルカメラに置換える
[RequireComponent(typeof(Camera))]
public class MyCameraMover : MonoBehaviour
{
    // カメラの追従対象
    [SerializeField] private Transform _follow;

    // カメラオフセット
    [SerializeField] private Vector3 _offset;

    // 制御対象のカメラ
    // TODO : ここをバーチャルカメラに置換える
    private Camera _mainCamera;

    private Vector3 _cameraPos;
    private Vector3 _cameraVelocity;

    private Vector3 TargetPosition => _follow.position + _offset;

    private void Start()
    {
        // TODO : ここをバーチャルカメラに置換える
        _mainCamera = GetComponent<Camera>();

        _cameraPos = TargetPosition;
    }

    private void Update()
    {
        // カメラのFOVをマウスホイールによって変更
        // TODO : ここをバーチャルカメラに置換える
        _mainCamera.fieldOfView -= 10 * Input.mouseScrollDelta.y;
    }

    private void FixedUpdate()
    {
        // 移動先の位置計算
        var targetPos = TargetPosition;

        // カメラ位置計算(滑らかな追従のためSmoothDamp使用)
        _cameraPos = Vector3.SmoothDamp(_cameraPos, targetPos, ref _cameraVelocity, 0.5f);

        transform.position = _cameraPos;
    }
}

これは、カメラを滑らかに追従させる制御とマウスホイールによるズームを行うスクリプトです。カメラオブジェクトに直接アタッチして使うことを想定しています。

TODOコメントで示した部分は、Unity標準カメラにアクセスしている部分で、バーチャルカメラを参照するように修正する必要がある部分です。例では、カメラのFOVを編集しているので、これをバーチャルカメラで管理しているFOVを編集するようにすれば良いです。

位置の追従処理のように、カメラのTransformを編集している部分は修正不要です。

修正したスクリプトの例

Unity標準カメラにアクセスしている部分をバーチャルカメラに置き換える修正を施したスクリプトは次のようになります。

using Cinemachine;  // Cinemachineを追加でusing
using UnityEngine;

[RequireComponent(typeof(CinemachineVirtualCamera))]
public class MyCameraMoverFixed : MonoBehaviour
{
    // カメラの追従対象
    [SerializeField] private Transform _follow;

    // カメラオフセット
    [SerializeField] private Vector3 _offset;

    // 制御対象のカメラ
    private CinemachineVirtualCamera _virtualCamera;

    private Vector3 _cameraPos;
    private Vector3 _cameraVelocity;

    private Vector3 TargetPosition => _follow.position + _offset;

    private void Start()
    {
        _virtualCamera = GetComponent<CinemachineVirtualCamera>();

        _cameraPos = TargetPosition;
    }

    private void Update()
    {
        // カメラのFOVをマウスホイールによって変更
        _virtualCamera.m_Lens.FieldOfView -= 10 * Input.mouseScrollDelta.y;
    }

    private void FixedUpdate()
    {
        // 移動先の位置計算
        var targetPos = TargetPosition;

        // カメラ位置計算(滑らかな追従のためSmoothDamp使用)
        _cameraPos = Vector3.SmoothDamp(_cameraPos, targetPos, ref _cameraVelocity, 0.5f);

        transform.position = _cameraPos;
    }
}

CameraクラスはCinemachineVirtualCameraクラスに置き換わります。CinemachineVirtualCameraはCinemachineのバーチャルカメラを表すコンポーネントクラスです。

参考:Class CinemachineVirtualCamera| Package Manager UI website

そして、FOVを編集するコードは次のように修正します。

修正前

// カメラのFOVをマウスホイールによって変更
// TODO : ここをバーチャルカメラに置換える
_mainCamera.fieldOfView -= 10 * Input.mouseScrollDelta.y;

修正後

// カメラのFOVをマウスホイールによって変更
_virtualCamera.m_Lens.FieldOfView -= 10 * Input.mouseScrollDelta.y;

CinemachineVirtualCamera.m_Lens.FieldOfViewフィールドは、バーチャルカメラで保持されるFOV情報です。Cinemachineのカメラ制御では、CinemachineBrainがこれをCamera.fieldOfViewフィールドに反映する処理を行います。

参考:Struct LensSettings| Package Manager UI website

修正したスクリプトの適用

必要な修正が済んだら、バーチャルカメラを制御するようにスクリプトを適用します。

別のオブジェクトにアタッチしなおす場合、元のカメラにアタッチしていたスクリプトを忘れずに削除しておきましょう。

実行結果

ここまで手順を正しく実行すると、自作スクリプトからバーチャルカメラを制御できるようになります。

シーンビューを見ると、バーチャルカメラが自作スクリプトから制御されていることが確認できます。

さいごに

Cinemachineと自作のカメラワークスクリプトとの共存は、何もしないダミーのバーチャルカメラを仲介することで実現できます。

様々なカメラワークをCinemachine側に統合することで、複雑なカメラ遷移やブレンドなどをCinemachine側に任せることができるようになります。

カメラワークのブレンドについては、以下記事で解説していますので、興味があればご覧ください。

参考サイト

スポンサーリンク