Skip to content

Instantly share code, notes, and snippets.

@monsieurh
Created June 6, 2019 15:54
Show Gist options
  • Save monsieurh/45faae1bf7de0d5bb2958d3daa281b04 to your computer and use it in GitHub Desktop.
Save monsieurh/45faae1bf7de0d5bb2958d3daa281b04 to your computer and use it in GitHub Desktop.
La camera des jeux d'action !
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