Search Unity

Unityでどこまでノーコーディングでカメラワークを実現できるか

この記事では、3人称視点とイベントカメラを組み合わせたゲームのカメラワークを、CinemachineとVisual Scriptingを用いてノーコーディングで実装しながら、Cinemachineの機能やその設計について学ぶことができます。

はじめに

Cineamchineにはゲームや映像制作のために必要なカメラの制御が一揃い実装されているので、様々なカメラワークの実装を各種コンポーネントの設定を適切に行うことで実現できます。Cinemachine単体でもノンコーディングで様々なカメラワークを実装できますが、Unity 2021から本格実装されたVisual Scriptingも活用することで、Cinemachine単体ではできないことも実装できるようになります。

本記事では、改めてCinemachineの基本的な設計をおさらいしてから、実際のゲームであるようなカメラワークの例を紹介し、その後にCinemachineとVisual Scriptingを組み合わせて下記のような簡単なゲームのカメラワークを実装する手法を紹介します。

Unityでどこまでノーコーディングでカメラワークを実現できるか、一緒に学んでいきましょう。

検証環境

  • Unity 2021.3.3f1
  • Cinemachine 2.8.6
  • Visual Scripting 1.7.8

Cinemachineとは

Cinemachineは、Unity公式が提供するカメラ制御のためのパッケージです。

ゲームではジャンルでの違いはあるとはいえ、プレイヤーの操作やプログラムで動く敵などの位置により動的にカメラの位置や姿勢を決める必要があります。そのため、カメラを制御するためにはターゲットの追従や構成、カメラワーク間のブレンドなど、さまざまなロジックの実装が必要になります。

Cinemachineにはゲームや映像制作のために必要なカメラ制御のためのロジックを一揃い実装されているため、開発者自身でカメラ制御を実装せずとも、多くのカメラワークをCinemachineに用意されている各種コンポーネントを適切に設定するだけで実現できます。

また、Cinemachineの標準機能で実現できないカメラワークがあった場合でも、拡張機能などを用いて独自に拡張することが可能です。

Cinemachineのインストール

CinemachineはPackage Manager経由でインストールできます。

まず、メニューから Window > Package Manager を選択して Package Managerウインドウを開きます。

次に、ウインドウ左上の「+」ボタン右のプルダウンをクリックして、「Unity Registry」を選択します。

すると、Unityが提供してるパッケージ一覧が表示されるので、Cinemachineを選択して、ウインドウ右下の「Install」ボタンをクリックすることで、インストールが完了します。

Cinemachineの設計について理解する

まず、Cinemachineがどのようにカメラを制御するかと、Cinemachineではどのようにカメラワークを設計していくのか、についてそれぞれ説明します。

Virtual Camera(バーチャルカメラ)

ゲーム制作ではその規模が大きくなると、場面に応じて性質の異なる複数のカメラワークを用意し、状況に応じて切り替えたいといったニーズが発生します。

例えばアクションゲームで、プレイアブル時はキャラクターを追従するサードパーソンカメラを用いて、会話などのイベントシーンに入ると、キャラクターと対象のNPCの両方を映すようなカメラワークを用いたい、というようなケースです。

このように複数のカメラワークを管理するために、Virtual Camera(以後バーチャルカメラと記載します)という仕組みが用意されています。

Cinemachineではシーンごとに実現したいカメラワークが異なる場合、下図のようにそのカメラワークごとにバーチャルカメラを配置して、アクティブバーチャルカメラを切り替えることで、ゲームのカメラワークを作り込んでいきます。

すべてのバーチャルカメラは CinemachineVirtualCamera という抽象クラスを継承し、カメラワークの性質ごとにクラスが用意されています。

それぞれのバーチャルカメラは、それぞれのカメラワークの計算結果を下記コードのような CameraState という構造体のインスタンスとして出力します。

