using Invector; using UnityEngine; public class vThirdPersonCamera : MonoBehaviour { #region inspector properties public Transform target; [Tooltip("Lerp speed between Camera States")] public float smoothCameraRotation = 12f; [Tooltip("What layer will be culled")] public LayerMask cullingLayer = 1 << 0; [Tooltip("Debug purposes, lock the camera behind the character for better align the states")] public bool lockCamera; public float rightOffset = 0f; public float defaultDistance = 2.5f; public float height = 1.4f; public float smoothFollow = 10f; public float xMouseSensitivity = 3f; public float yMouseSensitivity = 3f; public float yMinLimit = -40f; public float yMaxLimit = 80f; #endregion #region hide properties [HideInInspector] public int indexList, indexLookPoint; [HideInInspector] public float offSetPlayerPivot; [HideInInspector] public string currentStateName; [HideInInspector] public Transform currentTarget; [HideInInspector] public Vector2 movementSpeed; private Transform targetLookAt; private Vector3 currentTargetPos; private Vector3 lookPoint; private Vector3 current_cPos; private Vector3 desired_cPos; private Camera _camera; [SerializeField] private float distance = 5f; private float mouseY = 0f; private float mouseX = 0f; private float currentHeight; private float cullingDistance; private float checkHeightRadius = 0.4f; private float clipPlaneMargin = 0f; private float forward = -1f; private float xMinLimit = -360f; private float xMaxLimit = 360f; private float cullingHeight = 0.2f; private float cullingMinDist = 0.1f; #endregion void Start() { Init(); } public void Init() { if (target == null) return; _camera = GetComponent(); currentTarget = target; currentTargetPos = new Vector3(currentTarget.position.x, currentTarget.position.y + offSetPlayerPivot, currentTarget.position.z); targetLookAt = new GameObject("targetLookAt").transform; targetLookAt.position = currentTarget.position; targetLookAt.hideFlags = HideFlags.HideInHierarchy; targetLookAt.rotation = currentTarget.rotation; mouseY = currentTarget.eulerAngles.x; mouseX = currentTarget.eulerAngles.y; distance = defaultDistance; currentHeight = height; } void FixedUpdate() { if (target == null || targetLookAt == null) return; CameraMovement(); } /// /// Set the target for the camera /// /// public void SetTarget(Transform newTarget) { currentTarget = newTarget ? newTarget : target; } public void SetMainTarget(Transform newTarget) { target = newTarget; currentTarget = newTarget; mouseY = currentTarget.rotation.eulerAngles.x; mouseX = currentTarget.rotation.eulerAngles.y; Init(); } /// /// Convert a point in the screen in a Ray for the world /// /// /// public Ray ScreenPointToRay(Vector3 Point) { return this.GetComponent().ScreenPointToRay(Point); } /// /// Camera Rotation behaviour /// /// /// public void RotateCamera(float x, float y) { // free rotation mouseX += x * xMouseSensitivity; mouseY -= y * yMouseSensitivity; movementSpeed.x = x; movementSpeed.y = -y; if (!lockCamera) { mouseY = vExtensions.ClampAngle(mouseY, yMinLimit, yMaxLimit); mouseX = vExtensions.ClampAngle(mouseX, xMinLimit, xMaxLimit); } else { mouseY = currentTarget.root.localEulerAngles.x; mouseX = currentTarget.root.localEulerAngles.y; } } /// /// Camera behaviour /// void CameraMovement() { if (currentTarget == null) return; distance = Mathf.Lerp(distance, defaultDistance, smoothFollow * Time.deltaTime); cullingDistance = Mathf.Lerp(cullingDistance, distance, Time.deltaTime); var camDir = (forward * targetLookAt.forward) + (rightOffset * targetLookAt.right); camDir = camDir.normalized; var targetPos = new Vector3(currentTarget.position.x, currentTarget.position.y + offSetPlayerPivot, currentTarget.position.z); currentTargetPos = targetPos; desired_cPos = targetPos + new Vector3(0, height, 0); current_cPos = currentTargetPos + new Vector3(0, currentHeight, 0); RaycastHit hitInfo; ClipPlanePoints planePoints = _camera.NearClipPlanePoints(current_cPos + (camDir * (distance)), clipPlaneMargin); ClipPlanePoints oldPoints = _camera.NearClipPlanePoints(desired_cPos + (camDir * distance), clipPlaneMargin); //Check if Height is not blocked if (Physics.SphereCast(targetPos, checkHeightRadius, Vector3.up, out hitInfo, cullingHeight + 0.2f, cullingLayer)) { var t = hitInfo.distance - 0.2f; t -= height; t /= (cullingHeight - height); cullingHeight = Mathf.Lerp(height, cullingHeight, Mathf.Clamp(t, 0.0f, 1.0f)); } //Check if desired target position is not blocked if (CullingRayCast(desired_cPos, oldPoints, out hitInfo, distance + 0.2f, cullingLayer, Color.blue)) { distance = hitInfo.distance - 0.2f; if (distance < defaultDistance) { var t = hitInfo.distance; t -= cullingMinDist; t /= cullingMinDist; currentHeight = Mathf.Lerp(cullingHeight, height, Mathf.Clamp(t, 0.0f, 1.0f)); current_cPos = currentTargetPos + new Vector3(0, currentHeight, 0); } } else { currentHeight = height; } //Check if target position with culling height applied is not blocked if (CullingRayCast(current_cPos, planePoints, out hitInfo, distance, cullingLayer, Color.cyan)) distance = Mathf.Clamp(cullingDistance, 0.0f, defaultDistance); var lookPoint = current_cPos + targetLookAt.forward * 2f; lookPoint += (targetLookAt.right * Vector3.Dot(camDir * (distance), targetLookAt.right)); targetLookAt.position = current_cPos; Quaternion newRot = Quaternion.Euler(mouseY, mouseX, 0); targetLookAt.rotation = Quaternion.Slerp(targetLookAt.rotation, newRot, smoothCameraRotation * Time.deltaTime); transform.position = current_cPos + (camDir * (distance)); var rotation = Quaternion.LookRotation((lookPoint) - transform.position); transform.rotation = rotation; movementSpeed = Vector2.zero; } /// /// Custom Raycast using NearClipPlanesPoints /// /// /// /// /// /// /// bool CullingRayCast(Vector3 from, ClipPlanePoints _to, out RaycastHit hitInfo, float distance, LayerMask cullingLayer, Color color) { bool value = false; if (Physics.Raycast(from, _to.LowerLeft - from, out hitInfo, distance, cullingLayer)) { value = true; cullingDistance = hitInfo.distance; } if (Physics.Raycast(from, _to.LowerRight - from, out hitInfo, distance, cullingLayer)) { value = true; if (cullingDistance > hitInfo.distance) cullingDistance = hitInfo.distance; } if (Physics.Raycast(from, _to.UpperLeft - from, out hitInfo, distance, cullingLayer)) { value = true; if (cullingDistance > hitInfo.distance) cullingDistance = hitInfo.distance; } if (Physics.Raycast(from, _to.UpperRight - from, out hitInfo, distance, cullingLayer)) { value = true; if (cullingDistance > hitInfo.distance) cullingDistance = hitInfo.distance; } Renderer renderer = null; if (hitInfo.collider) { renderer = hitInfo.transform.GetComponent(); if (renderer != null) return renderer.enabled && value; else return false; } return hitInfo.collider && value; //return hitInfo.collider && value; } }