using System; using System.Collections; using System.Collections.Generic; using Unity.Mathematics; using UnityEngine; namespace DS { public class CameraHandler : MonoBehaviour { public Transform player; // 跟踪目标(如玩家角色) // ========== 公开引用 ========== [Header("Camera Transforms")] public Transform cameraTransform; // 摄像机 Transform(子物体) public Transform cameraPivotTransform; // 摄像机枢轴 Transform(用于俯仰旋转) // ========== 私有引用与状态 ========== private Transform _myTransform; // 当前对象的 Transform 缓存 private Vector3 _cameraTransformPosition; // 用于 Lerp 的摄像机局部位置 private Vector3 cameraFollowVelocity = Vector3.zero; // SmoothDamp 速度参考值 private bool _cursorLocked = false; // 鼠标是否锁定 // ========== 单例 ========== public static CameraHandler singleton; // 全局访问实例 // ========== 速度参数(可调) ========== [Header("Movement Speeds")] public float lookSpeed = 0.1f; // 水平旋转速度 public float followSpeed = 0.1f; // 跟随目标的速度 public float pivotSpeed = 0.03f; // 俯仰旋转速度 // ========== 内部状态 ========== private float _targetPosition; // 摄像机与枢轴的目标距离 private float _defaultPosition; // 摄像机的默认 Z 轴局部位置(距离) private float _lookAngle; // 水平旋转角度(Y 轴) private float _pivotAngle; // 俯仰旋转角度(X 轴) [Header("Pivot Clamps")] public float minimumPivot = -35; // 俯仰最小角度 public float maximumPivot = 35; // 俯仰最大角度 // ========== 碰撞检测参数 ========== [Header("Collision Settings")] public float cameraSphereRadius = 0.2f; // 球体投射的半径 public LayerMask ignoreLayers; // 忽略的碰撞层(玩家、UI 等) public float cameraCollisionOffSet = 0.2f; // 碰撞后摄像机偏移量 public float minimumCollisionOffSet = 0.2f; // 最小偏移量,防止穿透 // ==================================================================== // Awake:初始化引用与默认值 // ==================================================================== private void Awake() { singleton = this; // 设置单例 _myTransform = transform; // 缓存自身 Transform _defaultPosition = cameraTransform.localPosition.z; // 记录默认 Z 距离 // 自动绑定 PlayerLocomotion if (player == null) { player = FindObjectOfType().transform; } } // ==================================================================== // Update:每帧检测鼠标锁定状态 // ==================================================================== private void Update() { UpdateCursorLock(); } // ==================================================================== // UpdateCursorLock:管理鼠标锁定与可见性 // ==================================================================== private void UpdateCursorLock() { // 左键点击 -> 锁定鼠标(游戏控制模式) if (Input.GetMouseButtonDown(0)) { Cursor.lockState = CursorLockMode.Locked; // 锁定鼠标到窗口中心 Cursor.visible = false; // 隐藏鼠标 _cursorLocked = true; } // 按下 Escape -> 解锁鼠标(UI / 菜单模式) if (Input.GetKeyDown(KeyCode.Escape)) { Cursor.lockState = CursorLockMode.None; // 释放鼠标 Cursor.visible = true; // 显示鼠标 _cursorLocked = false; } } // ==================================================================== // FollowTarget:平滑跟随目标,并处理摄像机碰撞 // ==================================================================== public void FollowTarget(float delta) { // 使用 SmoothDamp 平滑移动到目标位置 Vector3 targetPosition = Vector3.SmoothDamp( _myTransform.position, player.position, ref cameraFollowVelocity, delta / followSpeed); _myTransform.position = targetPosition; // 处理摄像机与场景物体的碰撞 HandleCameraCollisions(delta); } // ==================================================================== // HandleCameraRotation:处理鼠标输入的摄像机旋转 // ==================================================================== public void HandleCameraRotation(float delta, float mouseInputX, float mouseInputY) { // 鼠标未锁定时不处理旋转 if (!_cursorLocked) return; // 累加水平旋转角度(左右) _lookAngle += (mouseInputX * lookSpeed) / delta; // 累加俯仰旋转角度(上下,取反使鼠标向上移动时视角向上) _pivotAngle -= (mouseInputY * pivotSpeed) / delta; // 限制俯仰角度范围,防止视角翻转 _pivotAngle = Mathf.Clamp(_pivotAngle, minimumPivot, maximumPivot); // 应用水平旋转到当前对象(Y 轴旋转) Vector3 rotation = Vector3.zero; rotation.y = _lookAngle; Quaternion targetRotation = Quaternion.Euler(rotation); _myTransform.rotation = targetRotation; // 应用俯仰旋转到枢轴对象(X 轴旋转,局部坐标) rotation = Vector3.zero; rotation.x = _pivotAngle; targetRotation = Quaternion.Euler(rotation); cameraPivotTransform.localRotation = targetRotation; } // ==================================================================== // HandleCameraCollisions:检测并避免摄像机与场景物体穿插 // ==================================================================== public void HandleCameraCollisions(float delta) { // 目标距离默认设为默认距离 _targetPosition = _defaultPosition; RaycastHit hit; // 计算从枢轴指向摄像机的方向向量 Vector3 direction = cameraTransform.position - cameraPivotTransform.position; direction.Normalize(); // 从枢轴位置沿 direction 方向进行 SphereCast 检测 // 检测距离为 _targetPosition 的绝对值 if (Physics.SphereCast( cameraPivotTransform.position, cameraSphereRadius, direction, out hit, Mathf.Abs(_targetPosition), ignoreLayers)) { // 计算枢轴到碰撞点的实际距离 float dis = Vector3.Distance(cameraPivotTransform.position, hit.point); // 摄像机应位于碰撞点前方 cameraCollisionOffSet 处 _targetPosition = -(dis - cameraCollisionOffSet); } // 如果计算出的目标距离绝对值太小,强制使用最小值防止视觉异常 if (Mathf.Abs(_targetPosition) < minimumCollisionOffSet) { _targetPosition = -minimumCollisionOffSet; } // 使用 Lerp 平滑调整摄像机的局部 Z 轴位置 _cameraTransformPosition.z = Mathf.Lerp( cameraTransform.localPosition.z, _targetPosition, delta / 0.2f); // 应用位置 cameraTransform.localPosition = _cameraTransformPosition; } } }