namespace Cinemachine
{
    // CameraStateの一部抜粋
    public struct CameraState
    {
        // カメラのレンズパラメーター(fieldOfViewやorthgraphicsなど)
        public LensSettings Lens;
        // 補正前のカメラ位置
        public Vector3 RawPosition;
        // 補正前のカメラの回転
        public Quaternion RawOrientation;
        // カメラ位置の補正値
        public Vector3 PositionCorrection;
        // カメラの回転の補正値
        public Quaternion OrientationCorrection;
        // 最終的なカメラ位置はRaw + 補正位置で算出
        public Vector3 FinalPosition => RawPosition + PositionCorrection;
        // 最終的なカメラの回転
        public Quaternion FinalOrientation
        {
            get
            {
                if (Mathf.Abs(Lens.Dutch) > UnityVectorExtensions.Epsilon)
                    return CorrectedOrientation *
                        Quaternion.AngleAxis(Lens.Dutch, Vector3.forward);
                return CorrectedOrientation;
            }
        }
        // ... 他にもフィールド・メソッドの定義がある
    }
}

このようにバーチャルカメラの出力を書き出しておくことで、コンポーネントの連携が疎結合になり、取り回しを効かせやすくしています。

後述するバーチャルカメラのブレンドは2つの CameraState を補間することで実現したり、拡張機能によるカメラワークの加工もバーチャルカメラが出力した CameraState を編集することで実現しています。

Cinemachine Brain

Cinemachine Brainは、下図のようにバーチャルカメラによって計算されたカメラワークを適用したいカメラにアタッチして利用します。

シーン中に複数用意されたバーチャルカメラを取りまとめて、最終的に計算されたカメラワークを実際のカメラに適用します。

シーン中のすべてのバーチャルカメラの状態を監視して、もっとも優先度の高いバーチャルカメラをアクティブなカメラワークとしてカメラに適用します。

また、優先度が変わることでアクティブなバーチャルカメラが切り替わったときの、バーチャルカメラのブレンドにも対応しています。このとき、パッとバーチャルカメラが切り替わるのではなく、なめらかに補間するような自然なカメラワークの切り替えも自動で行ってくれます。(もちろん演出上、パッとカメラワークを切り替えたい場合も、ブレンドの設定だけで行なえます。)

Extension(拡張機能)

Extension(以後拡張機能と記載します)は、その名の通りバーチャルカメラの動作を拡張するためのコンポーネントで、バーチャルカメラが算出したカメラワークを、後づけで加工できます。

Cinemachineでは拡張機能を用いて、例えばターゲットを映すカメラの視野を遮るゲームオブジェクトを回避するようにカメラを動かす Collider というコンポーネントを実装しています。Cinemachineはこれ以外にもたくさんの拡張機能を実装しています。

拡張機能は、バーチャルカメラのコンポーネントのインスペクターを開き、Extensions > Add Extension で プルダウンメニューをクリックし、追加したい拡張機能を選択します。

選択すると、バーチャルカメラのコンポーネントがアタッチされているゲームオブジェクトに拡張機能のコンポーネントがアタッチされます。

バーチャルカメラは、そのゲームオブジェクトにアタッチされているコンポーネントを、拡張機能として認識します。

また、拡張機能は CinemachineExtension を継承したクラスを実装することで、開発者が独自の機能を実装することもできます。

Cinemachineコンポーネントの処理フローのまとめ

ここまでで、Cinemachineを構成するそれぞれのコンポーネントについて紹介しました。

それぞれのコンポーネントがどのように連携して、実際のカメラを制御するかについて整理しておきます。

Cinemachine Virtual Camera によるカメラワークの基本

Cinemachineでは様々な用途に応えるべく、設定項目がたくさんあり複雑です。

Cinemachine Virtual Cameraコンポーネントでカメラワークを作るための基本的な設定についてと、いくつかのカメラワークを設定する方法について紹介します。

カメラの位置制御と Body・Follow プロパティ

Bodyプロパティではカメラを動かすアルゴリズムを決定します。

また、Bodyで選択されたアルゴリズムに対して追従する対象がある場合、その対象は Follow プロパティで指定されたゲームオブジェクトになります。

Bodyプロパティは、Cinemachine 2.8.6 では下記のアルゴリズムが用意されています。

  • Do nothing: カメラを動かさない
  • 3rd Person Follow: 対象を原点に円周上に動く
  • Transposer: 対象の位置に対して一定の距離を保ちながら動く
  • Framing Transposer: 対象に対してスクリーンスペース上の距離を一定に保ちながら動く
  • Hard Lock To Target: 対象の位置と同じ位置になるように動く
  • Orbital Transposer: 対象の周囲を一定の距離を保ちながら動く
  • Tracked Dolly: Tracked Dollyで事前に定義されたパスに沿って動く

