目的
この記事では、内側から Cinemachine を見て解説します。
内側が分かると、Cinemachine の動かせ方が自然と分かるようになり、ゲームのバトルとシナリオのカメラワークをより格好良く調整出来ます。
また、Cinemachine の基本機能では足らなくなった時に、拡張クラスも書けるようになります。
是非この記事を見て、素晴らしいカメラワークを作ってみてください!
Cinemachine とは
Cinemachine は Unity が映像制作のために作った公式ツールです。
Cinemachine を使えば、カメラの位置・回転や FOV などが簡単に操作・切り替えができて、イベントシーンや必殺技のカメラワークが簡単にできます。
本記事では基本的な使い方の説明をせず、内部処理のほうにフォーカスします。
Cinemachine の仕組み
Cinemachine は様々なカメラワークを実現するために、色々なクラスがあります。
しかし、処理の中身を探求すると、理解するのに本当に重要なクラスは以下の3つのみです。
-
Cinemachine Virtual Camera Base
- 全てのバーチャルカメラの基底クラス
- 次の CameraState を提供する
-
Camera State (struct)
- バーチャルカメラの 計算結果
- バーチャルカメラはどの時でも必ず CameraState を提供しなければならない
-
Cinemachine Brain
- Priority の一番高いバーチャルカメラの 計算結果をカメラに適用する管理クラス
- Priority の一番高いバーチャルカメラの 計算結果をカメラに適用する管理クラス
Cinemachine を使うと、毎フレーム、Cinemachine によってカメラが適宜な位置に配置されますが、
その時、上記3クラスの処理と関係は以下のようになります:
- CinemachineBrain が一番 Priority の高いバーチャルカメラを取得します
- 該当バーチャルカメラの CameraState を更新して・取得します
- CameraState をそのままカメラに適用します
上のように、Cinemachine は VirtualCamera -> CameraState -> CinemachineBrain -> Camera
の処理の流れで Camera を更新しています。
下節で、各コンポーネントの役割を説明します。
CinemachineVirtualCameraBase
CinemachineVirtualCameraBase は、全てのバーチャルカメラの基底クラスです。
基底クラスゆえに、カメラ操作用の処理はあまり入っていませんが、
継承クラスのルールとして、「必ず CameraState を提供しなければならならない」があります。
関連の定義は下記になります:
public abstract class CinemachineVirtualCameraBase : MonoBehaviour, ICinemachineCamera
{
public int m_Priority = 10;
public abstract CameraState State { get; }
public abstract void InternalUpdateCameraState(Vector3 worldUp, float deltaTime);
... ...
}
図解として、下記のようになります:
各バーチャルカメラは、InternalUpdateCameraState
を override して、独自の CameraState の更新処理の記述をしています。
バーチャルカメラに提供された CameraState で、カメラのこのフレームのパラメータが決まります。
CameraState
バーチャルカメラによって計算された CameraState ですが、
その役割としては、Camera を操作するためのパラメータのデータ構造です。
中には position・rotation や FOV などの情報が入っていて、Camera に直接適用するのに適しています。
関連の定義は下記になります:
public struct CameraState
{
// ノイズ適用前の位置
public Vector3 RawPosition;
// ノイズ適用前の回転
public Quaternion RawOrientation;
// 最終位置。FinalPosition はそのまま Camera の Transform に適用される
public Vector3 FinalPosition => RawPosition + PositionCorrection;
// FOV, perspective/orthographic などの設定
public LensSettings Lens;
... ...
// ほか、細かい調整用のパラメータ
}
このように、馴染み深い Vector3 と Quaternion が多く入っており、それがカメラの Transform 更新に使用されます。
また、複数のバーチャルカメラ間の遷移を実現するために、CameraState は Lerp()
の関数も実装しています。
単純な Vector3 の Lerp のみならず、Spherical Blend や Cylindrical Blend (円を描くような遷移経路)の処理もここに入っています。
Lerp()
の出力結果もまた CameraState ですので、必要に応じて更に Lerp することも出来ます。
CinemachineBrain
各バーチャルカメラはそれぞれ CameraState を提供していますが、
実際に Camera に適用されるのは Priority の一番高いバーチャルカメラの CameraState のみです。
Priority を見て、適切なバーチャルカメラの CameraState を Camera に適用する管理クラスは、ズバリ CinemachineBrain です。
幸い、CameraState はすでに適した形で定義されていますので、
以下のように、適用時はほぼ state の情報そのまま適用できます。
public class CinemachineBrain : MonoBehaviour
{
... ...
// CameraState を Camera に適用する関数
private void PushStateToUnityCamera(ref CameraState state)
{
transform.position = state.FinalPosition;
transform.rotation = state.FinalOrientation;
Camera cam = OutputCamera;
if (cam != null) {
cam.nearClipPlane = state.Lens.NearClipPlane;
cam.farClipPlane = state.Lens.FarClipPlane;
... ...
}
}
}
また、最高 Priority のバーチャルカメラが変わった時に、スムーズな遷移をするための処理もしています。
バーチャルカメラ間の遷移
Priority 変動によって適用するバーチャルカメラが変わった時、急激なカメラの画角変化を防ぐために、
CinemachineBrain は緩やかに遷移元から遷移先へブレンドしています。
具体的には、CameraState の節でも説明した通り、
遷移元のカメラと遷移先のカメラの CameraState を Lerp させ、結果を Camera に適用しています。
こうすることで、スムーズなカメラ遷移が出来て、気持ちいいカメラワークが出来ます。
上記の知識を持って、VirtualCamera を見てみる
実際に上記の知識を持って、CinemachineVirtualCameraBase の継承クラスを分析してみましょう。
今回は CinemachineVirtualCamera と CinemachineBlendListCamera を紹介します。
CinemachineVirtualCamera
CinemachineVirtualCamera は一番操作種類が多く、とても強力なバーチャルカメラです。
様々な追従・注視方法で、キャラをフォーカスするカメラが簡単に作れます。
CinemachineVirtualCamera のインスペクター上のパラメータは下記のようになります。
他のバーチャルカメラと比べて、Body と Aim、Noise の項目があります。
ここから推察すると、おそらく VirtualCamera はアップデート時、Body と Aim の方法で CameraState を作り、更にちょっとした Noise を追加して、それを最終結果として出力しています。
CinemachineBlendListCamera
CinemachineBlendListCamera は複数の子バーチャルカメラを持って、
各子カメラの映す時間や遷移時間が設定出来るバーチャルカメラです。
CinemachineBlendListCamera のインスペクター上のパラメータは下記のようになります。
VirtualCamera と比べて、Body・Aimt・Noise が無く、代わりに複数の子 VirtualCamera を持っています。
実際に 実装 (Github) を見てみると、BlendListCamera は自身で複雑な処理をせず、子 VirtualCamera の CameraState を取得して、そのまま自分の CameraState として提供しています。
遷移時は遷移元と遷移先の子カメラの CameraState をブレンドして提供しています。
次のステップへ
上記のように、内部処理が分かると、各バーチャルカメラをインスペクターを見ただけで、何をやっているかが何となく分かるようになります。
しかし、それだけではありません。
Cinemachine の内部知識があると、新たなバーチャルカメラを作ったり、既存のバーチャルカメラの拡張処理クラス(CinemachineExtension)を書くことができます。
実際に自分で一から VirtualCamera を書くのは大変ですが、既存のバーチャルカメラの微調整ならとても簡単です。
以下で拡張クラスの作り方を紹介します。
Cinemachine の拡張処理クラス (CinemachineExtension) とは
CinemachineVirtualCamera のインスペクターの一番下に、Add Extension という項目があります。
プルダウンリストを選択すると、新たな CinemachineExtension がアタッチされ、様々なカスタマイズができます。
例えば、爆発などをシミュレートする ImpulseListener、カメラをめり込まないようにする Collider なども CinemachineExtension
です。
拡張処理クラスの作り方
CinemachineExtension
を継承すれば、独自の処理を書くこともできます。
新たな CinemachineExtension
クラスを作ると、自動的に Add Extension のプルダウンリストに追加されます。(めっちゃ便利です!)
以下はもっともシンプルな Extension 定義です。
public class CinemachineTestExtension : CinemachineExtension
{
// 自動的に呼ばれる、abstract な callback 関数。 VirtualCamera の state を更にカスタマイズできる
protected override void PostPipelineStageCallback(
CinemachineVirtualCameraBase vcam,
CinemachineCore.Stage stage,
ref CameraState state,
float deltaTime)
{
// 独自のカメラ操作の記述
}
}
abstract 関数の PostPipelineStageCallback
を override すれば、ref 引数として CameraState が渡されます。
前章で説明した通り、CameraState は Camera の調整に直結していますので、
ここでカスタムな調整記述をすると、結果はそのまま Camera に反映されます。
別記事の Cinemachineカメラの俯瞰・アオリ角の最大値を制限する / CinemachineExtension のススメ で
実際に CinemachineExtension を継承して、俯瞰・アオリ角を制限する拡張を作りました。
興味のある方は是非見てみてください。
備考:CinemachineCore.Stage について
CinemachineExtension.PostPipelineStageCallback()
関数に、見慣れない引数の CinemachineCore.Stage があります。
これはズバリ 「今、PostPipelineStageCallback()
が呼ばれたのは何の処理の後か」を伝える enum です。
この stage をチェックすることで、正しいタイミングで自分の調整を適用することができます。
例えば、最終調整後の CameraState にのみ手を入れたい場合は、if (stage != CinemachineCore.Stage.Finalize) return;
を関数の先頭に追加しましょう。
// CinemachineCore.Stage の定義 public enum Stage { Body, // 位置調整ステージ Aim, // 回転調整ステージ Noise, // ノイズ(手ブレなど)ステージ Finalize // 最終調整ステージ };
結
この記事で、Cinemachine の仕組みと、Cinemachine を支える3つのクラス、
CinemachineVirtualCameraBase
CameraState
CinemachineBrain
を紹介しました。
また、CinemachineExtension を使って CameraState の調整のやり方も紹介しました。
これらを使って、素晴らしいカメラワークを是非作ってみてください。