using System.Collections; using System.Collections.Generic; using UnityEngine; public class CameraControl : MonoBehaviour { public Transform Character; // 玩家的引用。 public Vector3 pivotOffset = new Vector3(0.0f, 1.7f, 0.0f); // 相机相对于玩家的偏移量,用于调整相机的中心点。 public Vector3 camOffset = new Vector3(0.4f, 0.0f, -2.0f); // 相机相对于玩家位置的偏移量,用于定位相机。 public float smooth = 10f; // 相机响应的平滑速度。 public float horizontalAimingSpeed = 6f; // 水平转动速度。 public float verticalAimingSpeed = 6f; // 垂直转动速度。 public float maxVerticalAngle = 30f; // 相机最大垂直夹角。 public float minVerticalAngle = -60f; // 相机最小垂直夹角。 private float angleH = 0; // 用于存储相机水平角度的变量,基于鼠标移动。 private float angleV = 0; // 用于存储相机垂直角度的变量,基于鼠标移动。 private Transform cam; // 相机的变换组件。 private Vector3 smoothPivotOffset; // 当前的中心点偏移量,用于插值计算。 private Vector3 smoothCamOffset; // 当前的相机偏移量,用于插值计算。 private Vector3 targetPivotOffset; // 中心点偏移量目标,用于插值计算。 private Vector3 targetCamOffset; // 相机偏移量目标,用于插值计算。 private float defaultFOV; // 默认相机视场角(FOV)。 private float targetFOV; // 目标相机视场角。 private float targetMaxVerticalAngle; // 自定义相机的最大垂直夹角。 private bool isCustomOffset; // 布尔值,判断是否正在使用自定义相机偏移量。 private float deltaH = 0; // 用于锁定相机方向时的水平旋转增量。 private Vector3 firstDirection; // 用于第一次锁定相机方向时的方向。 private Vector3 directionToLock; // 当前锁定相机的方向。 private float recoilAngle = 0f; // 垂直抖动相机时的角度,用于模拟后坐力。 private Vector3 forwardHorizontalRef; // 水平平面上的前向参考方向,用于限制相机旋转。 private float leftRelHorizontalAngle, rightRelHorizontalAngle; // 用于限制相机水平旋转的左右相对角度。 // 获取相机的水平角度。 public float GetH => angleH; void Awake() { // 引用相机的变换组件。 cam = transform; // 设置相机的默认位置。 cam.position = Character.position + Quaternion.identity * pivotOffset + Quaternion.identity * camOffset; cam.rotation = Quaternion.identity; // 设置参考值和默认参数。 smoothPivotOffset = pivotOffset; smoothCamOffset = camOffset; defaultFOV = cam.GetComponent().fieldOfView; angleH = Character.eulerAngles.y; ResetTargetOffsets(); ResetFOV(); ResetMaxVerticalAngle(); // 检查是否存在垂直偏移。 if (camOffset.y > 0) Debug.LogWarning("垂直相机偏移量 (Y) 在碰撞时会被忽略!\n" +"建议将所有垂直偏移量设置在 Pivot Offset 中。"); } void Update() { // 获取鼠标移动来控制相机的旋转。 // 鼠标控制: angleH += Mathf.Clamp(Input.GetAxis("Mouse X"), -1, 1) * horizontalAimingSpeed; angleV += Mathf.Clamp(Input.GetAxis("Mouse Y"), -1, 1) * verticalAimingSpeed; // 设置垂直移动的限制。 angleV = Mathf.Clamp(angleV, minVerticalAngle, targetMaxVerticalAngle); // 设置垂直相机抖动(模拟后坐力)。 angleV = Mathf.LerpAngle(angleV, angleV + recoilAngle, 10f * Time.deltaTime); // 处理相机方向锁定。 if (firstDirection != Vector3.zero) { angleH -= deltaH; UpdateLockAngle(); angleH += deltaH; } // 处理相机的水平旋转限制(如果设置了)。 if (forwardHorizontalRef != default(Vector3)) { ClampHorizontal(); } // 设置相机的方向。 Quaternion camYRotation = Quaternion.Euler(0, angleH, 0); Quaternion aimRotation = Quaternion.Euler(-angleV, angleH, 0); cam.rotation = aimRotation; // 设置相机的视场角。 cam.GetComponent().fieldOfView = Mathf.Lerp(cam.GetComponent().fieldOfView, targetFOV, Time.deltaTime); // 基于当前相机位置,检测与环境的碰撞。 Vector3 baseTempPosition = Character.position + camYRotation * targetPivotOffset; Vector3 noCollisionOffset = targetCamOffset; while (noCollisionOffset.magnitude >= 0.2f) { if (DoubleViewingPosCheck(baseTempPosition + aimRotation * noCollisionOffset)) break; noCollisionOffset -= noCollisionOffset.normalized * 0.2f; } if (noCollisionOffset.magnitude < 0.2f) noCollisionOffset = Vector3.zero; // 如果有自定义偏移,并且相机碰撞,将进入第一人称模式。 bool customOffsetCollision = isCustomOffset && noCollisionOffset.sqrMagnitude < targetCamOffset.sqrMagnitude; // 重新定位相机。 smoothPivotOffset = Vector3.Lerp(smoothPivotOffset, customOffsetCollision ? pivotOffset : targetPivotOffset, smooth * Time.deltaTime); smoothCamOffset = Vector3.Lerp(smoothCamOffset, customOffsetCollision ? Vector3.zero : noCollisionOffset, smooth * Time.deltaTime); cam.position = Character.position + camYRotation * smoothPivotOffset + aimRotation * smoothCamOffset; // 平滑相机的垂直抖动(后坐力)。 if (recoilAngle > 0) recoilAngle -= 5 * Time.deltaTime; else if (recoilAngle < 0) recoilAngle += 5 * Time.deltaTime; } // 设置/取消水平旋转的限制。 public void ToggleClampHorizontal(float LeftAngle = 0, float RightAngle = 0, Vector3 fwd = default(Vector3)) { forwardHorizontalRef = fwd; leftRelHorizontalAngle = LeftAngle; rightRelHorizontalAngle = RightAngle; } // 限制相机的水平旋转。 private void ClampHorizontal() { // 获取相机当前朝向和参考方向之间的角度。 Vector3 cam2dFwd = this.transform.forward; cam2dFwd.y = 0; float angleBetween = Vector3.Angle(cam2dFwd, forwardHorizontalRef); float sign = Mathf.Sign(Vector3.Cross(cam2dFwd, forwardHorizontalRef).y); angleBetween = angleBetween * sign; // 获取当前输入移动,以在达到限制角度时进行补偿。 float acc = Mathf.Clamp(Input.GetAxis("Mouse X"), -1, 1) * horizontalAimingSpeed; acc += Mathf.Clamp(Input.GetAxis("Analog X"), -1, 1) * 60 * horizontalAimingSpeed * Time.deltaTime; // 限制左侧角度。 if (sign < 0 && angleBetween < leftRelHorizontalAngle) { if (acc > 0) angleH -= acc; } // 限制右侧角度。 else if (angleBetween > rightRelHorizontalAngle) { if (acc < 0) angleH -= acc; } } // 处理锁定特定方向时的相机朝向。 private void UpdateLockAngle() { directionToLock.y = 0f; float centerLockAngle = Vector3.Angle(firstDirection, directionToLock); Vector3 cross = Vector3.Cross(firstDirection, directionToLock); if (cross.y < 0) centerLockAngle = -centerLockAngle; deltaH = centerLockAngle; } // 锁定相机朝向跟随特定方向。通常用于短暂的动作。 public void LockOnDirection(Vector3 direction) { if (firstDirection == Vector3.zero) { firstDirection = direction; firstDirection.y = 0f; } directionToLock = Vector3.Lerp(directionToLock, direction, 0.15f * smooth * Time.deltaTime); } // 解锁相机方向。 public void UnlockOnDirection() { deltaH = 0; firstDirection = directionToLock = Vector3.zero; } // 设置自定义相机偏移量。 public void SetTargetOffsets(Vector3 newPivotOffset, Vector3 newCamOffset) { targetPivotOffset = newPivotOffset; targetCamOffset = newCamOffset; isCustomOffset = true; } // 重置相机偏移量为默认值。 public void ResetTargetOffsets() { targetPivotOffset = pivotOffset; targetCamOffset = camOffset; isCustomOffset = false; } // 重置相机垂直偏移量。 public void ResetYCamOffset() { targetCamOffset.y = camOffset.y; } // 设置相机垂直偏移量。 public void SetYCamOffset(float y) { targetCamOffset.y = y; } // 设置相机水平偏移量。 public void SetXCamOffset(float x) { targetCamOffset.x = x; } // 设置自定义视场角(FOV)。 public void SetFOV(float customFOV) { this.targetFOV = customFOV; } // 重置视场角为默认值。 public void ResetFOV() { this.targetFOV = defaultFOV; } // 设置最大垂直相机旋转角度。 public void SetMaxVerticalAngle(float angle) { this.targetMaxVerticalAngle = angle; } // 重置最大垂直相机旋转角度为默认值。 public void ResetMaxVerticalAngle() { this.targetMaxVerticalAngle = maxVerticalAngle; } // 双重检测碰撞:某些凹面物体无法从外部检测到,因此从两个方向进行检测。 bool DoubleViewingPosCheck(Vector3 checkPos) { return ViewingPosCheck(checkPos) && ReverseViewingPosCheck(checkPos); } // 检查相机到玩家的碰撞。 bool ViewingPosCheck(Vector3 checkPos) { // 定义目标和方向。 Vector3 target = Character.position + pivotOffset; Vector3 direction = target - checkPos; // 如果从检测位置到玩家的射线检测到障碍物... if (Physics.SphereCast(checkPos, 0.2f, direction, out RaycastHit hit, direction.magnitude)) { // 如果不是玩家... if (hit.transform != Character && !hit.transform.GetComponent().isTrigger) { // 这个位置不合适。 return false; } } // 如果没有检测到任何障碍物,或者检测到的是玩家,这个位置合适。 return true; } // 检查从玩家到相机的碰撞。 bool ReverseViewingPosCheck(Vector3 checkPos) { // 定义起点和方向。 Vector3 origin = Character.position + pivotOffset; Vector3 direction = checkPos - origin; if (Physics.SphereCast(origin, 0.2f, direction, out RaycastHit hit, direction.magnitude)) { if (hit.transform != Character && hit.transform != transform && !hit.transform.GetComponent().isTrigger) { return false; } } return true; } // 获取当前相机的中心点偏移量的大小。 public float GetCurrentPivotMagnitude(Vector3 finalPivotOffset) { return Mathf.Abs((finalPivotOffset - smoothPivotOffset).magnitude); } }