「Transposer」を用いると対象に対して一定の距離を取って追従するようなカメラワークを実現できます。もともとは2Dゲーム用途ですが「Framing Transposer」を用いることで一定の距離から見下ろし視点のカメラワークも実装できます。

「3rd Person Follow」は名前の通りサードパーソンカメラを簡単に実現できるアルゴリズムです。(このアルゴリズムの詳細は後述します)

カメラの回転制御と Aim・Look At プロパティ

Aimプロパティはカメラの回転を計算するアルゴリズムを決定します。

BodyプロパティにおけるFollowプロパティと同様に、Aimで選択されたアルゴリズムに対してカメラが映すべき対象がある場合は、その対象はLook Atプロパティで指定されたゲームオブジェクトになります。

Cinemachine 2.8.6では、下記のアルゴリズムが用意されています。

  • Do nothing: カメラの回転を制御しない
  • Composer: 対象をカメラに収めるようにカメラが回転する
  • Group Composer: 複数の対象をカメラに収めるようにカメラが回転する
  • POV: ユーザー入力に基づく
  • Same as Follow Target: Followプロパティで指定された対象を無垢
  • Hard Look At: 対象をLookAt

「Composer」を用いると、カメラのダンピングや構図を考慮した対象の追従を簡単に行なえます。

また、「Group Composer」を用いることで、複数の対象を映すようなカメラの制御も行うことができます。(このアルゴリズムは後述します)

Noiseプロパティ

Noiseプロパティを設定することで、下記動画のような手ブレのようなカメラの振動をシミュレートすることができます。

Cinemachine 2.8.6では、バーチャルカメラの動きに対してパーリンノイズを加える Basic Multi Channel Perlinのみ選択できます。

Noise Profileで使用するノイズをアセットから選択できます。独自にNoise Profileを作成する事もできます。

また、Noise Profileで発生するノイズを、Amplitude Gainでノイズの振幅(大きさ)を、Frequency Gainでノイズの周波数(速さ)を調整できます

Lensプロパティ

カメラのFieldOfViewやNear Clip、Far Clipを設定します。またプリセットを定義することで、複数のバーチャルカメラで共通のLensプロパティを持つことができます。

Priority

Priorityは名前の通り、Virtual Cameraの優先度を決定するプロパティです。

Cinemachine Brainはシーン中のすべてのVirtual Cameraの中からPriorityが最も大きいVirtual Cameraのカメラワークをアクティブなカメラワークとして適用します。

Standby Update

Virtual Cameraがライブでない時(つまりStandby状態)の更新頻度を制御します。

「Always」・「Never」・「RoundRobin」の3つから選べ、デフォルトで「RoundRobin」が選ばれます。

「Always」は毎フレーム更新、「RoundRobin」はVirtualCameraの状況に応じて時々フレーム更新、「Never」は全く更新を行いません。

Virutal Camera間のブレンドが必要ない場合(つまり、ぱっとカメラが切り替わるようなブレンド)は、処理負荷の観点から「Never」を選択しても良いでしょう。

「Always」か「RoundRobin」のどちらを選ぶかですが、「Always」を選ぶとStandbyなすべてのVirtual Cameraのアップデートが毎フレーム実行されるので、特にVirtual Cameraが多いシーンでは負荷が高くなります。

そのため、問題が発生しない限りは「RoundRobin」を選択するとよいでしょう。

バーチャルカメラを用いたカメラワークの実例

実際のゲームであるようなカメラワークをいくつかピックアップして、それをバーチャルカメラを用いてどのように実現するかについて説明します。

サードパーソンカメラ

CinemachineVirtualCameraコンポーネントのBodyプロパティに「3rd Person Follow」を用いることで、下記動画のようなターゲットの周りを一定の距離で追従するようなサードパーソンカメラを実装できます。

Bodyプロパティに「3rd Person Follow」を設定する場合、カメラの回転も合わせて制御されるので、Aimプロパティには「Do nothing」を指定します。

「3rd Person Follow」の制御パラメーターは大きく分けて Damping と Rig と Obstacles とシンプルです。

Dampingはカメラの応答性を定義します。この値が大きいほどカメラが遅く、値が小さいほどカメラが速く応答します。

