プレイヤーが操作するオブジェクトの移動に合わせてカメラも動かしたい!
そんな状況あるよね(テレビショッピング風に)。
ただ、カメラの挙動は奥が深いです。いろんなことを考えなくてはなりません。
そんな奥深くなくていいから、パパっとカメラの挙動が作りたい。
1週間ゲームジャムに参加したいけど、時間がないためカメラの挙動にそんな時間を割くことができない。
そんなあなたにStandardAssets。
プロジェクトに組み込むだけで、カメラの挙動が簡単に作れちゃうぞ。
今らなら税込み198……、いや無料か。
というわけで今回はStandardAssetsのCameraのアセットについてメモ書きでござい。
この記事にはUnity5.6.1f1を使用しています。
はじめに
早速カメラのStandardAssetsを組み込んでみましょう。
メニューバーのAssetsからCamerasのPackageをインポートしちゃうぞ。
Standard Assetsのカメラの種類は4種類あるようですね。
- CctvCamera
- FreeLookCameraRig
- HandheldCamera
- MultipurposeCameraRig
うーん、Prefab名だけではどんなカメラなのかよくわからないや。
1つずつ見ていきましょうか。
CctvCamera
CctvCameraを使ってみました。
まず、カメラの位置は動かない。
そして、X軸・Y軸の回転で指定したターゲットに向くようにしているみたいですね。
Prefabの作りは階層がないシンプルな作り。
以下のコンポーネントがアタッチされていました。
LookatTarget
TargetFieldOfView
AbstractTargetFollower
Prefabにアタッチされているコンポーネントとは違いますが、カメラ制御のコンポーネントにおいて、AbstractTargetFollower
は重要です。
今回使用するカメラ制御用コンポーネントは、大体がこのクラスを継承しているようです。
そのため、このクラスにあるプロパティはカメラ共通のプロパティになります。
プロパティ一覧
- Target :
Transform
カメラの向きや移動する目標。 - AutoTargetPlayer :
bool
有効にすることでPlayer*のついたTransform
を上記のTarget**に自動で設定する。 - UpdateType :
UpdateType
カメラの更新タイミング。
Target
に指定するTransform
は、スクリプト上でもSetTarget()
やTarget
プロパティを使うことで取得・設定が可能みたいですね。
AutoTargetPlayer
はtrue
にすることで、Target
がnull
の場合に、タグがPlayerのものをHierarchy上から検索し自動で設定してくれるようです。
複数Playerがあった場合は、どれになるかしらないけど……。
UpdateType
に指定できるタイプには以下の種類があるみたいです。
- FixedUpdate
FixedUpdate()
で更新される。 - LateUpdate
LateUpdate()
で更新される。 - ManualUpdate
自動では更新されなくなり、他のスクリプトからManualUpdate()
を呼ぶことで更新されるようになる。
LookatTarget
プロパティ一覧
- RotationRange :
Vector2
X軸、Y軸のカメラの向く最大角度。 - FollowSpeed :
float
ターゲットの方を向くまでの速度を指定する。
RotationRange
にはX軸、Y軸のカメラの向く最大角度を指定できるみたいです。
気を付けたいのは、設定した角度の半分ずつをプラスマイナスで使用するということ。
例としては、70を設定した場合、初期のカメラ向きの-35度から+35度まで回転可能になります。
360を超えて指定する場合は、回転に制限がなくなりますよー。
FollowSpeed
にはターゲットの方を向くまでの速度を指定するみたいです。
ただ、Follow**Speed**
という名前ではあるが、スクリプト上ではVector3.SmoothDamp()
の第4引数として使用されるため、実際に使用される単位としては時間だったりします。
そのため、設定する数値が小さければ小さいほど早くターゲットの方を向くのです。
設定する際の注意
Start()
にて初期のカメラの回転情報を保存しておき、ターゲットへの回転を掛け合わせることでカメラの回転を更新していく形のようです。
そのため、初期状態のカメラの回転状態はとっても重要。
LookatTarget.cs
protected override void Start() { base.Start(); m_OriginalRotation = transform.localRotation; }
基本LookatTarget
はX軸とY軸の回転しか用いないのだけど、初期状態でX軸に回転している状態だと計算の過程でZ軸まで回転してしまう可能性がでてきます。
ああー……、傾いてしまった。
TargetFieldOfView
カメラのFieldOfViewを制御して、ズームを表現してくれるみたいです。
ちなみにデフォルトではCctvCameraではデフォルトはdisableになってますよ。
プロパティ一覧
- FovAdjustTime :
float
ターゲットにズームするまでの時間。 - ZoomAmountMultiplier :
float
ズーム乗算値。 - IncludeEffectsInSize :
bool
未使用。
ZoomAmountMultiplier
にはズーム乗算値(?)を指定するみたいです。
設定する数値が小さければ小さいほど目標が大きく表示されます。
IncludeEffectsInSize
はスクリプトを見る限り使われていないきがするんだけど……。
設定する際の注意
ターゲットへのズームの計算には、ターゲットのGameObject
にあるRenderer
のBounds
を利用して必要な大きさを計算しているようです。
子のRenderer
のBounds
も計算に含まれます。
ただし、TrailRenderer
やParticleSystemRenderer
は計算に含まれないので、その手のオブジェクトでは正しくズームされません。
当然、Renderer
のないターゲットには正しく動作しないことに注意が必要です。
どんどん近づいて行ってしまっています。
HandheldCamera
基本的な挙動はCctvCamera同じみたいですね。
それに手ぶれが追加された感じです。
Prefabの作りはCctvCamera同様に階層がないシンプルな作り。
以下のコンポーネントがアタッチされていました。
HandHeldCam
TargetFieldOfView
HandHeldCam
HandHeldCam
はLookatTarget
を継承しているため、同じパラメータが存在します。
プロパティ一覧
- SwaySpeed :
float
ランダム速度 - BaseSwayAmount :
float
カメラ位置に対するブレの量。 - TrackingSwayAmount :
float
ターゲット位置に対するブレの量。
手ぶれのランダム性はMathf.PerlinNoise()
を使って表現しているようです。
次のPerlinNoiseのY地点に進む速度をSwaySpeed
にて指定しているのかな。
パーリンノイズについては、僕がまだまだ勉強不足なので割愛しますね。
設定する際の注意
基本的にな挙動はLookatTarget
を継承しているので、注意点もLookatTarget
と同じかな。
FreeLookCameraRig
ターゲットに向かって移動して近づいていきます。
マウスの位置に合わせて上下左右にカメラが向く機能がついていますね。
Prefabの作りは3つの階層の構成になっています。
Rig - Pivot - Camera
以下のコンポーネントがアタッチされていました。
FreeLookCam
ProtectCameraFromWallClip
FreeLookCam
プロパティ一覧
- MoveSpeed :
float
ターゲットに向かって移動する速度。 - TurnSpeed :
float
カメラの回転速度 - TurnSmoothing :
float
カメラの回転の補間速度。 - TiltMax :
float
カメラのX軸の最大角度。 - TiltMin :
float
カメラのX軸の最小角度。 - LockCursor :
bool
カーソルを非表示にするかどうか。 - VerticalAutoReturn :
bool
有効にすると自動でカメラのX軸回転を0に戻るようになる。
MoveSpeed
はスピードとは名ばかりでVector3.Lerp()
の割合で使用されています。スピードじゃないです。
TurnSmoothing
に0.0f
指定すると補間されなくなります。ちなみに指定した数値はQuaternion.Slerp()
で使用されますよ。
TiltMin
に指定した数値は、スクリプト上でマイナス値を掛けて使用されています。そのためエディタ上でマイナス値で設定すると大変なことになっちゃいます。
VerticalAutoReturn
を有効にした場合、TurnSmoothing
に値を指定しておかないと、X軸回転が毎フレーム0になるのでたいへんなことになりました。
すげぇグラグラする。
設定する際の注意
Prefabの各GameObject
の担当として、
RigのGameObject
がターゲットの位置に向かって移動していきます
最終的にRigはターゲットとほぼ同じ位置になるはずです。
そのままではカメラがターゲットにめり込んでしまいます。
そこで子GameObject
であるPivotの位置を調整することでターゲットを映すのに最適な位置にする形で使用するみたいです。
ちなみに、その子GameObject
であるCameraの位置は、後述のProtectCameraFromWallClip
が固定してしまうため、あんまり位置調整には適さないみたいですね。
カメラの向きの回転については、各GameObject
で担当が決まっているみたいです。
Y軸回転はRigが、X軸回転はPivotのGameObject
が担当しているようです。
初期状態で担当している軸以外を回転しているとあんまりいいことはなさそうですねー。
カメラの向きの操作更新はUpdate()
で更新されており、UpdateType
の指定は関係なかったりします。
そのため、ManualUpdate
を指定したところで、容赦なく勝手に更新されたりします。
カメラの向きの操作の入力は、CrossPlatformInputManager
が使われているみたいですね。
CrossPlatformInput
については昔に少しだけ調べたような気がするけど。
www.urablog.xyz
ただ、カーソルのOnOffはInput.GetMouseButtonUp()
を使用しています。
うーん、統一感……。
ProtectCameraFromWallClip
壁を素早くすり抜けるためのコンポーネントみたいですね。
ここでいう壁とは、Collider
があり、Rigidbody
がないオブジェクトのことを指すみたいです。
Prefab構成一番下のCameraがついているGameObject
の位置を制御してすり抜け処理を行っているようです。
初期のCameraの位置がデフォルトの位置扱いになるので重要になります。
ギュインと抜けます。ギュインと。
プロパティ一覧
- ClipMoveTime :
float
壁を検知した際に、壁の位置まで移動する時間。 - ReturnTime :
float
壁をすり抜けた後に、デフォルトに位置まで戻る時間。 - SphereCastRadius :
float
壁を検知する範囲。 - VisualiseInEditor :
bool
壁を検知した際に、エディターのSceneビューに壁までのレイを表示するかのフラグ。 - ClosestDistance :
float
壁を検知して、最も壁に近づいた際に、近づける最小距離。 - DontClipTag :
string
壁と検知しないオブジェクトのタグ。
MultipurposeCameraRig
ターゲットに向かってカメラが移動します。
カメラの向きはターゲットの進行方向に向くように回転してくれます。
ターゲットの進行方向はTransform.forward
を使用するけど、設定しだいでRigidbody.velocity
を使用することもできるみたいです。
Prefabの作りは3つの階層の構成になっています。
Rig - Pivot - Camera
以下のコンポーネントがアタッチされていました。
AutoCam
ProtectCameraFromWallClip
AutoCam
プロパティ一覧
- MoveSpeed :
float
ターゲットに向かって移動する速度。 - TurnSpeed :
float
カメラの回転速度。 - RollSpeed :
float
Upベクトルの回転速度。Z軸の回転に対する速さかな。 - FollowVelocity :
bool
有効にすれば、ターゲットの進行方向としてRigidbody.velocity
を使用することが出来る。 - FollowTilt :
bool
ターゲットのX軸の回転に対して、カメラの向きも対応するかのフラグ。 - SpinTurnLimit :
float
回転量のリミット - TargetVelocityLowerLimit :
float
m_FollowVelocity
が有効の場合、この値以上のRigidbody.velocity
の場合にRigidbody.velocity
が進行方向として有効となる。 - SmoothTurnTime :
float
回転量の補間速度。
やっぱりMoveSpeed
はスピードとは名ばかりでVector3.Lerp()
の割合で使用されています
そしてRollSpeed
もスピードとは名ばかりでVector3.Slerp()
の割合で使用されています。
設定する際の注意
m_FollowVelocity
を有効にすることでRigidbody.velocity
を進行方向の判定として利用することができます。
ただし、使用されるRigidbody
には、継承元クラスであるAbstractTargetFollower
のtargetRigidbody
が使用されます。
しかし、targetRigidbody
はStart()
の時点でしか設定されなかったりします。
これはバグだろうか……。
そのため、ランタイム中にターゲットを変更する場合などには、ターゲットのRigidbody
が使われません。
ちなみに1週間ゲームジャムで使用した際は、以下のようにコードを修正しました
AbstractTargetFollower.cs
public virtual void SetTarget(Transform newTransform) { m_Target = newTransform; if (m_Target != null) { targetRigidbody = m_Target.GetComponent<Rigidbody>(); } }
おわりに
最終的に1週間ゲームジャムでは、MultipurposeCameraRigを使用しました。
上記のようにバグらしき挙動は見られましたが、カメラの挙動作成の時間は削減できたので助かりました。
それにしても、各スクリプトでプロパティ名などフォーマットに大分違いがみられたんだけど、いろんな人が作ってるのかなぁ。