マイコン研究会の日記

大阪市立大学マイコン研究会のブログです。

【Unity】カメラワーク入門【Cinemachine】

どうも!fimasuです(`・ω・´)
以前↓の記事を書いて以来なので、ブログを書くのは久しぶりです。
ocumcr.hatenablog.com

さて、今回からは「3Dゲームを作る」というテーマで不定期連載を始めようと思います。
連載できそうになかったためこの記事のみとなりました…

今回はUnityのPackageManagerにあるCinemachineというパッケージを使ってみます。このCinemachineというパッケージは、簡単に言えば仮想カメラを使うことでカメラワークを作成するためのものです。


本記事で使用しているCinemachineは主にver 2.5.0です。
※ Cinemachine ver2.6.3を用いてInputSystemと連携する方法について追記しました。 ※
異なるバージョン(特にこれらより古いバージョン)のCinemachineを用いた場合、動作は保証できませんのでご了承ください。

パッケージのインストール

では、早速インストールしていきましょう!
まずはUnityのプロジェクトを立ち上げていつもの画面にしてください。
開いたら次の画像のようにウインドウからPackageManagerを選択してください。
f:id:ocumcr:20200323213607p:plain

するとPackageManagerの画面が開くので、画像のように選択しInstallボタンを押せばインストール完了です!
f:id:ocumcr:20200323213615p:plain

完了すれば次の画像のようにCinemachine用のメニューが増えます。
f:id:ocumcr:20200404145614p:plain
このメニューからは様々な仮想カメラを作ることができます。

仮想カメラを実際に使ってみる

さて、まずは仮想カメラを使ってカメラワークを作る方法を見ていきます。

準備

カメラで映すものとして動くものが欲しいですよね?
そこで、平面上でキューブを動せるものを準備します。
ヒエラルキータブの+ボタンからオブジェクトを作れるので平面とキューブを作ります。
f:id:ocumcr:20200404150857p:plain
作っただけだとキューブが埋まってしまっているのでCubeを選択してインスペクタから位置を調整します。
f:id:ocumcr:20200404151136p:plain
その後プロジェクトタブの+ボタンからC#スクリプトが作れるので、MoveManagerなど適当な名前を入力してスクリプトを作成します。
f:id:ocumcr:20200404151526p:plain

今回はテストができればよいだけなので、ひとまず次の簡単な移動スプリクトを使うことにしました。

using UnityEngine;

public class MoveManager : MonoBehaviour {

    //移動の速さ
    public float Speed = 3.0f;

    //移動入力保存用
    private Vector3 InputDirection = Vector3.zero;
    
    // Update is called once per frame
    void Update() {

        //入力受付
        InputDirection = Vector3.Normalize(new Vector3(
            Input.GetAxis("Horizontal"),
            0,
            Input.GetAxis("Vertical")));

        //移動処理
        transform.position += Speed * Time.deltaTime * InputDirection;
    } 
}

これを次の画像のようにインスペクタからキューブにアタッチします。
f:id:ocumcr:20200406003959p:plain
これでキューブを動かすことができるようになったのでテストしていきます。

仮想カメラの設定

とりあえず、一番単純な仮想カメラであるVirtualCameraを試してみましょう。
上述のメニューからVirtualCameraを作成すると次の画像のようになると思います。
f:id:ocumcr:20200406004002p:plain
このVirtualCameraを選択し、インスペクタからFollowとLookAtに先ほどのキューブをアタッチします。
f:id:ocumcr:20200409184000p:plain
これで設定完了です!
再生してみるとWASDまたは矢印キーでキューブを動かすことができ、それにカメラが自動でついてくると思います。これが仮想カメラを使うための基本の手順です。
ね?便利でしょう?

仮想カメラの設定の紹介

インスペクタからBodyやAimの設定を変更することで仮想カメラの挙動を変更できます。どんなものがあるか簡単に紹介しておきます。

Body
Transposer系 Followターゲットに追従する
Hard Lock to Target Followターゲットからの位置を固定する
Tracked Dolly 特定の経路(パス)の上のみを動く
Do Nothing 自動では移動を行わない


Transposer系のBodyには純粋なTransposerの他にFraming TransposerとOrbital Transposerがあります。これらは次のような挙動の違いがあります。

Transposer 単純にFollowターゲットに追従する。VirtualCameraのデフォルト設定。
Framing Transposer 主に2Dゲームのカメラ用の設定。カメラが動くアルゴリズム
  1. カメラのX-Y平面から設定した距離にFollowターゲットが来るまでカメラをZ軸に沿って移動させる。
  2. カメラのX-Y平面内で、カメラの画面上の設定した位置にFollowターゲットが来るまでカメラを移動させる。
という感じ。LookAtターゲットを無視するので注意が必要
Orbital Transposer Followターゲットを中心とした円周上にカメラが来るように追従する。その際Heading(向いている方向)にカメラの向きを合わせる。(デフォルトではターゲットの真後ろに来るようになっている)また、ただ追従するだけでなく、ユーザーの入力に応じてFollowターゲットを中心にカメラを回転させることもできる。
Aim
Composer系 LookAtターゲットの方を向く
Same As Follow Target LookAtターゲットとローテーションを一致させる
Hard Look At LookAtターゲットをカメラフレームの中央にあわせる
POV ユーザーの入力に基づいて仮想カメラを回転させる
Do Nothing 自動では回転を行わない

Composer系のAimには純粋なComposerの他にGroup Composerがあります。これらは次のような挙動の違いがあります。

Composer 単純にLookAtターゲットの方を向く。VirtualCameraのデフォルト設定。
Group Composer LookAtターゲットがCinemachineターゲットグループの場合はターゲットグループに指定したものがうまく映るように調整する。

InputManager以外の入力システムを用いたい場合

Cinemachine ver2.5系の場合

仮想カメラのAimをOrbital TransposerやPOVなどに設定した場合、カメラを動かすためにユーザーの入力を使うことがあります。デフォルトでは仮想カメラは、この入力を扱う仕組みとしてUnityの標準(だった)InputManagerを用いるような設定となっています。そのため、InputManagerを無効にすると当然ですがユーザーの入力を使うタイプの仮想カメラは上手く動きません。
つまり、InputManager以外の入力システムを用いたい場合(例えば、Unityの新しい入力システムのInputSystemや、自作した入力システムを使う必要がある時)には一部の仮想カメラが使えないことになります。

※2020/9/26 InputSystemを用いたい場合について追記※

ここでの説明にはCinemachine ver2.6.3及びInputSystem ver1.0.0を用います。
InputSystemを用いる場合はInputManagerを無効にしないことも可能ですが、今回は無効になっているものとして説明していきます。
なお解説は上記の内容を前提知識とし、Cinemachine ver2.6.3及びInputSystem ver1.0.0のインストールが完了しているという前提で書いています。

次の画像のようにプロジェクト設定を開いてください。
f:id:ocumcr:20200924000339p:plain
開くと次の画像のようになると思います。
f:id:ocumcr:20200924001112p:plain
そこでスクロールしていくと「使用中の入力処理*」の項目があると思いますが、これが画像のように「入力システムパッケージ(新)」となっていればOKです。
f:id:ocumcr:20200924001121p:plain
また、「入力マネージャー(旧)」になっている場合はInputSystemが無効なので設定を変えてください。もし「両方」の場合はInputSystem自体は有効なので一応動くとは思いますがあまりオススメはしません。
その後、Input System Packageのタブに切り替えてください。切り替えると次の画像のようになっていると思います。
f:id:ocumcr:20200924002826p:plain
この画面にあるCreate settings assetボタンを押すとInputSystem.inputsettingsというアセットが自動生成され、次のようにInputSystemの設定画面が開きます。
f:id:ocumcr:20200924005659p:plain
そして次の画像のようにSurpported Devicesを追加します。
f:id:ocumcr:20200924010021p:plain
今回は次の画像のようにキーボードとマウスを追加しました。
f:id:ocumcr:20200924010219p:plain
次に、「どの入力をどのようなデータとして受け取るか」の設定を行います。
プロジェクトのタブの+ボタンからInputActionsを選択し、MyControlなどと適当な名前を入力してファイルを生成します。
f:id:ocumcr:20200924011458p:plain
f:id:ocumcr:20200924012423p:plain
ファイルが生成できると次の画像のようになっていると思います。
f:id:ocumcr:20200924012541p:plain
そうしたらGenerate C# Classにチェックを入れ「適当する」ボタンを押してからEdit assetボタンを押します。
f:id:ocumcr:20200924013743p:plain
すると次の画像のような画面が開くと思います。これが入力の設定画面です。
f:id:ocumcr:20200924013945p:plain
その後、一番左側のActionMapsの欄の+ボタンを押して次の画像のようにPlayerやCameraなどと適当な名前を付けてAction Mapを二つ作成します。
f:id:ocumcr:20200924014633p:plain
そして、ActionMapsがCameraの方になっていることを確認した上で、真ん中のActionsの欄に初めからあるActionの名前をLookなどの適当な名前に変え、一番右のPropertiesの欄のAction Typeを「値」に変更します。
f:id:ocumcr:20200924015351p:plain
そうしたら新たにControl Typeの項目が増えるのでこれを「Vector2」に設定します。
f:id:ocumcr:20200924015720p:plain
これで入力をVector2型のデータとして受け取ることが設定できたことになります。ここで、「どんなデバイスが使える環境を想定しているか」の設定を行います。
次の画像のように画面左上の「No Control Schemes」とある所からAdd Control Scheme... を選択してください。
f:id:ocumcr:20200924072613p:plain
すると次の画像のようなメニューが開くのでKeyboard&Mouseなどと適当な名前を入力します。
f:id:ocumcr:20200924072630p:plain
その後、次の画像のように「リストは空です」とある所からKeyboardとMouseを追加します。
f:id:ocumcr:20200924072650p:plain
追加したら次の画像のようになっていると思うのでSaveボタンを押してください。
f:id:ocumcr:20200924072706p:plain
これで「どんなデバイスが使える環境を想定しているか」の設定が終わりました。次は、入力を割り当てていきます。Actionsの欄のLookなどと名付けたアクションの左側の▽印をクリックし、下に新たに出てくる「 <No Binding> {GROBAL} 」となっているところを選択すると次の画像のようになると思います。
f:id:ocumcr:20200924073159p:plain
その後、Pathの項目を選択し次の二つの画像のようにMouseのDeltaを選択します。
f:id:ocumcr:20200924075134p:plain
f:id:ocumcr:20200924075149p:plain
選択し終わったら忘れないうちにUse in control schemeの項目のKeyboard&Mouseにチェックを入れておきます。ここまで終わったら次の画像のようになっていると思います。
f:id:ocumcr:20200924075201p:plain
これでマウス操作の入力をVector2型のデータとして受け取るように設定できたことになりますので上部のSave Assetボタンを押して保存してください。
同様にしてキーボードのWASDをVector2型のデータとして受け取るように設定します。ActionMapsをPlayerの方に切り替え、Actionsの欄に初めからあるActionの名前をMoveなどの適当な名前に変え、Propertiesの欄のAction Typeを「値」に変更します。その後Control Typeの項目を「Vector2」に設定し、忘れないうちにUse in control schemeの項目のKeyboard&Mouseにチェックを入れておきます。ここまで行うと次の画像のようになっていると思います。
f:id:ocumcr:20200925003825p:plain
次はActionsの欄のMoveなどと名付けたアクションの左側の▽印をクリックすると新たに下に「 <No Binding>」となっているものが出てきますが、今回はこれを右クリックして出てくるメニューから次の画像のように削除します。
f:id:ocumcr:20200925005126p:plain
その後Move右側の+ボタンを押して次の画像のようにAdd2DVectorCompsiteを選択し、できたものにWASDなど分かりやすい名前を付けておきます。
f:id:ocumcr:20200925013041p:plain
f:id:ocumcr:20200925013058p:plain
そうしたら新たに下に「Up」「Down」「Left」「Right」が出てきますがこれらを選択するとPathの項目があるので、それぞれに対応するキーを割り当てます。その際、Listenボタンを押せば入力したキーを候補として挙げてくれるので簡単に設定できます。これらを設定し終えると次の画像のようになります。
f:id:ocumcr:20200925013852p:plain
これでキーボードのWASDをVector2型のデータとして受け取るように設定できたことになりますので上部のSave Assetボタンを押して保存し設定画面を閉じてください。
次は動作確認のために一番単純な仮想カメラであるVirtualCameraをBody:Orbital Transposer, Aim:Composerの設定にしたものを使います。なお、上述のものと同様にキューブと平面を作成し、仮想カメラのFollowとLookAtにキューブを設定しておきます。ここまですると次の画像のようになっていると思います。
f:id:ocumcr:20200925233431p:plain
その後、仮想カメラ(画像では CM vcam1 という名前のオブジェクト)のインスペクタをスクロールしていくとコンポーネントを追加というボタンがあるので、ボタンを押して、スクリプト -> Cinemachine -> Cinemachine Input Provider の順で選択します。これによって次の画像のように仮想カメラにCinemachine Input Providerというコンポーネントが追加されます。
f:id:ocumcr:20200925234135p:plain
このコンポーネントによってCinemachineの仮想カメラとInputSystemが連携できます。このコンポーネントのXY Axisのところをクリックすると画像のように先ほど作ったLookなどと名付けたアクションが設定できるのでこれを設定します。これで再生すると仮想カメラを左右に回転させることが可能になっていると思います。(但し、上述したスプリクトのようにInputManagerを使用したスプリクトをアタッチしたままだとエラーが出ると思うので気をつけてください。)
f:id:ocumcr:20200925235832p:plain
また、上述したテスト用の移動スプリクトは次のように直すことで先ほど作ったMoveなどと名付けたアクションに対応して移動するスプリクトに直すことができます。

using UnityEngine;
using UnityEngine.InputSystem;

public class MoveManager : MonoBehaviour, MyControl.IPlayerActions {

    MyControl.PlayerActions input;
    private Vector3 InputDirection = Vector3.zero;

    public float Speed = 3.0f;

    // Awakeのタイミングでコールバックの登録をしておく
    void Awake() {
        input = new MyControl.PlayerActions(new MyControl());
        input.SetCallbacks(this);
    }

    // 有効化されたらインプットも有効化
    void OnEnable() => input.Enable();

    // 無効化される、または破壊されるときにインプットも無効化
    void OnDisable() => input.Disable();
    void OnDestroy() => input.Disable();


    void Update() {
        transform.position += Speed * Time.deltaTime * InputDirection;
    }

    public void OnMove(InputAction.CallbackContext context) {
        Vector2 vec = context.ReadValue<Vector2>();
        InputDirection.x = vec.x;
        InputDirection.z = vec.y;
    }
}

このように直した移動スプリクトをキューブにアタッチして再生するとWASDキーでキューブを動かしすことができ、カメラがそれに追従します。

仮想カメラの種類の紹介

Cinemachineで追加される仮想カメラには次の表のようにいくつか種類があります。

VirtualCamera 一番単純な仮想カメラ。
FreeLookCamera BodyがOrbital Transposerの仮想カメラでできた「リグ」を三つ持っており、これを上段のリグ、中段のリグ、下段のリグとして使用する。この三つのリグをブレンドすることでTPSカメラのように働く。
Blend List Camera 仮想カメラを切り替えていくためのリストを持っており、時間経過とともにそのリストに従って仮想カメラを切り替える。
State-Driven Camera Animatorの状態遷移に応じて仮想カメラを切り替えるもの
Clear Shot Camera 障害物に隠れてターゲットが見えなくなってしまうのを防ぐように仮想カメラを切り替える。
Mixing Camera 複数の仮想カメラをミックスして中間の位置、角度からターゲットを映す。

今回はこの中から私が個人的にオススメのFreeLookCameraを紹介します。
これを使うことでTPSカメラをとても簡単に作ることができます。
先ほどと同様にメニューからカメラを作成してFollowとLookAtに先ほどのキューブをアタッチすると次の画像のようになります。
(画像では先ほど作ったVirtualCameraは削除しています。)
f:id:ocumcr:20200411013844p:plain
シーン画面を見るとわかりやすいのですが、FreeLookCameraはターゲットを中心に回転するようにカメラが追従するもので、動く範囲は三つのリグで決まります。
f:id:ocumcr:20200411015130p:plain
また、各リグごとにBodyとAimがありますが、Bodyは仕様上Orbital Transposerで固定されています。
特に何もせずともこの時点で十分TPSカメラとして使うことができますが各リグの半径やターゲットからの高さを設定することでより使い勝手が良くなります。
Aim内のTracked Object Offset を変更することでカメラの追従する位置をズラしたり,ScreenXやScreenYを変更することで画面のどの位置に表示するかを変更したりするのも良いと思います。



他にもCinemachineには仮想カメラの機能を追加する拡張機能(カメラを振動させるエフェクトをかけるなど)などの便利な機能がたくさんあります。
Cinemachineを使いこなせば、もうカメラワークに悩まされずに済みますね!