Rigはカメラの向きを制御します。「3rd Person Follow」では、Followプロパティで指定した位置を原点として、肩越しの位置を映すかたちでカメラの向きが制御されます。

具体的には、「Shoulder Offset」で肩越し位置のオフセットを設定します。さらに肩越しの位置に対して垂直方向に「Vertical Arm Length」長さ分高さのオフセットが入ります。これは、カメラを持つ側の腕の長さで、この値が適切に設定されている方が、垂直方向のカメラの回転に対して自然な動きになります。

「Camera Side」は右か左かどちらの肩を追従するかを設定するパラメーターで、0 ~ 1を取ります。「Shoulder Offset」で右肩の位置をオフセットとして設定する場合、この値が1で右肩を、0で左肩を追従します。0.5を設定するとX方向のオフセットがかからなくなります。

「Camera Distance」はカメラが対象からどれだけ離れているかを表す値です。

「3rd Person Follow」ではビルトインで衝突解決システムが組み込まれています。カメラが障害物にめり込むと、それを回避するようにカメラが動き、常にターゲットがカメラに映るようにカメラワークが補正されます。障害物として認識させるレイヤーと無視するレイヤーをそれぞれ「Camera Collision Filter」と「Ignore Tag」で、カメラの大きさを「Camera Radius」などで設定することで、このシステムが動作します。

複数のターゲットを映すカメラ

CinemachineVirtualCameraのAimプロパティに「Group Composer」を設定することで、下の動画のようにプレイヤーと特定のターゲットが収まるようなカメラワークを実現できます。

通常、CinemachineVirtualCameraのLook Atプロパティには追従対象のゲームオブジェクトを指定しますが、Aimに「Group Composer」を指定した場合は、Look AtプロパティにCinemachineTargetGroupコンポーネントを設定したゲームオブジェクトを指定します。

CinemachineTargetGroupコンポーネントでは、下図のようにカメラに写したいターゲットを複数個設定します。

実際のカメラが追従する位置は「Position Mode」で決定され、「Group Center」と「Group Average」の2つが選択できます。それぞれ「Group Center」は全ターゲットのバウンディングボックスの中心を、「Group Average」は全ターゲットの位置の加重平均を取るかたちで算出されます。

Visual Scriptingを用いたバーチャルカメラのPriorityの制御

Cinemachine単体では、シーンの場所ごとに応じてバーチャルカメラのPriorityを切り替える事ができません。

バーチャルカメラを切り替えたい領域をコリジョンで定義して、その領域に入っているときだけバーチャルカメラをアクティブに切り替えるようなPriority制御を、Visual Scriptingを用いて実現する方法について紹介します。

Visual Scriptingを用いたカメラワーク切り替えの方針

Cinemachineでは、それぞれのシーンごとにバーチャルカメラを定義して、状況に応じてバーチャルカメラのPriorityを切り替えることで、ゲームのカメラを制御できます。

しかし、Cinemachine単体では場所ごとにバーチャルカメラのPriorityを切り替える機能は存在しないため、今回はこれを実現するためにVirutal Scriptingを利用します。

具体的には、PhysicsのColliderでバーチャルカメラを適用する領域を定義し、ColliderのトリガーイベントをフックしてバーチャルカメラのPriorityを切り替えます。

Visual Scriptingのインストール

Visual Scriptingは、Unity 2021.1以降ではデフォルトでインストール済みとなっています。

もし、Unity 2021.1以前に作成したプロジェクトを2021.1以降にアップグレードした場合などでインストールされていない場合は、メニューの Window > Package Manager でパッケージマネジャーウインドウを開いて、Unity Registryから「Visual Scripting」をインストールできます。

Unity 2020以前の場合は、Visual ScriptingのもととなるBoltというライブラリを Asset Store からインストールすることで同様の実装を行うことができますが、今回紹介する方法は利用できないのでご了承ください。

Cinemachine Virtual Camera BaseをVisual Scriptingで扱うための設定

バーチャルカメラをVisual Scriptingで利用するために、扱える型に CinemachineVirtualCameraBase を追加します。

メニューの Edit > Project Settingsから下図のようなProject Settings Windowを開き、画面左のVisual Scriptingを選択します。

