Created
June 6, 2019 15:54
-
-
Save monsieurh/45faae1bf7de0d5bb2958d3daa281b04 to your computer and use it in GitHub Desktop.
La camera des jeux d'action !
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
[RequireComponent(typeof(Camera))] | |
public class GameCamera : MonoBehaviour | |
{ | |
[Header("Runtime attributes")] [Tooltip("The point the camera is looking at")] | |
public Transform cameraTarget; | |
[Tooltip("The point the camera is going to")] | |
public Transform cameraAnchor; | |
[Tooltip("Setting this to true will widen the fov to 'wideFov' otherwise to 'defaultFov'")] | |
public bool isWideMode; | |
[Tooltip("Setting this to false won't smooth/lerp the fov")] | |
public bool isRotationSmoothingEnabled = true; | |
[Tooltip("Setting this to false won't smooth/lerp the position")] | |
public bool isPositionSmoothingEnabled = true; | |
[Tooltip("Setting this to false won't smooth/lerp the rotation")] | |
public bool isFovSmoothingEnabled = true; | |
[Tooltip("Setting this to false will prevent the camera from moving")] | |
public bool lockPosition; | |
[Tooltip("Setting this to false will prevent the camera from rotating")] | |
public bool lockRotation; | |
[Tooltip("Setting this to false will prevent the camera from changing fov")] | |
public bool lockFov; | |
// ReSharper disable once MemberCanBePrivate.Global | |
// ReSharper disable once UnusedAutoPropertyAccessor.Global | |
/// <summary> | |
/// This value can be used to make your GUI react to camera movement. | |
/// </summary> | |
/// <example> | |
/// myGuiTransform.position = myGuiTransform.initialPosition + GameCamera.CameraScreenSpaceMotion; | |
/// </example> | |
public Vector2 CameraScreenSpaceMotion { get; private set; } | |
[Header("Configuration"), SerializeField] | |
private UpdateMode currentUpdateMode; | |
[Tooltip("The time it take for the spring to bring the camera to the target position"), SerializeField] | |
public float positionSmoothing; | |
[Tooltip("The time it take for the spring to bring the camera to the target rotation"), SerializeField] | |
public float rotationSmoothing; | |
[Tooltip("The time it take for the spring to bring the camera to the target FOV"), SerializeField] | |
private float fovSmooth = 1f; | |
[Tooltip("The amount of tilt to apply when you turn sideways"), SerializeField, Range(-1, 1)] | |
private float cameraTilt; | |
[Tooltip("The FOV to use by default"), SerializeField] | |
private float defaultFov = 75; | |
[Tooltip("The FOV to use when there is some action"), SerializeField] | |
public float wideFov = 120; | |
private Vector3 positionVelocity; | |
private float xAngleVelocity; | |
private float fovVelocity; | |
private float yAngleVelocity; | |
#region utils | |
private Camera Camera => GetComponent<Camera>(); | |
private float DeltaTime => currentUpdateMode == UpdateMode.FixedUpdate ? Time.fixedDeltaTime : Time.deltaTime; | |
private bool IsMissingTarget => cameraTarget == null || cameraAnchor == null; | |
private Transform self; | |
private void Awake() | |
{ | |
self = transform; //Avoids regular calls to `this.transform` which is as slow as GetComponent<Transform>() | |
} | |
#endregion | |
#region update_mode | |
private enum UpdateMode | |
{ | |
Update, | |
LateUpdate, | |
FixedUpdate | |
} | |
private void Update() | |
{ | |
if (currentUpdateMode == UpdateMode.Update) CustomUpdate(); | |
} | |
private void LateUpdate() | |
{ | |
if (currentUpdateMode == UpdateMode.LateUpdate) CustomUpdate(); | |
} | |
private void FixedUpdate() | |
{ | |
if (currentUpdateMode == UpdateMode.FixedUpdate) CustomUpdate(); | |
} | |
#endregion | |
private void CustomUpdate() | |
{ | |
if (IsMissingTarget) return; | |
if (!lockPosition) UpdatePosition(); | |
if (!lockRotation) UpdateRotation(); | |
if (!lockFov) UpdateFov(); | |
} | |
private void UpdateFov() | |
{ | |
float target = isWideMode ? wideFov : defaultFov; | |
if (isFovSmoothingEnabled) target = SmoothFov(target); | |
Camera.fieldOfView = target; | |
} | |
private void UpdateRotation() | |
{ | |
Vector3 target = Quaternion.LookRotation(cameraTarget.transform.position - transform.position).eulerAngles; | |
if (isRotationSmoothingEnabled) target = SmoothRotation(target); | |
Camera.transform.rotation = Quaternion.Euler(target); | |
} | |
private void UpdatePosition() | |
{ | |
Vector3 previous = transform.position; | |
Vector3 target = cameraAnchor.transform.position; | |
if (isPositionSmoothingEnabled) target = SmoothPosition(target); | |
self.position = target; | |
CameraScreenSpaceMotion = target - previous; | |
} | |
#region smoothing | |
private Vector3 SmoothRotation(Vector3 target) | |
{ | |
Vector3 current = transform.rotation.eulerAngles; | |
current.x = Mathf.SmoothDampAngle(current.x, | |
target.x, | |
ref xAngleVelocity, | |
rotationSmoothing, | |
float.MaxValue, | |
DeltaTime | |
); | |
current.y = Mathf.SmoothDampAngle(current.y, | |
target.y, | |
ref yAngleVelocity, | |
rotationSmoothing, | |
float.MaxValue, | |
DeltaTime | |
); | |
current.z = yAngleVelocity * -cameraTilt; //We never want to use z angle except for dynamic tilting | |
return current; | |
} | |
private Vector3 SmoothPosition(Vector3 target) | |
{ | |
return Vector3.SmoothDamp( | |
self.position, | |
target, | |
ref positionVelocity, | |
positionSmoothing, | |
float.MaxValue, | |
DeltaTime | |
); | |
} | |
private float SmoothFov(float target) | |
{ | |
float smoothTime = isWideMode ? fovSmooth : fovSmooth / 2f; //Fov comes back to normal twice as fast | |
return Mathf.SmoothDamp(Camera.fieldOfView, target, ref fovVelocity, smoothTime, float.MaxValue, DeltaTime); | |
} | |
#endregion | |
#region gizmos | |
private void OnDrawGizmos() | |
{ | |
DrawGizmos(.25f, .1f); | |
} | |
private void OnDrawGizmosSelected() | |
{ | |
DrawGizmos(.75f, .2f); | |
} | |
private void DrawGizmos(float alpha, float size) | |
{ | |
if (cameraTarget != null) | |
{ | |
Gizmos.color = new Color(0f, 1f, .5f, alpha); | |
Gizmos.DrawSphere(cameraTarget.position, size); | |
Gizmos.DrawLine(transform.position, cameraTarget.position); | |
} | |
if (cameraAnchor != null) | |
{ | |
Gizmos.color = new Color(0f, .5f, 1f, alpha); | |
Gizmos.DrawSphere(cameraAnchor.position, size); | |
Gizmos.DrawLine(transform.position, cameraAnchor.position); | |
} | |
Gizmos.color = new Color(.5f, 1f, .5f, alpha); | |
Gizmos.DrawSphere(transform.position, size); | |
} | |
#endregion | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment