using System; using System.Collections; using System.Collections.Generic; using Unity.VisualScripting; using UnityEngine; using Random = UnityEngine.Random; public class CameraFollow : MonoBehaviour { public UpdateFrameType UpdateType; public Transform target; public Vector2 offset; public float yMinLimit, yMaxLimit; public float xSpeed, ySpeed; public float lookSensitivity; public float distance = 3; public float x, y; public bool isAiming= false; public float aimingFovIncrease = -20; [Header("Moving effects")] public float movingShakeIntensity; float defaultFov; public float movingFovIncrease = 10; public float movingZoomDist=1; public float movingEffectsSpeed = 0.4f; public float movingEffectsThershold; public float currentTargetSpeed; public static CameraFollow instance; public bool isViewObstructed; public float obstacleAvoidanceDistMult = 2f; public float pitchZoomThreshold = -25; public float pitchZoomDist = 1.5f; private void Awake() { instance = this; isPaused = false; defaultFov = Camera.main.fieldOfView; } void Start(){ SettingsManager.instance.OnSettingsChanged.AddListener(OnSettingsChanged); OnSettingsChanged(); } void OnSettingsChanged(){ lookSensitivity = PlayerPrefs.GetFloat(SettingsManager.LOOKSENS_PREF_KEY); movingFovIncrease = SettingsManager.instance.cameraFovSlider.value; movingZoomDist = SettingsManager.instance.cameraZoomSlider.value; } // Update is called once per frame void Update() { if(UpdateType == UpdateFrameType.Normal) { UpdateFrame(); } } private void LateUpdate() { x += Input.GetAxis("Mouse X") * xSpeed * 0.02f * lookSensitivity; y -= Input.GetAxis("Mouse Y") * ySpeed * 0.02f * lookSensitivity; if(UpdateType == UpdateFrameType.Late) { UpdateFrame(); } } private void FixedUpdate() { if(UpdateType == UpdateFrameType.Fixed) { UpdateFrame(); } } private bool isPaused = false; public static void Pause(){ instance.isPaused = true; } public static void Resume(){ instance.isPaused = false; } Vector3 targetLastPos; float movingEffectOffsetY = 0; float obstacleOffsetY=0; float pitchZoom=0; void UpdateFrame() { if(isPaused){return;} if(target == null) { return; } // currentTargetSpeed = (target.position - targetLastPos).sqrMagnitude; // targetLastPos = target.position; if (distance < 2) distance = 2; // distance -= Input.GetAxis("Mouse ScrollWheel") * 2; var pos = Input.mousePosition; var dpiScale = 1f; if (Screen.dpi < 1) dpiScale = 1; if (Screen.dpi < 200) dpiScale = 1; else dpiScale = Screen.dpi / 200f; if (pos.x < 380 * dpiScale && Screen.height - pos.y < 250 * dpiScale) return; // comment out these two lines if you don't want to hide mouse curser or you have a UI button Cursor.visible = UIManager.isPaused; Cursor.lockState = UIManager.isPaused ? CursorLockMode.None:CursorLockMode.Locked; float fovDelta = 0; if(currentTargetSpeed > movingEffectsThershold){ fovDelta = movingFovIncrease; movingEffectOffsetY = Mathf.Lerp(movingEffectOffsetY, movingZoomDist, movingEffectsSpeed); }else{ fovDelta =0; movingEffectOffsetY = Mathf.Lerp(movingEffectOffsetY, 0, movingEffectsSpeed); } float m_obstacleZoomDist =0; isViewObstructed=false; RaycastHit hit = new RaycastHit(); if(Physics.Linecast(target.position + (transform.forward * -obstacleAvoidanceDistMult * 2), target.position,out hit)){ if(hit.collider.tag == "Player"){}else{ // m_obstacleZoomDist = Vector3.Distance(transform.position, hit.point) * obstacleAvoidanceDistMult; m_obstacleZoomDist=obstacleAvoidanceDistMult; isViewObstructed=true; } } obstacleOffsetY = Mathf.Lerp(obstacleOffsetY, m_obstacleZoomDist, movingEffectsSpeed*5f); if(isAiming){ fovDelta = aimingFovIncrease; } Camera.main.fieldOfView = Mathf.Lerp(Camera.main.fieldOfView, defaultFov + fovDelta, movingEffectsSpeed *(isAiming ? 10f:1)); y = ClampAngle(y, yMinLimit, yMaxLimit); var rotation = Quaternion.Euler(y, x, 0); var position = rotation * new Vector3(0.0f, 0.0f, -distance) + target.transform.position; pitchZoom = Mathf.Lerp(pitchZoom,y < pitchZoomThreshold ? pitchZoomDist : 0, movingEffectsSpeed); position += transform.right * offset.x; position += transform.forward *( offset.y + movingEffectOffsetY + obstacleOffsetY + pitchZoom); transform.rotation = rotation; transform.position = position; if (Mathf.Abs(prevDistance - distance) > 0.001f) { prevDistance = distance; var rot = Quaternion.Euler(y, x, 0); var po = rot * new Vector3(0.0f, 0.0f, -distance) + target.transform.position; transform.rotation = rot; transform.position = po; } if(shakeIndex>0){ shakeIndex--; float progression = (float)shakeIndex / (float)SHAKE_FRAMES; transform.position += new Vector3(Random.Range(-1f,1f),Random.Range(-1f,1f),Random.Range(-1f,1f)) * shakeIntensity * progression; } } public int shakeIndex =0; private float shakeIntensity; const int SHAKE_FRAMES = 25; public static void Shake(float intensity){ instance.shakeIndex = SHAKE_FRAMES; instance.shakeIntensity = Mathf.Clamp(intensity,0,0.5f); } float prevDistance; static float ClampAngle(float angle, float min, float max) { if (angle < -360) angle += 360; if (angle > 360) angle -= 360; return Mathf.Clamp(angle, min, max); } } public enum UpdateFrameType { Normal, Fixed, Late }