Last Updated 2024.02.09
初心者Unity備忘録。
今回はカメラの動きをAnimatorを使って制御する際の方法と要点について。
gif重かったら申し訳ないです(´・ω・`)
↓その前に確認しておきたいこと。
■カメラワーク (Camerawork)
カメラの操作。カメラ自体の動きみたいなイメージ。
■カメラ割り(CameraBlocking)
カット割りとも。個人的にはこっちの方がお馴染みです。
アングルや構図、どのカメラを使用するか等の詳細。
Unity脳だと、カメラワークはカメラの動かし方、カメラ割りはTransformの値(位置座標と回転角度)と使用するカメラ、みたいに置き換えた方が分かりやすいのかなぁ……と。
下準備
まず空のGameObjectを作成。
その子要素に、今回のメインになるプレーンなカメラ(chinemachineではない)を新規作成。
CameraのコンポーネントにAnimatorを取り付け、Controllerを設定。
非常に重要なのが、ここで 親オブジェクトとその子要素のカメラの座標と角度(Transformの値)を対象にしたいオブジェクトと一致させておく こと。
今回は1Pプレイヤーを対象にするので、それに合わせます。
Animationファイルの作成
あとはいつも通りにAnimatorの組み立て。
Animationファイルを新規作成し、カメラの位置や角度をSceneビューや実際のゲーム画面で確認しながら、動かしたいように登録と調整を繰り返します。
制御する要素としては、カメラの位置と角度、FOVとEnabled設定。
実際の動きを確認しながら作業できるので、直感的に作っても大丈夫です。
数字を直接入力する場合はTabキーを使うと少しスムーズ。
前述の位置合わせをしていないと、親はワールド座標、子はローカル座標の影響下で動く関係で、制作段階だと大丈夫なのに、実際の画面では「対象が映らない」「意図してた挙動と違う」といった事態になります。ここのカラクリが、初心者には今まで理解出来てなかったんですよね。
文章で説明するのが難しいのですが、カメラコンポーネントが親要素についてるとAnimationで設定した数字はワールド座標として動くので、子要素に設定することでワールド座標を親の座標に固定し、そこを起点として動くようになるので位置ズレが起きない……みたいなことなんだと思います。もうちょっと上手い説明が思いついたらリライト予定。
仕様として重要なこと
cameraにAnimatorを設定している場合、スクリプト等で直接トランスフォームの値を変更したり、回転させることは出来なくなります。この場合、Animatorを無効化することで直接的な数字操作が可能に。カット割りの値をUnity上で確認しながら細かく決める際に使ったりするので覚えておくこと。
また、カメラを子要素に設定するもう一つの利点として、ここの部分を無視してスクリプトから「親の座標や角度」を動かすことは可能です。
chinemachineからプレーンなカメラに切り替える場合だと、優先順位に関係なく瞬時に切り替わるためカットイン的な演出になります。
chinemachineカメラのブレンド機能や優先度の設定は、それはそれで欲しい場面もあるので、ゆっくり自然に切り替わるchinemachineカメラと、カットで切り替わる通常カメラの仕組みと利点を上手く使い分けられるのが理想的だと思います。
カメラのAnimator制御による利点
chinemachine機能を使ったカメラ同士の切り替えはブレンドが発生し、その都度ブレンドにかかる時間を把握、制御しないといけないため、スクリプトの可読性も下がるし、かなり面倒です。
▼chinemachineを使った際のカメラ切り替えのサンプル
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Cinemachine;//chinemachineを使用
public class GameManager : MonoBehaviour
public Animator _1PAnimator;//1Pのアニメーターを収納
public bool IsThrow = false;//ダメージチェック用
public GameObject subCamera;
public CinemachineVirtualCamera subcameChinemachine;
void LateUpdate()
{
//投げモーション中にカメラを切り替え
if (_1PAnimator.GetCurrentAnimatorStateInfo(0).IsName("N_Throw"))
{
if (IsThrow)
{
subCamera.gameObject.SetActive(true);
subcameChinemachine.Priority = 12;
}
else
{
Invoke("CameraChenge", 0.5f);//ダメが入ったら元に戻す
}
}
}
void CameraChenge()
{
subcameChinemachine.Priority = 8;
subCamera.gameObject.SetActive(false);
}
実際に使っていたスクリプトの一部。
カメラの操作は基本的にLateUpdate()内に記述。
投げ成立時にカメラの切り替えと、ダメージが入ったらカメラを戻す、といった内容です。
Invokeによる時間調整や、priorityの調整とカメラの有効化無効化のタイミングが違ってたりするので解り難いですね。初心者丸出しです 負荷を考えなければ subCamera は常時アクティブでも問題ないので『SetActive()』は省けるのですが……
一方、Animationで座標や角度、Enabledを設定しておくとスクリプトに記述するのが
・Animatorの定義
・Paramenters(boolやTriggerのアレ)の記述
くらいになります。
どっちがいい悪いとか、優劣の話ではないです。
オブジェクトに対する正確な周回軌道など、回転系はスクリプトの方が簡単だったりするので、結局はどっちも使ってます。
記述の簡略化以外に、AnimatorのSettings機能もそのまま使えるので、抜け時間や前後のブレンド、speed調整もAnimatorの機能だけで可能です。
おわりに
Animatorを使ったカメラ制御はかなり直感的に設定、修正できるので、色々なカット割りを実現、再現できるようになりました。考えたり試したりするのが楽しいです。
そしてまた、夢中になって湯水の如く時間が溶けていくのですが……
今まで1P側2側、距離によっても分岐させ、それも演出が必要な技ごとにとか「カメラ何台使えば出来るんだ……」と、途方にくれてたりしたのですが、カメラ1つで割とギュインギュイン動かせるようになったので、希望の光とか問題解決の糸口にはなってくれそうです。
真面目に、テストプレイでイメージ通りに動いた瞬間はかなり感動しました。
あと、この時の撮影時には使っていませんが、任意のタイミングで、任意の対象のみアンチエイリアスをかけるといった仕組みも(副産物的に)作れました。やっぱりアップ時は綺麗に魅せたいですからね。