using System; using DG.Tweening; using Framework; using Gameplay.Performance; using UnityEngine; using Sirenix.OdinInspector; using Gameplay.Guide; using UnityEngine.EventSystems; [ExecuteAlways] public class CameraController : MonoBehaviour { private const float CameraStartY = 25; private const float CameraStartSize = 12; private const float CameraStartFOV = 30; private const float NearClipPlane = 0.03f; private const float FarClipPlane = 150; private readonly Vector3 cameraStartRotationO = new Vector3(37.5f, -10f, 0f); private readonly Vector3 cameraStartRotationP = new Vector3(45, -10, 0); //之前的缩放是6次滚轮从最小放到最大,故每一次缩放的值为1/6f private const float ZoomStep = 1 / 12f; public float leftMargin = 1.5f; public float rightMargin = 1.5f; public float topMargin = 0.9f; public float bottomMargin = 0.9f; public float moveSpeedX = 0.19f; public float moveSpeedY = 0.3f; public float unitPerPixel = 0.01f; [ShowIf("isFightCamera")] public float CameraMinSize = 6; [ShowIf("isFightCamera")] public float CameraMaxSize = 12; [HideIf("isFightCamera")] public BoxCollider boxBounds; [HideIf("isFightCamera")] public float CameraMinFOV = 15; [HideIf("isFightCamera")] public float CameraMaxFOV = 30; [LabelText("是否战斗相机")] public bool isFightCamera = true; private Camera mainCamera; private bool isIgnoreZoomLimit; public Bounds CameraBounds { get; set; } private bool isOrthographic => mainCamera.orthographic; //Camera拖动控制 public static bool FingerMoveEnable = true; //size,将透视和正交相机的fov和orthographic size整合为统一数值 private float Size { get { if (isOrthographic) return mainCamera.orthographicSize; else { var fov = mainCamera.fieldOfView; //这里乘(CameraMaxSize - CameraMinSize)/(CameraMaxFOV - CameraMinFOV)是将fov的区间变得和orthographic size区间一致 return fov * (CameraMaxSize - CameraMinSize) / (CameraMaxFOV - CameraMinFOV); } } } //缩放百分比,0表示放到最小,1表示放到最大 private float ZoomScale { get { if (isOrthographic) return (CameraMaxSize - mainCamera.orthographicSize) / (CameraMaxSize - CameraMinSize); else return (CameraMaxFOV - mainCamera.fieldOfView) / (CameraMaxFOV - CameraMinFOV); } set { var t = Mathf.Clamp(value, 0, 1); if (Math.Abs(ZoomScale - t) < float.Epsilon) return; if (isOrthographic) mainCamera.orthographicSize = Mathf.Lerp(CameraMaxSize, CameraMinSize, t); else mainCamera.fieldOfView = Mathf.Lerp(CameraMaxFOV, CameraMinFOV, t); } } private void Awake() { mainCamera = GetComponent(); if (isFightCamera) { var useOrthographic = PerformanceManager.instance != null && PerformanceManager.instance.data.fightUseOrthographic; var rotEuler = useOrthographic ? cameraStartRotationO : cameraStartRotationP; if (useOrthographic) { // TODO: 这里的值是固定的,需要根据实际情况调整 //bottomMargin = 0.9f; mainCamera.orthographic = true; mainCamera.orthographicSize = CameraStartSize; // 修改固定旋转值 //mainCamera.transform.SetPositionAndRotation(new Vector3(0, CameraStartY, 0), Quaternion.Euler(rotEuler)); //mainCamera.transform.position = new Vector3(0, CameraStartY, 0); } else { // TODO: 这里的值是固定的,需要根据实际情况调整 //bottomMargin = 0.7f; mainCamera.orthographic = false; mainCamera.fieldOfView = 30; // 修改固定旋转值 // mainCamera.transform.SetPositionAndRotation(new Vector3(0, 27, 0), Quaternion.Euler(rotEuler)); //mainCamera.transform.position = new Vector3(0, 27, 0); } } mainCamera.nearClipPlane = NearClipPlane; mainCamera.farClipPlane = FarClipPlane; CameraBounds = boxBounds != null ? boxBounds.bounds : CameraBounds; AdjustSelf(); } private void OnEnable() { if (Application.isPlaying) { //InputManager.Instance.OnFingerMove += OnFingerMove2; EventManager.Instance.Register(EventManager.EventName.Level_OnDrag, _OnDragMove); InputManager.Instance.OnFingersMove += OnFingersMove; } } private void OnDisable() { if (Application.isPlaying) { if (InputManager.Instance == null) return; //InputManager.Instance.OnFingerMove -= OnFingerMove2; EventManager.Instance.Unregister(EventManager.EventName.Level_OnDrag, _OnDragMove); InputManager.Instance.OnFingersMove -= OnFingersMove; } } private void _OnDragMove(PointerEventData pointerData) { if (!FingerMoveEnable) return; var startPos = pointerData.pressPosition; var delta = pointerData.delta; var oldRay = mainCamera.ScreenPointToRay(startPos); var newRay = mainCamera.ScreenPointToRay( startPos + delta); var layerMask = 1 << LayerMask.NameToLayer("Ground"); if (Physics.Raycast(oldRay, out var oldHit, 1000, layerMask) && Physics.Raycast(newRay, out var newHit, 1000, layerMask)) { var offset = oldHit.point - newHit.point; transform.Translate(offset, Space.World); // DebugUtil.LogG("delta: " + delta + " offset: " + offset); } else { Vector2 touchDeltaPosition = -delta; float ratio = Size * unitPerPixel; Vector2 cameraMove = new(touchDeltaPosition.x * moveSpeedX * ratio, touchDeltaPosition.y * moveSpeedY * ratio); var cameraTransform = transform; var moveY = cameraTransform.up; moveY.y = 0; moveY = moveY.normalized * cameraMove.y; var moveX = cameraTransform.right; moveX.y = 0; moveX = moveX.normalized * cameraMove.x; transform.Translate(moveX + moveY, Space.World); } AdjustSelf(); } private void OnFingersMove(float delta) { if (!FingerMoveEnable) { return; } if (GuideManager.Instance.IsGuiding) // 引导中禁止缩放 return; //delta小于0是缩小 var zoomDelta = delta < 0 ? -ZoomStep : ZoomStep; //修改缩放值 ZoomScale += zoomDelta; AdjustSelf(); } public void KeepCenter(Vector3 centerPos) { var cacheTrans = mainCamera.transform; var oldPos = cacheTrans.position; oldPos.x = centerPos.x - 5f; oldPos.z = centerPos.z - 25f; // cacheTrans.position = oldPos; cacheTrans.DOMove(oldPos, 0.3f).OnUpdate(AdjustSelf); } Vector3 CalculateHitHorizontalPosition(Vector3 startPosition, Vector3 dir, float y) { if (dir.y == 0) return Vector3.zero; var p1 = startPosition; // 直线上一点 var v = dir; // 直线的方向向量 float x, z; // 已知直线上一点 p1(x1, y1, z1) 和 直线的方向向量 v(m, n, p) // 根据点向式 // (x - x1) / m = (y - y1) / n = (z - z1) / p // 有: // (x - p1.x) / v.x = (y - p1.y) / v.y = (z - p1.z) / v.z // 有: // x = p1.x + v.x * (y - p1.y) / v.y // z = p1.z + v.z * (y - p1.y) / v.y // 与平面 y = y 的交点坐标为 x = p1.x + v.x * (y - p1.y) / v.y; z = p1.z + v.z * (y - p1.y) / v.y; return new Vector3(x, y, z); } public void InitializeCameraPosition(Vector3 targetPosition) { var useOrthographic = PerformanceManager.instance != null && PerformanceManager.instance.data.fightUseOrthographic; var rotEuler = useOrthographic ? cameraStartRotationO : cameraStartRotationP; mainCamera.transform.position = CalculateCell2CameraPosition(targetPosition); // 修改为支持修改相机角度 /*mainCamera.transform.SetPositionAndRotation( CalculateCell2CameraPosition(targetPosition), Quaternion.Euler(rotEuler));*/ AdjustSelf(); } private Vector3 CalculateCell2CameraPosition(Vector3 targetPosition) { var cameraTransform = mainCamera.transform; var startPosition = cameraTransform.position; var v = cameraTransform.forward; float x, z; z = targetPosition.z - v.z * (targetPosition.y - startPosition.y) / v.y; x = targetPosition.x - v.x * (targetPosition.y - startPosition.y) / v.y; return new Vector3(x, startPosition.y, z); } public void AdjustSelf() { mainCamera.transform.position = AdjustPosition(Size, out var isInsideBounds); } private Vector3 AdjustPosition(float size, out bool IsInsideBounds) { var position = transform.position; IsInsideBounds = true; var center = CalculateHitHorizontalPosition(position, transform.forward, 0); var left = center.x - leftMargin * size; var right = center.x + rightMargin * size; var top = center.z + topMargin * size; var bottom = center.z - bottomMargin * size; if (right - left < CameraBounds.max.x - CameraBounds.min.x) { if (left < CameraBounds.min.x) { center.x = CameraBounds.min.x + leftMargin * size; IsInsideBounds = false; } else if (right > CameraBounds.max.x) { center.x = CameraBounds.max.x - rightMargin * size; IsInsideBounds = false; } } else { center.x = CameraBounds.center.x; IsInsideBounds = false; } if (top - bottom < CameraBounds.max.z - CameraBounds.min.z) { if (bottom < CameraBounds.min.z) { center.z = CameraBounds.min.z + bottomMargin * size; IsInsideBounds = false; } else if (top > CameraBounds.max.z) { center.z = CameraBounds.max.z - topMargin * size; IsInsideBounds = false; } } else { center.z = CameraBounds.center.z; IsInsideBounds = false; } position = CalculateHitHorizontalPosition(center, transform.forward, position.y); return position; } private void OnDrawGizmos() { if (mainCamera != null) { Gizmos.color = Color.red; var cameraTransform = transform; var position = cameraTransform.position; var size = Size; var center = CalculateHitHorizontalPosition(position, cameraTransform.forward, 0); var left = center.x - leftMargin * size; var right = center.x + rightMargin * size; var top = center.z + topMargin * size; var bottom = center.z - bottomMargin * size; Gizmos.DrawLineList(new ReadOnlySpan(new[] { new Vector3(left, 0.5f, top), new Vector3(left, 0.5f, bottom), new Vector3(left, 0.5f, bottom), new Vector3(right, 0.5f, bottom), new Vector3(right, 0.5f, bottom), new Vector3(right, 0.5f, top), new Vector3(right, 0.5f, top), new Vector3(left, 0.5f, top), } ) ); CameraBounds = boxBounds == null ? CameraBounds : boxBounds.bounds; Gizmos.DrawWireCube(CameraBounds.center, CameraBounds.size); } } }