using UnityEngine; using MalbersAnimations.Scriptables; using System.Collections; using UnityEngine.Events; using UnityEditor; /// This is the same Camera FreeLookCam of the Stardard Assets Modify to Fit My Needs 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 /// Stores the Update type when the game starts 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(); /// Additional FOV when Sprinting [Space, Header("Sprint Field of View"), Tooltip("Additional FOV when Sprinting")] public FloatReference SprintFOV = new FloatReference(10f); /// Duration of the Transition when Sprinting [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; /// Next Camera State to Go to protected FreeLookCameraState NextState; /// Current Camera State protected FreeLookCameraState currentState; IEnumerator IChangeStates; IEnumerator IChange_FOV; public Transform Target { get => m_Target.Value; set { m_Target.Value = value; GetTargetGravity(); } } /// Main Camera public Camera Cam { get; private set; } /// Main Camera Transform public Transform CamT { get; private set; } public Transform Pivot { get; private set; } ///// Camera Horizontal Input Value //public float XCam { get; set; } ///// Camera Vertical Input Value //public float YCam { get; set; } /// Stores the Current FOV of the Camera public float ActiveFOV { get; internal set; } private IInputSystem inputSystem; protected void Awake() { Cam = GetComponentInChildren(); 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(); 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(); } 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); /// When the Rider is Aiming is necesary to change the Update Mode to Late Update public virtual void ForceUpdateMode(bool val) => updateType = val ? UpdateType.LateUpdate : defaultUpdate; } }