🙌
Unity Cinemachineを使わないカメラワーク設計
SimpleCameraControl
CameraManager.cs
/*
被写体の座標 CameraParentの座標
被写体からの距離 CameraChildのローカルZ座標(負数)
画角 Main CameraのField Of View
被写体への回り込み角度 CameraParentの角度
視界オフセット座標 Main Cameraのローカル座標
視界オフセット角度 Main Cameraのローカル角度
*/
using System;
using UnityEngine;
// シーンを実行しなくてもカメラワークが反映されるよう、ExecuteInEditModeを付与
[ExecuteInEditMode]
public class CameraManager : MonoBehaviour
{
/// <summary> カメラのパラメータ </summary>
[Serializable]
public class Parameter
{
public Vector3 position;
public Vector3 angles = new Vector3(10f, 0f, 0f);
public float distance = 7f;
public float fieldOfView = 45f;
public Vector3 offsetPosition = new Vector3(0f, 1f, 0f);
public Vector3 offsetAngles;
}
[SerializeField]
private Transform _parent;
[SerializeField]
private Transform _child;
[SerializeField]
private Camera _camera;
[SerializeField]
private Parameter _parameter;
// 被写体などの移動更新が済んだ後にカメラを更新したいので、LateUpdateを使う
private void LateUpdate()
{
if(_parent == null || _child == null || _camera == null)
{
return;
}
// パラメータを各種オブジェクトに反映
_parent.position = _parameter.position;
_parent.eulerAngles = _parameter.angles;
var childPos = _child.localPosition;
childPos.z = -_parameter.distance;
_child.localPosition = childPos;
_camera.fieldOfView = _parameter.fieldOfView;
_camera.transform.localPosition = _parameter.offsetPosition;
_camera.transform.localEulerAngles = _parameter.offsetAngles;
}
}
被写体を追いかけるには、Parameter.positionに被写体の座標を代入し続ける必要があります。
多くの場合、被写体はTransformを持つと思うのでCamera.Parameterクラスに追従対象(trackTarget)をTransformで指定できるように拡張してみます
CameraManager.cs
/// <summary> カメラのパラメータ </summary>
[Serializable]
public class Parameter
{
public Transform trackTarget; // ← 追加
public Vector3 position;
public Vector3 angles = new Vector3(10f, 0f, 0f);
public float distance = 7f;
public float fieldOfView = 45f;
public Vector3 offsetPosition = new Vector3(0f, 1f, 0f);
public Vector3 offsetAngles;
}
private void LateUpdate()
{
/* ↓↓↓ここから追加↓↓↓ */
if(_parameter.trackTarget != null)
{
// 被写体がTransformで指定されている場合、positionパラメータに座標を上書き
_parameter.position = _parameter.trackTarget.position;
}
/* ↑↑↑追加ここまで↑↑↑ */
// パラメータを各種オブジェクトに反映
_parent.position = _parameter.position;
}
被写体の動きに対してちょっと遅れてついてくる
Lerpを使った簡易的なイージングを入れて、座標更新の変化をなめらかなものにします。
ピッタリカメラがついてくるよりは自然な印象のカメラワークになりました。
CameraManager.cs
private void LateUpdate()
{
if(_parameter.trackTarget != null)
{
// がTransformで指定されている場合、positionパラメータに座標を上書き
_parameter.position = Vector3.Lerp(
a: _parameter.position,
b: _parameter.trackTarget.position,
t: Time.deltaTime * 4f
);
}
}
マウスでカメラを回転させるFPS/TPSっぽい動き
CameraManager.csに実装するよりは、操作用のクラスを作って外部からカメラのParameterを変更するほうが設計としては良い
CameraManager.cs
public class CameraManager : MonoBehaviour
{
private void Update()
{
// マウスの動きの差分をカメラの回り込み角度に反映
Vector3 diffAngles = new Vector3(
x: -Input.GetAxis("Mouse Y"),
y: Input.GetAxis("Mouse X")
) * 10f;
_parameter.angles += diffAngles;
}
}
Lerpメソッドを作って2つのカメラワークをブレンド遷移
次に行くべき場所を示したり、仕掛けを操作したことで動いたオブジェクトを一度見せたりする
CameraManager.cs
public class CameraManager : MonoBehaviour
{
/// <summary> カメラのパラメータ </summary>
[Serializable]
public class Parameter
{
public static Parameter Lerp(Parameter a, Parameter b, float t, Parameter ret)
{
ret.position = Vector3.Lerp(a.position, b.position, t);
ret.angles = LerpAngles(a.angles, b.angles, t);
ret.distance = Mathf.Lerp(a.distance, b.distance, t);
ret.fieldOfView = Mathf.Lerp(a.fieldOfView, b.fieldOfView, t);
ret.offsetPosition = Vector3.Lerp(a.offsetPosition, b.offsetPosition, t);
ret.offsetAngles = LerpAngles(a.offsetAngles, b.offsetAngles, t);
return ret;
}
private static Vector3 LerpAngles(Vector3 a, Vector3 b, float t)
{
Vector3 ret = Vector3.zero;
ret.x = Mathf.LerpAngle(a.x, b.x, t);
ret.y = Mathf.LerpAngle(a.y, b.y, t);
ret.z = Mathf.LerpAngle(a.z, b.z, t);
return ret;
}
}
}
TPSゲームで通常のビューから銃を構えたビューに切り替える
ズーム時のビューはdistance(被写体からの距離)を小さくするだけでなく、fieldOfView(画角)も小さくすることで、位置的な近さ+遠近感を薄めて遠くのものが見えやすい
offsetPosition(Main Cameraのローカル座標)の x の値をズラして、被写体であるプレイヤーが画面中央の照準に被らないようにしている
とりあえずイイカンジのカメラ構造
3D空間上に表示する距離・角度の影響を受けないUI
Discussion