You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

339 lines
12 KiB
C#

3 years ago
using UnityEngine;
using MalbersAnimations.Scriptables;
using System.Collections;
using UnityEngine.Events;
using UnityEditor;
/// <summary> This is the same Camera FreeLookCam of the Stardard Assets Modify to Fit My Needs </summary>
namespace MalbersAnimations
{
[AddComponentMenu("Malbers/Utilities/Camera/Free Look Camera")]
public class MFreeLookCamera : MonoBehaviour
{
#if REWIRED
[Header("Rewired Connection")]
#else
[HideInInspector]
#endif
public string PlayerID = "Player0";
[Space]
public TransformReference m_Target; // The target object to follow
public UpdateType updateType = UpdateType.FixedUpdate; // stores the selected update type
/// <summary>Stores the Update type when the game starts </summary>
internal UpdateType defaultUpdate;
public float m_MoveSpeed = 10f; // How fast the rig will move to keep up with the target's position.
[Range(0f, 10f)]
public float m_TurnSpeed = 10f; // How fast the rig will rotate from user input.
public float m_TurnSmoothing = 10f; // How much smoothing to apply to the turn input, to reduce mouse-turn jerkiness
public float m_TiltMax = 75f; // The maximum value of the x axis rotation of the pivot.
public float m_TiltMin = 45f; // The minimum value of the x axis rotation of the pivot.
[Header("Camera Input Axis")]
public InputAxis Vertical = new InputAxis("Mouse Y", true, false);
public InputAxis Horizontal = new InputAxis("Mouse X", true, false);
public Vector2Reference MovementAxis = new Vector2Reference();
private IGravity TargetGravity;
[Space]
public FreeLockCameraManager manager;
public FreeLookCameraState DefaultState;
public Transform DefaultTarget { get; set; }
[HideInInspector] public UnityEvent OnStateChange = new UnityEvent();
/// <summary>Additional FOV when Sprinting</summary>
[Space, Header("Sprint Field of View"), Tooltip("Additional FOV when Sprinting")]
public FloatReference SprintFOV = new FloatReference(10f);
/// <summary>Duration of the Transition when Sprinting </summary>
[Tooltip("Additional FOV when Sprinting")]
public FloatReference FOVTransition = new FloatReference(1f);
private float m_LookAngle; // The rig's y axis rotation.
private float m_TiltAngle; // The pivot's x axis rotation.
private Vector3 m_PivotEulers;
private Vector3 m_UpVector;
private Quaternion m_PivotTargetRot;
private Quaternion m_TransformTargetRot;
/// <summary>Next Camera State to Go to</summary>
protected FreeLookCameraState NextState;
/// <summary> Current Camera State </summary>
protected FreeLookCameraState currentState;
IEnumerator IChangeStates;
IEnumerator IChange_FOV;
public Transform Target
{
get => m_Target.Value;
set
{
m_Target.Value = value;
GetTargetGravity();
}
}
/// <summary>Main Camera</summary>
public Camera Cam { get; private set; }
/// <summary>Main Camera Transform</summary>
public Transform CamT { get; private set; }
public Transform Pivot { get; private set; }
///// <summary>Camera Horizontal Input Value</summary>
//public float XCam { get; set; }
///// <summary>Camera Vertical Input Value</summary>
//public float YCam { get; set; }
/// <summary> Stores the Current FOV of the Camera</summary>
public float ActiveFOV { get; internal set; }
private IInputSystem inputSystem;
protected void Awake()
{
Cam = GetComponentInChildren<Camera>();
CamT = Cam.transform;
Pivot = Cam.transform.parent;
currentState = null;
NextState = null;
if (manager) manager.SetCamera(this);
if (DefaultState) Set_State(DefaultState);
m_PivotEulers = Pivot.rotation.eulerAngles;
m_PivotTargetRot = Pivot.transform.localRotation;
m_TransformTargetRot = transform.localRotation;
ActiveFOV = Cam.fieldOfView;
inputSystem = DefaultInput.GetInputSystem(PlayerID);
Horizontal.InputSystem = Vertical.InputSystem = inputSystem; //Update the Input System on the Axis
defaultUpdate = updateType;
if (DefaultState == null)
{
DefaultState = ScriptableObject.CreateInstance<FreeLookCameraState>();
DefaultState.CamFOV = Cam.fieldOfView;
DefaultState.PivotPos = Pivot.localPosition;
DefaultState.CamPos = CamT.localPosition;
DefaultState.name = "Default State";
OnStateChange.Invoke();
}
MovementAxis = Vector2.zero;
}
void Start()
{
GetTargetGravity();
}
void GetTargetGravity()
{
if (Target)
TargetGravity = Target.gameObject.FindInterface<IGravity>();
}
public virtual void Set_State(FreeLookCameraState state)
{
Pivot.localPosition = state.PivotPos;
Cam.transform.localPosition = state.CamPos;
Cam.fieldOfView = ActiveFOV = state.CamFOV;
OnStateChange.Invoke();
// Debug.Log("Set_State" + state.name);
}
#region Private Methods
protected void FollowTarget(float deltaTime)
{
if (Target == null) return;
transform.position = Vector3.Lerp(transform.position, Target.position, deltaTime * m_MoveSpeed); // Move the rig towards target position.
}
internal void UpdateState(FreeLookCameraState state)
{
if (state == null || !enabled) return;
Pivot.localPosition = state.PivotPos;
CamT.localPosition = state.CamPos;
Cam.fieldOfView = ActiveFOV = state.CamFOV;
//Debug.Log("UpdateState" + state.name);
OnStateChange.Invoke();
}
public void EnableInput(bool value)
{
Vertical.active = value;
Horizontal.active = value;
}
public virtual void SetInputAxis(Vector2 input) => MovementAxis.Value = input;
private void HandleRotationMovement(float time)
{
if (Time.timeScale < float.Epsilon) return;
if (Horizontal.active) MovementAxis.x = Horizontal.GetAxis;
if (Vertical.active) MovementAxis.y = Vertical.GetAxis;
m_LookAngle += MovementAxis.x * m_TurnSpeed; // Adjust the look angle by an amount proportional to the turn speed and horizontal input.
if (TargetGravity != null) m_UpVector = Vector3.Slerp(m_UpVector, TargetGravity.UpVector, time * 15);
// transform.rotation = Quaternion.FromToRotation(transform.up, m_UpVector) * transform.rotation; //This Make it f
m_TransformTargetRot = Quaternion.FromToRotation(transform.up, m_UpVector) * Quaternion.Euler(0f, m_LookAngle, 0f); // Rotate the rig (the root object) around Y axis only:
m_TransformTargetRot = Quaternion.Euler(0f, m_LookAngle, 0f); // Rotate the rig (the root object) around Y axis only:
m_TiltAngle -= MovementAxis.y * m_TurnSpeed; // on platforms with a mouse, we adjust the current angle based on Y mouse input and turn speed
m_TiltAngle = Mathf.Clamp(m_TiltAngle, -m_TiltMin, m_TiltMax); // and make sure the new value is within the tilt range
m_PivotTargetRot = Quaternion.Euler(m_TiltAngle, m_PivotEulers.y, m_PivotEulers.z); // Tilt input around X is applied to the pivot (the child of this object)
Pivot.localRotation = Quaternion.Slerp(Pivot.localRotation, m_PivotTargetRot, m_TurnSmoothing * time);
transform.localRotation = Quaternion.Slerp(transform.localRotation, m_TransformTargetRot, m_TurnSmoothing * time);
}
void FixedUpdate()
{
if (updateType == UpdateType.FixedUpdate)
{
FollowTarget(Time.fixedDeltaTime);
HandleRotationMovement(Time.fixedDeltaTime);
}
}
void LateUpdate()
{
if (updateType == UpdateType.LateUpdate)
{
FollowTarget(Time.deltaTime);
HandleRotationMovement(Time.deltaTime);
}
}
#endregion
public void Set_State_Smooth(FreeLookCameraState state) => SetState(state, false);
public void Set_State_Temporal(FreeLookCameraState state) => SetState(state, true);
internal void SetState_Instant(FreeLookCameraState state, bool temporal)
{
if (state == null) return;
if (currentState && state == currentState) return;
NextState = state;
if (IChangeStates != null) StopCoroutine(IChangeStates);
if (!temporal) DefaultState = state; //Set the New Default State when is NOT temporal
UpdateState(state);
}
internal void SetState(FreeLookCameraState state, bool temporal)
{
if (state == null) return;
if (currentState && state == currentState) return;
NextState = state;
if (IChangeStates != null) StopCoroutine(IChangeStates);
if (!temporal) DefaultState = state; //Set the New Default State when is NOT temporal
IChangeStates = StateTransition(state.transition);
StartCoroutine(IChangeStates);
}
public void Set_State_Default_Smooth() => SetState(DefaultState, true);
public void Set_State_Default() => Set_State(DefaultState);
public void ToggleSprintFOV(bool val) => ChangeFOV(val ? ActiveFOV + SprintFOV.Value : ActiveFOV);
public void ChangeFOV(float newFOV)
{
if (IChange_FOV != null) StopCoroutine(IChange_FOV);
IChange_FOV = C_SprintFOV(newFOV, FOVTransition);
StartCoroutine(IChange_FOV);
}
#region Coroutines
private IEnumerator StateTransition(float time)
{
float elapsedTime = 0;
currentState = NextState;
while (elapsedTime < time)
{
Pivot.localPosition = Vector3.Lerp(Pivot.localPosition, NextState.PivotPos, Mathf.SmoothStep(0, 1, elapsedTime / time));
CamT.localPosition = Vector3.Lerp(CamT.localPosition, NextState.CamPos, Mathf.SmoothStep(0, 1, elapsedTime / time));
Cam.fieldOfView = ActiveFOV = Mathf.Lerp(Cam.fieldOfView, NextState.CamFOV, Mathf.SmoothStep(0, 1, elapsedTime / time));
OnStateChange.Invoke();
elapsedTime += Time.deltaTime;
//Debug.Log("StateTransition");
yield return null;
}
UpdateState(NextState);
NextState = null;
yield return null;
}
private IEnumerator C_SprintFOV(float newFOV, float time)
{
float elapsedTime = 0f;
float startFOV = Cam.fieldOfView;
while (elapsedTime < time)
{
Cam.fieldOfView = Mathf.Lerp(startFOV, newFOV, Mathf.SmoothStep(0, 1, elapsedTime / time));
elapsedTime += Time.deltaTime;
yield return null;
}
Cam.fieldOfView = newFOV;
yield return null;
}
#endregion
public virtual void Target_Set(Transform newTransform) => Target = DefaultTarget = newTransform;
public virtual void Target_Set_Temporal(Transform newTransform) => Target = newTransform;
public virtual void Target_Restore() => Target = DefaultTarget;
public virtual void Target_Set(GameObject newGO) => Target_Set(newGO.transform);
public virtual void Target_Set_Temporal(GameObject newGO) => Target_Set_Temporal(newGO.transform);
/// <summary> When the Rider is Aiming is necesary to change the Update Mode to Late Update</summary>
public virtual void ForceUpdateMode(bool val) => updateType = val ? UpdateType.LateUpdate : defaultUpdate;
}
}