7 lines
11 KiB
C#
7 lines
11 KiB
C#
using System;
|
||
using DG.Tweening;
|
||
using Framework;
|
||
using Gameplay.Performance;
|
||
using UnityEngine;
|
||
using Sirenix.OdinInspector;
|
||
using Gameplay.Guide;
|
||
|
||
[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<Camera>();
|
||
|
||
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));
|
||
}
|
||
else
|
||
{
|
||
// TODO: 这里的值是固定的,需要根据实际情况调整
|
||
//bottomMargin = 0.7f;
|
||
mainCamera.orthographic = false;
|
||
mainCamera.fieldOfView = 30;
|
||
mainCamera.transform.SetPositionAndRotation(new Vector3(0, 27, 0),
|
||
Quaternion.Euler(rotEuler));
|
||
}
|
||
}
|
||
|
||
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, (Action<Vector2>) OnFingerMove2);
|
||
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, (Action<Vector2>) OnFingerMove2);
|
||
InputManager.Instance.OnFingersMove -= OnFingersMove;
|
||
}
|
||
}
|
||
|
||
private void OnFingerMove2(Vector2 delta)
|
||
{
|
||
if (!FingerMoveEnable)
|
||
return;
|
||
|
||
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.5f).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.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<Vector3>(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);
|
||
}
|
||
}
|
||
} |