using System.Collections; using System.Collections.Generic; using UnityEngine; namespace SK.Framework { /// /// 观察相机组件 /// [DisallowMultipleComponent] public class RoamCameraController : MonoBehaviour { #region Inspector [Header("Target")] [Tooltip("观察物体不可以为空")] public Transform target; public Vector3 targetOffset = Vector3.zero; [Header("Distance")] public float distance = 5f; public float minDistance = 2f; public float maxDistance = 15f; public float zoomSpeed = 2f; [Header("Rotation")] public float xSpeed = 120f; public float ySpeed = 80f; public float minYAngle = -30f; public float maxYAngle = 80f; [Header("Damping")] public float rotationDamping = 8f; public float zoomDamping = 8f; public float positionDamping = 10f; [Header("Control")] public bool enableControl = true; [Header("鼠标的键值(0左键,1右键)")] public int rotateMouseButton = 0; // 0 = Left Mouse Button #endregion #region Runtime State private float currentX; private float currentY; private float targetDistance; private float smoothX; private float smoothY; private float smoothDistance; private bool hasReportedNullTargetError = false; #endregion #region Unity Lifecycle void Start() { Vector3 angles = transform.eulerAngles; currentX = angles.y; currentY = angles.x; targetDistance = Mathf.Clamp(distance, minDistance, maxDistance); smoothDistance = targetDistance; } void LateUpdate() { if (!enableControl) return; if (target == null) { ReportNullTargetErrorOnce(); return; //停止 } HandleInput(); UpdateCamera(); } #if UNITY_EDITOR void OnValidate() { if (target == null) { Debug.LogError( "[RoamCameraController] Target 没有被设置!", this ); } if (minDistance > maxDistance) { minDistance = maxDistance; } } #endif #endregion #region Core Logic void HandleInput() { if (Input.GetMouseButton(rotateMouseButton)) { currentX += Input.GetAxis("Mouse X") * xSpeed * 0.02f; currentY -= Input.GetAxis("Mouse Y") * ySpeed * 0.02f; } currentY = ClampAngle(currentY, minYAngle, maxYAngle); float scroll = Input.GetAxis("Mouse ScrollWheel"); if (Mathf.Abs(scroll) > 0.001f) { targetDistance -= scroll * zoomSpeed; targetDistance = Mathf.Clamp(targetDistance, minDistance, maxDistance); } } void UpdateCamera() { smoothX = Mathf.LerpAngle(smoothX, currentX, Time.deltaTime * rotationDamping); smoothY = Mathf.LerpAngle(smoothY, currentY, Time.deltaTime * rotationDamping); smoothDistance = Mathf.Lerp( smoothDistance, targetDistance, Time.deltaTime * zoomDamping ); Quaternion rotation = Quaternion.Euler(smoothY, smoothX, 0f); Vector3 dir = rotation * Vector3.back * smoothDistance; Vector3 targetPos = target.position + targetOffset; Vector3 desiredPos = targetPos + dir; transform.position = Vector3.Lerp( transform.position, desiredPos, Time.deltaTime * positionDamping ); transform.rotation = rotation; } #endregion #region Public API /// /// Switch observed target. /// public void SetTarget(Transform newTarget, bool instant = true) { target = newTarget; if (target == null) { ReportNullTargetErrorOnce(); return; } // 恢复正常状态 hasReportedNullTargetError = false; Vector3 targetPos = target.position + targetOffset; Quaternion rot = Quaternion.Euler(currentY, currentX, 0f); Vector3 dir = rot * Vector3.back * targetDistance; if (instant) { transform.position = targetPos + dir; smoothX = currentX; smoothY = currentY; smoothDistance = targetDistance; } } #endregion #region Error Handling void ReportNullTargetErrorOnce() { if (hasReportedNullTargetError) return; hasReportedNullTargetError = true; Debug.LogError( $"[RoamCameraController] Target为空,相机停止运行 " + $"GameObject: {gameObject.name}", this ); } #endregion #region Utility static float ClampAngle(float angle, float min, float max) { angle = Mathf.Repeat(angle + 180f, 360f) - 180f; return Mathf.Clamp(angle, min, max); } #endregion } }