Type Options プルダウンをクリックすると、Visual Scriptingで扱える型の一覧が表示されます。画面をスクロールして下の方に行くと「+」ボタンがあるのでクリックして、追加された行のプルダウンメニューを開いて「Cinemachine Virtual Camera Base (in Cinemachine)」を選択します。

最後に「Regenerate Nodes」をクリックしてノードを再生成します。

これでVariablesコンポーネントで CinemachineVirtualCameraBase を扱えるようになります。下図のように、 Type に「List of Cinemachine Virtual Camera Base」が選択でき、値に Cinemachine Virtual Camera Base を設定できるようになります。

コライダー内にいるときにCinemachine Virtual CameraのPriorityを変更するScript Machine

変数で指定したコライダー内にいるときだけ、Priorityの値を大きくしてアクティブなバーチャルカメラとして、コライダーを出たときPriorityの値を小さくしてアクティブなバーチャルカメラから外すScript Machineを組んでみます。

また、このScript Machineはシーンに対して複数設置して、場所ごとにバーチャルカメラを切り替えたいので、使い回せるようにプレハブ化します。

まず、ヒエラルキー上に 「VirtualCameraPriorityChanger」というゲームオブジェクトを作成します。次に、インスペクターウインドウで「Add Component」ボタンをクリックして「Script Machine」コンポーネントを追加します。

Script Machineを設定します。まずTitleを「Chinemachine Virtual Camera Priority Changer」とします。次にSourceを設定します。Sourceは、スクリプトを外部アセットとして保存して、それを参照する「Graph」と、Script Machineをアタッチしたゲームオブジェクト内にそのままスクリプトを埋め込む「Embed」という2つの形式が選べます。

どちらでも良いのですが、同じスクリプトを複数のScript Machineで使いまわしたい場合、スクリプトを外部アセット化してそれを参照するほうが、保守の観点からもアセットサイズの観点からも効率が良いため、今回は「Graph」を選択します。

「Graph」を選択した場合、スクリプトを外部アセットとして作成する必要があります。Script MachineのGraph項目右の「New」ボタンをクリックすると、外部アセットを保存する先を選択するウインドウが表示されます。

今回はプロジェクトのAssetsフォルダ下にVisualScriptsというフォルダーを作成して、その中に VirtualCameraPriorityChanger.asset という名前で保存することにします。

スクリプトを組む準備ができたので、Script Machineコンポーネントの「Edit Graph」ボタンをクリックします。

すると下図のようなScript Graphウインドウが開くので、スクリプトを組んでいきます。

まず、Script Machineで利用する変数を定義します。Visual Scriptingでは複数種類の変数がありますが、今回はシーン内でカメラワークを変更したい箇所分のCinemachine Virtual Camera Priority Changerを配置するので、変数を「Object Variables」として定義します。

「Object」では、Script Machineコンポーネントがアタッチされたゲームオブジェクトと同階層にアタッチされたVariablesコンポーネントに設定された値を参照します。

つまり、別々のScript Machineオブジェクトごとに、オブジェクト指向で言うところの個別のインスタンス変数が定義できます。

「Object Variables」を定義するには、Script Graphウインドウの画面左下の「Object」タブを選択します。

下記の2つの変数を定義します。

NameType備考
Target ColliderCollider(in Unity Engine)この変数で指定したコライダー入ったときにバーチャルカメラのPriorityを操作する。
Target Virtual CameraList of Cinemachine Virtual Camera Base(in Cinemachine)Priorityを操作するバーチャルカメラ(の一覧)。

変数で指定したコライダーのトリガーイベントをフックしてなにかの処理を行うには、下図のようなスクリプトを組みます。

Get Variable ノードで変数に割り当てられている値や参照を取得します。ノード名下のプルダウンで取得する変数の種類を選択できます。今回はObject Variableを利用するので「Object」を選択しています。

割り当てられているコライダーを取得したいので「Target Collider」を選択しています。このノードの出力(右側のポート)に変数に割り当てられたコライダーが入っているので、その出力と On Trigger Enter ノードと On Trigger Exit ノードを作成して、その入力(左側のポート)にそれぞれつなげます。

On Trigger Enter ノードと On Trigger Exit ノードは名前の通り、入力であたえられたコライダーの OnTriggerEnter と OnTriggerExit コールバックが呼び出されたときに、そのイベントを出力のトリガーとするノードです。今回は、このトリガーを受け取ってバーチャルカメラのPriorityを変更するので、さらに下図のようにスクリプトを組みます。

