using System.Collections; using UnityEngine; using DG.Tweening; using UnityEngine.EventSystems; [RequireComponent(typeof(Camera))] /// /// 摄像机控制脚本 /// public class CameraOrbit : CameraBase { [Header("开关")] [Tooltip("是否可控.关闭则无法控制摄像机")] public bool controlable = true; [HideInInspector] /// /// 是否初始化 /// public bool hasInit = false; [Header("是否可以旋转")] /// /// 是否可以旋转 /// public bool allowRotate = true; [Header("是否可以平移")] /// /// 是否可以平移 /// public bool allowPan = true; [Header("是否双击定位")] /// /// 是否双击定位 /// public bool allLocate = true; [Header("是否自动旋转")] /// /// 是否自动旋转 /// private bool autoRotate = false; [Header("Movement")] public CameraMovementType movementType = CameraMovementType.Normal; [Header("旋转按键")] public CameraMouseInputType rotateInputKey = CameraMouseInputType.LeftMouse; [Header("平移按键")] public CameraMouseInputType panInputKey = CameraMouseInputType.RightMouse; [Header("观察的目标")] public Vector3 target; public Transform targetTrans; [Header("摄像机与目标之间的距离")] public float Distance = 150f; [Header("限制")] [Tooltip("摄像机与目标之间的距离限制")] public Vector2 distanceClamp = new Vector2(1, 1000); public Vector2 yLimitClamp = new Vector2(-20, 90); public Vector2 xLimitClamp = new Vector2(360, 360); public Vector2 yHeightLimitClamp = new Vector2(-Mathf.Infinity, Mathf.Infinity); [Header("控制参数")] [Header("鼠标滑动灵敏度")] [Range(0.01f, 10)] public float axisSensitivity = 3; [Range(1, 100)] public float axisLerp = 10; [Header("鼠标滚轮灵敏度")] [Range(0.001f, 10)] float scrollSensitivity = 0.02f; [Range(1, 100)] float zoomLerp = 20; [Header("相机平移灵敏度")] [Range(0.01f, 10)] public float panSensitivity = 1f; [Header("相机自动旋转速度")] [Range(0.01f, 10)] public float autoRotateSpeed = 2f; [Range(1, 100)] public float panLerp = 15; [Range(1, 100)] private float lerpSpeed = 5; [Header("切换目标时黑屏时间")] [Range(0.01f, 10)] public float swichtSpeed = 1; public Ease ease = Ease.Linear; private float x, y; private float horizontal; private float vertical; private float distance = 0; private Vector3 panOffset; private Vector3 zoomVector; private Vector3 currentPosition; private Quaternion currentRotation; private float initXRotation; private bool isSwitchingTarget = false; private float fadeAlpha = 0; private bool control = true; [SerializeField] private Texture2D fadeTexture = null; public bool Controlable { get { return controlable; } set { controlable = value; } } private void Awake() { Init(); } private void LateUpdate() { target = targetTrans.position; Detection(); if (!controlable) return; if (isSwitchingTarget) return; if (autoRotate) { AutoRotate(); } if (control) { if (allowRotate) { ZoomControl(false); OrbitControl(); } else { ZoomControl(true); } if (allowPan) { PanControl(); } } if (allLocate) { //DclickLocate(); } target = LimitHeight(target); } /// /// 初始化 /// private void Init() { distance = Distance; Vector3 eulerAngles = Transform.eulerAngles; x = eulerAngles.y; y = eulerAngles.x; initXRotation = eulerAngles.y; horizontal = x; vertical = y; hasInit = true; } /// /// 旋转控制 /// private void OrbitControl() { if (controlable) { if (IsInputKeyRotate) { horizontal += (axisSensitivity) * AxisX; vertical -= (axisSensitivity) * AxisY; } } float delta = Time.deltaTime; vertical = ClampAngle(vertical, yLimitClamp.x, yLimitClamp.y); if (xLimitClamp.x < 360 && xLimitClamp.y < 360) { horizontal = ClampAngle(horizontal, (initXRotation - xLimitClamp.y), (xLimitClamp.x + initXRotation)); } x = Mathf.Lerp(x, horizontal, Time.deltaTime * axisLerp); y = Mathf.Lerp(y, vertical, Time.deltaTime * axisLerp); y = ClampAngle(y, yLimitClamp.x, yLimitClamp.y); currentRotation = Quaternion.Euler(y, x, 0f); currentPosition = (currentRotation * zoomVector) + target; switch (movementType) { case CameraMovementType.Dynamic: Transform.position = Vector3.Lerp(Transform.position, currentPosition, (lerpSpeed) * delta); Transform.rotation = Quaternion.Lerp(Transform.rotation, currentRotation, (lerpSpeed * 2) * delta); break; case CameraMovementType.Normal: Transform.position = currentPosition; Transform.rotation = currentRotation; break; case CameraMovementType.Towars: Transform.position = Vector3.MoveTowards(Transform.position, currentPosition, lerpSpeed); Transform.rotation = Quaternion.RotateTowards(Transform.rotation, currentRotation, lerpSpeed); break; } } /// /// 距离控制 /// /// private void ZoomControl(bool autoApply) { float delta = Time.deltaTime; distance = Mathf.Clamp(distance - (MouseScrollWheel * scrollSensitivity * 10), distanceClamp.x, distanceClamp.y); distance = (distance < 1) ? 1 : distance; Distance = Mathf.SmoothStep(Distance, distance, delta * zoomLerp); zoomVector = new Vector3(0, 0, -Distance); if (autoApply) { currentPosition = (currentRotation * zoomVector) + target; switch (movementType) { case CameraMovementType.Dynamic: Transform.position = Vector3.Lerp(Transform.position, currentPosition, (lerpSpeed) * delta); Transform.rotation = Quaternion.Lerp(Transform.rotation, currentRotation, (lerpSpeed * 2) * delta); break; case CameraMovementType.Normal: Transform.position = currentPosition; Transform.rotation = currentRotation; break; case CameraMovementType.Towars: Transform.position = Vector3.MoveTowards(Transform.position, currentPosition, lerpSpeed); Transform.rotation = Quaternion.RotateTowards(Transform.rotation, currentRotation, lerpSpeed); break; } } } /// /// 平移控制 /// private void PanControl() { if (IsInputKeyPan) { Vector3 offset; if (Input.GetKey(KeyCode.LeftControl)) { offset = -transform.right.normalized * AxisX + transform.forward.normalized * AxisY; } else { offset = -transform.right.normalized * AxisX - transform.up.normalized * AxisY; } #if !UNITY_EDITOR offset = new Vector3(offset.x, 0, offset.z); #endif panOffset = Vector3.Lerp(panOffset, offset, panLerp * Time.deltaTime); } else { panOffset = Vector3.Lerp(panOffset, Vector3.zero, panLerp * Time.deltaTime); } target += panOffset * panSensitivity * 10 * GetZoom(); } /// /// 控制检测,按下的一瞬间是否在UI上,是的话无法控制相机 /// private void Detection() { if (Input.GetMouseButtonUp(0) || Input.GetMouseButtonUp(1) || Input.GetMouseButtonUp(2)) { control = true; } if (Input.GetMouseButtonDown(0) || Input.GetMouseButtonDown(1) || Input.GetMouseButtonDown(2)) { if (EventSystem.current.IsPointerOverGameObject()) { control = false; } } } private bool IsInputKeyRotate { get { switch (rotateInputKey) { case CameraMouseInputType.All: return (Input.GetKey(KeyCode.Mouse0) || Input.GetKey(KeyCode.Mouse1) || Input.GetKey(KeyCode.Mouse2) || Input.GetMouseButton(0)); case CameraMouseInputType.LeftAndRight: return (Input.GetKey(KeyCode.Mouse0) || Input.GetKey(KeyCode.Mouse1)); case CameraMouseInputType.LeftMouse: return (Input.GetKey(KeyCode.Mouse0)); case CameraMouseInputType.RightMouse: return (Input.GetKey(KeyCode.Mouse1)); case CameraMouseInputType.MouseScroll: return (Input.GetKey(KeyCode.Mouse2)); case CameraMouseInputType.MobileTouch: return (Input.GetMouseButton(0) || Input.GetMouseButton(1)); default: return (Input.GetKey(KeyCode.Mouse0)); } } } private bool IsInputKeyPan { get { switch (panInputKey) { case CameraMouseInputType.All: return (Input.GetKey(KeyCode.Mouse0) || Input.GetKey(KeyCode.Mouse1) || Input.GetKey(KeyCode.Mouse2) || Input.GetMouseButton(0)); case CameraMouseInputType.LeftAndRight: return (Input.GetKey(KeyCode.Mouse0) || Input.GetKey(KeyCode.Mouse1)); case CameraMouseInputType.LeftMouse: return (Input.GetKey(KeyCode.Mouse0)); case CameraMouseInputType.RightMouse: return (Input.GetKey(KeyCode.Mouse1)); case CameraMouseInputType.MouseScroll: return (Input.GetKey(KeyCode.Mouse2)); case CameraMouseInputType.MobileTouch: return (Input.GetMouseButton(0) || Input.GetMouseButton(1)); default: return (Input.GetKey(KeyCode.Mouse0)); } } } public void SetTarget(Vector3 newTarget) { newTarget = LimitHeight(newTarget); target = newTarget; } public void SetTargetLerp(Vector3 newTarget) { newTarget = LimitHeight(newTarget); DOTween.To(() => target, x => target = x, newTarget, 0.2f).SetEase(ease); } public void SetTarget(Transform newTarget) { StopCoroutine("TranslateTarget"); StartCoroutine("TranslateTarget", newTarget); } private IEnumerator TranslateTarget(Transform newTarget) { isSwitchingTarget = true; while (fadeAlpha < 1) { transform.position = Vector3.Lerp(transform.position, transform.position + new Vector3(0, 2, -2), Time.deltaTime); fadeAlpha += Time.smoothDeltaTime * swichtSpeed; yield return null; } target = newTarget.position; isSwitchingTarget = false; while (fadeAlpha > 0) { fadeAlpha -= Time.smoothDeltaTime * swichtSpeed; yield return null; } } /// /// 双击定位 /// private void DclickLocate() { if (EventSystem.current.IsPointerOverGameObject()) return; //if (Inputs.Key.DoubleClick(KeyCode.Mouse0, 0.2f)) //{ // Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); // if (Physics.Raycast(ray, out RaycastHit hit)) // { // SetTargetLerp(hit.point); // } //} } /// /// 限制目标点高度 /// private Vector3 LimitHeight(Vector3 _target) { return new Vector3(_target.x, Mathf.Clamp(_target.y, yHeightLimitClamp.x, yHeightLimitClamp.y), _target.z); } /// /// 设置九个方向朝向 /// /// public void SetViewPoint(CameraOrientation Orient = CameraOrientation.Forward) { switch (Orient) { case CameraOrientation.Forward: vertical = 0; horizontal = 0; break; case CameraOrientation.Behind: vertical = 0; horizontal = 180; break; case CameraOrientation.Left: vertical = 0; horizontal = 90; break; case CameraOrientation.Right: vertical = 0; horizontal = -90; break; case CameraOrientation.LeftForward: vertical = 0; horizontal = -45; break; case CameraOrientation.RightForward: vertical = 0; horizontal = -135; break; case CameraOrientation.LeftBehind: vertical = 0; horizontal = 45; break; case CameraOrientation.RightBehind: vertical = 0; horizontal = 135; break; case CameraOrientation.Top: vertical = 90; horizontal = 0; break; } } /// /// 镜头拉近拉远 /// /// public void Zoom(float value, float timer = 0.5f) { float targetDis = distance + value; DOTween.To(() => distance, x => distance = x, targetDis, timer).OnUpdate(() => { distance = Mathf.Clamp(distance, distanceClamp.x, distanceClamp.y); }); } /// /// /// /// public void SetStaticZoom(float value) { distance += value; } /// /// 设置位置 /// /// 注视点位置 /// 与注视点距离 /// 竖直角度 /// 水平角度 public void SetPosition(Vector3 newTarget, float dis, float angleX = 45, float angleY = 0, float timer = 0.2f) { newTarget = LimitHeight(newTarget); target = newTarget; distance += dis; DOTween.To(() => distance, x => distance = x, dis, timer).OnUpdate(() => { distance = Mathf.Clamp(distance, distanceClamp.x, distanceClamp.y); }); horizontal = angleY; vertical = angleX; } /// /// 定位摄像机到某个位置 /// /// public void ResetPosition(Vector3 position, float timer = 0.2f) { float targetDis = Vector3.Distance(target, position); DOTween.To(() => distance, x => distance = x, targetDis, timer).OnUpdate(() => { distance = Mathf.Clamp(distance, distanceClamp.x, distanceClamp.y); }); Vector3 tempPos = position - target; double square = tempPos.x * tempPos.x + tempPos.z * tempPos.z; double sqrt = System.Math.Sqrt(square); float ratio = (float)sqrt / targetDis; float deg = Mathf.Acos(ratio); float angle = deg * Mathf.Rad2Deg; vertical = angle * Mathf.Sign(tempPos.y); square = targetDis * targetDis - tempPos.y * tempPos.y; sqrt = System.Math.Sqrt(square); if (sqrt == 0) sqrt = 0.001f; ratio = tempPos.x / (float)sqrt; deg = Mathf.Acos(ratio); angle = 90 - deg * Mathf.Rad2Deg; angle += 180; if (tempPos.z < 0) { angle = 180 - angle; } horizontal = angle; x %= 360; } /// /// 设置与目标之间的距离 /// /// public void SetDistance(float value) { if (value <= 0) { value = 0; } distance = value; } /// /// 设置距离最大最小值 /// /// /// public void SetDistanceClamp(float min = 1, float max = 10000) { distanceClamp = new Vector2(min, max); } /// /// 相机自动旋转 /// private void AutoRotate() { horizontal -= autoRotateSpeed * Time.deltaTime; } /// /// 设置是否自动旋转 /// /// public void SetAutoRotate(bool rotate, float speed = 2) { autoRotate = rotate; autoRotateSpeed = speed; } /// /// 获取整体缩放 /// /// private float GetZoom() { return Distance / 100; } /// /// /// public float Horizontal { get { return horizontal; } set { horizontal += value; } } /// /// /// public float Vertical { get { return vertical; } set { vertical += value; } } private void OnGUI() { if (isSwitchingTarget) { GUI.color = new Color(1, 1, 1, fadeAlpha); if (fadeTexture != null) GUI.DrawTexture(new Rect(0, 0, Screen.width, Screen.height), fadeTexture, ScaleMode.StretchToFill); return; } } private void OnDestroy() { hasInit = false; } void OnDrawGizmosSelected() { Gizmos.color = new Color32(0, 221, 221, 255); if (target != null) { Gizmos.DrawLine(transform.position, target); Gizmos.matrix = Matrix4x4.TRS(target, transform.rotation, new Vector3(1f, 0, 1f)); Gizmos.DrawWireSphere(target, Distance); Gizmos.matrix = Matrix4x4.identity; } Gizmos.DrawCube(transform.position, new Vector3(1, 0.2f, 0.2f)); Gizmos.DrawCube(transform.position, Vector3.one / 2); } /// /// 限制角度 /// /// /// /// /// public static float ClampAngle(float angle, float min, float max) { if (angle < -360f) { angle += 360f; } if (angle > 360f) { angle -= 360f; } return Mathf.Clamp(angle, min, max); } }