projectEli/Assets/NeoFPS/Core/Camera/AdditiveEffects/HeadBobV2.cs
2022-11-06 20:28:33 -05:00

201 lines
8.1 KiB
C#

using UnityEngine;
using NeoFPS.CharacterMotion;
namespace NeoFPS
{
[HelpURL("https://docs.neofps.com/manual/fpcamref-mb-headbobv2.html")]
public class HeadBobV2 : MonoBehaviour, IAdditiveTransform
{
[SerializeField, Tooltip("The maximum position offset along the horizontal axis in either direction.")]
private float m_HorizontalDistance = 0.075f;
[SerializeField, Tooltip("The maximum position offset along the vertical axis in either direction.")]
private float m_VerticalDistance = 0.1f;
[SerializeField, Tooltip("The maximum angle to roll the camera in either direction.")]
private float m_RollAngle = 0.25f;
[SerializeField, Tooltip("The horizontal position curve over one step cycle for the weapon bob.")]
private AnimationCurve m_HorizontalCurve = new AnimationCurve(
new Keyframe(-1f, 0f), new Keyframe(-0.5f, 1f),
new Keyframe(0f, 0f), new Keyframe(0.5f, -1f),
new Keyframe(1f, 0f));
[SerializeField, Tooltip("The vertical position curve over one step cycle for the weapon bob.")]
private AnimationCurve m_VerticalCurve = new AnimationCurve(
new Keyframe(-1f, 1f), new Keyframe(-0.65f, -0.6f), new Keyframe(-0.4f, 0f),
new Keyframe(0f, 1f), new Keyframe(0.35f, -0.6f), new Keyframe(0.6f, 0f),
new Keyframe(1f, 1f));
[SerializeField, Tooltip("The roll curve over one step cycle for the weapon bob.")]
private AnimationCurve m_RollCurve = new AnimationCurve(
new Keyframe(-1f, 1f), new Keyframe(-0.85f, -0.15f), new Keyframe(-0.8f, 0.05f), new Keyframe(-0.75f, 0f),
new Keyframe(0f, -1f), new Keyframe(0.15f, 0.15f), new Keyframe(0.2f, -0.05f), new Keyframe(0.25f, 0f),
new Keyframe(1f, 1f));
[SerializeField, Range(0f, 5f), Tooltip("At or below this speed the bob will be scaled to zero.")]
private float m_MinLerpSpeed = 0.5f;
[SerializeField, Range(0.25f, 10f), Tooltip("At or above this speed the bob will have its full effect.")]
private float m_MaxLerpSpeed = 4f;
[Header("Aim Compensation")]
[SerializeField, Tooltip("Aim compensation involves rotating the camera so that the crosshair stays fixed on the same point in space.")]
private bool m_UseAimCompensation = true;
[SerializeField, Tooltip("The transform to use for camera/crosshair casting.")]
private Transform m_AimTransform = null;
[SerializeField, Tooltip("The layers to check against for aim depth.")]
private LayerMask m_AimLayers = PhysicsFilter.Masks.BulletBlockers;
[SerializeField, Delayed, Tooltip("The minimum distance to compensate against. At very close distances, the bob will have to rotate larger angles to keep the crosshair fixed on target.")]
private float m_MinDistance = 2.5f;
[SerializeField, Delayed, Tooltip("The maximum distance to compensate against.")]
private float m_MaxDistance = 200f;
[SerializeField, Range(0f, 1f), Tooltip("A damping against the crosshair target. Prevents objects crossing the camera at close ranges from causing sudden shifts.")]
private float m_Damping = 0.25f;
private const float k_FadeLerp = 0.05f;
private Vector3 m_BobPosition = Vector3.zero;
private Quaternion m_BobRotation = Quaternion.identity;
private float m_SettingsStrength = 1f;
private float m_Weight = 1f;
private float m_TargetWeight = 0f;
private float m_CompensationDistance = 200f;
private float m_CompenstationSpeed = 0f;
private IAdditiveTransformHandler m_Handler = null;
private MotionController m_Controller = null;
public Quaternion rotation
{
get { return Quaternion.Slerp(Quaternion.identity, m_BobRotation, m_SettingsStrength); }
}
public Vector3 position
{
get { return m_BobPosition * m_SettingsStrength; }
}
public bool bypassPositionMultiplier
{
get { return false; }
}
public bool bypassRotationMultiplier
{
get { return false; }
}
#if UNITY_EDITOR
void OnValidate()
{
m_HorizontalDistance = Mathf.Clamp(m_HorizontalDistance, 0f, 0.5f);
m_VerticalDistance = Mathf.Clamp(m_VerticalDistance, 0f, 0.5f);
m_MinDistance = Mathf.Clamp(m_MinDistance, 1f, m_MaxDistance);
m_MaxDistance = Mathf.Clamp(m_MaxDistance, m_MinDistance, 100f);
if (m_AimTransform == null)
m_AimTransform = transform;
}
#endif
void Awake()
{
m_Controller = GetComponentInParent<MotionController>();
m_Handler = GetComponent<IAdditiveTransformHandler>();
if (m_AimTransform == null)
m_AimTransform = transform;
FpsSettings.gameplay.onHeadBobChanged += OnHeadBobSettingsChanged;
OnHeadBobSettingsChanged(FpsSettings.gameplay.headBob);
}
void OnDestroy()
{
FpsSettings.gameplay.onHeadBobChanged -= OnHeadBobSettingsChanged;
}
private void OnHeadBobSettingsChanged(float bobStrength)
{
m_SettingsStrength = bobStrength;
if (bobStrength < 0.01f)
enabled = false;
else
enabled = true;
}
void OnEnable()
{
m_Handler.ApplyAdditiveEffect(this);
m_CompensationDistance = m_MaxDistance;
m_CompenstationSpeed = 0f;
}
void OnDisable()
{
m_Handler.RemoveAdditiveEffect(this);
}
void FixedUpdate()
{
m_Weight = Mathf.Lerp(m_Weight, m_TargetWeight, k_FadeLerp);
}
public void UpdateTransform()
{
float speed = m_Controller.smoothedStepRate;
if (m_Controller.strideLength == 0f || speed < m_MinLerpSpeed)
m_TargetWeight = 0f;
else
m_TargetWeight = (m_Controller.characterController.velocity.magnitude - m_MinLerpSpeed) / (m_MaxLerpSpeed - m_MinLerpSpeed);
if (m_Weight > 0.0001f)
{
float xCurve = m_HorizontalCurve.Evaluate(m_Controller.stepCounter);
float yCurve = m_VerticalCurve.Evaluate(m_Controller.stepCounter);
float rCurve = m_RollCurve.Evaluate(m_Controller.stepCounter);
float hRange = Mathf.Lerp(0f, m_HorizontalDistance, m_Weight);
float vRange = Mathf.Lerp(0f, m_VerticalDistance, m_Weight);
float rRange = Mathf.Lerp(0f, m_RollAngle, m_Weight);
m_BobPosition = new Vector3(xCurve * hRange, yCurve * vRange, 0f);
m_BobRotation = Quaternion.Euler(0f, 0f, rCurve * rRange);
// Handle the aim compensation
if (m_UseAimCompensation)
{
float crosshairDistance = m_MaxDistance;
// Get the clamped crosshair distance
var ray = new Ray(m_AimTransform.position, m_AimTransform.forward);
var hit = new RaycastHit();
if (PhysicsExtensions.RaycastNonAllocSingle(ray, out hit, m_MaxDistance, m_AimLayers, m_AimTransform.root, QueryTriggerInteraction.Ignore))
crosshairDistance = Mathf.Clamp(hit.distance, m_MinDistance, m_MaxDistance);
// Damp with previous
m_CompensationDistance = Mathf.SmoothDamp(m_CompensationDistance, crosshairDistance, ref m_CompenstationSpeed, Mathf.Lerp(0.05f, 1f, m_Damping));
// Get the rotation to point in to center
Vector3 compensationTarget = -m_BobPosition;
compensationTarget.z = m_CompensationDistance;
m_BobRotation *= Quaternion.LookRotation(compensationTarget);
}
}
else
{
m_BobPosition = Vector3.zero;
m_BobRotation = Quaternion.identity;
}
}
}
}