On Trigger Enter ノードと On Trigger Exit ノードの出力トリガー(右側矢印アイコンのポート)を Set Priority ノードの入力トリガー(左側1つ目の矢印アイコンのポート)につなげ、 Target Virtual Camera を取得する Get Variable ノードを新たに作成してその出力を Set Priority ノードの入力ターゲット(左側2つめのポート)につなげます。

ただし、 Target Virtual Camera は List of Cinemachine Virtual Camera Base とリスト型ですが、 Set Priority に渡したいのはリストの中身の Cinemachine Virtual Camera Base なので、 For Each Loop ノードを挟んでいます。

今回はスクリプトのぱっと見のわかりやすさを重視して、アクティブ時とスタンバイ時のPriorityの値を直値で設定しましたが、これは通常のコーディングでいうところのマジックナンバーにあたります。より規模の大きい開発を行う場合は「Application Variable」などのアプリケーションの設定として意味のある変数や定数を定義することで、可読性を向上させられるでしょう。

以上でスクリプトは組み終わりました。スクリプトの全文を下図に示しておきます。

最後に作成したゲームオブジェクトをプレハブ化して、複数箇所で使いまわしがしやすいようにしておきます。

Chinemachine Virtual Camera Priority Changerの使い方

上記で作成したCinemachine Virtual Camera Proirity Changerを実際に利用してみます。

先程作成したプレハブをシーン上に配置し、そのゲームオブジェクトにアタッチされているVariablesコンポーネントの Target Collider と Target Virtual Camera にそれぞれコライダーとバーチャルカメラを Value に設定します。

コライダーをトリガーとして扱いたいため、「Is Trigger」のチェックをONにします。

これで設定は完了です。コライダーに定義された領域に入るとバーチャルカメラのPriorityが変更されて、アクティブカメラが切り替わること(下記動画の場合は、PlayerFollowCameraからEventShotCameraに切り替わる)が確認できます。

カメラワークを作る上で気をつけたいポイント

最後に、カメラワークを作る上で気をつけておきたいポイントについて触れておきます。

乱用を避ける

また多くのゲームで、カメラの姿勢はユーザー入力の向きを決定する要素となります。キャラクターを動かしている最中にカメラワークがコロコロ変わると、入力向きがかわってユーザーのストレスとなります。

そのため、必要な箇所でピンポイントにカメラワークを変えるほうが良いでしょう。また、キャラクターを追従するカメラはバーチャルカメラのDampingの値に適切な値を設定することで、急激なカメラ移動を避けるようにします。

一度に大きく動かしすぎない

構図が大きく変わると、変化した状況をユーザーが理解できず、混乱を招く要因となります。そのため、基本的には大きく構図を変えすぎるようなカメラワークを連続させすぎないようにします。また、バーチャルカメラが切り替わるタイミングでは、適切なブレンドを設定するようにします。

ただし例外として、場面転換などの状況をガラッと変えたいようなケースでは、ブレンドを用いずガラッとカメラワークを変えるほうが適切な場合もあります。

まとめ

CinemachineのVisual Scriptingを組み合わせたノーコーディングなカメラワーク実装について紹介しました。

Cinemachineは高クオリティなカメラワークを簡単に実現できるとても便利なパッケージになっています。また、記事中に紹介した機能はほんの一部で、この他にもさまざまな機能が用意されています。

今回の記事を読んで、CinemachineやVisual Scriptingに興味を持たれた方は、ぜひご自身の手で一度これらを触ってみていただければと思います。

--

筆者紹介:ゆっち〜(向井 祐一郎)

株式会社サイバーエージェントに2015年度新卒として入社し、同年に子会社の株式会社アプリボットに配属。
「Neir Re[in]carnation」のキャラクター制御などに関わりつつ、Lead Developer Experience(LDX)としてクライアントエンジニアの開発体験の向上に会社横断で取り組む。
Unityをはじめとしたゲームに関する技術に触れることが好きで、会社有志の活動としてUniTipsUnity パフォーマンスチューニングバイブルなどの執筆に関わる。
個人でも毎週のUnityに関するニュースや記事をまとめた Unity Weeklyの運用を行う。
Twitter: https://twitter.com/yucchiy_

この記事はいかがでしたか?