NLDClient-yudde/ProjectNLD/Assets/Code/Scripts/GameWrapper/Camera/CameraController.cs

587 lines
19 KiB
C#
Raw Normal View History

2025-04-29 15:30:55 +08:00
using System;
using DG.Tweening;
2025-04-29 13:16:40 +08:00
using Framework;
using PhxhSDK;
2025-04-28 15:26:51 +08:00
using UnityEngine;
2025-04-29 15:30:55 +08:00
using Sirenix.OdinInspector;
2025-04-29 13:16:40 +08:00
using UnityEngine.EventSystems;
2025-04-28 15:26:51 +08:00
[ExecuteAlways]
public class CameraController : MonoBehaviour
{
2025-04-29 15:30:55 +08:00
private const float CameraStartY = 25;
private const float CameraStartSize = 12;
private float CameraStartFOV = 30;
private const float NearClipPlane = 0.03f;
private const float FarClipPlane = 150;
2025-06-10 16:31:09 +08:00
private float _lastFOV = 30;
private Vector3 _lastPos = new Vector3(0, 0, 0);
2025-04-29 15:30:55 +08:00
private readonly Vector3 cameraStartRotationO = new Vector3(37.5f, -10f, 0f);
2025-04-28 15:26:51 +08:00
2025-04-29 15:30:55 +08:00
private readonly Vector3 cameraStartRotationP = new Vector3(45, -10, 0);
2025-04-28 15:26:51 +08:00
2025-04-29 15:30:55 +08:00
//之前的缩放是6次滚轮从最小放到最大故每一次缩放的值为1/6f
private const float ZoomStep = 1 / 12f;
[Tooltip("控制缩放响应曲线")] public float zoomSensitivity = 2.0f;
[Tooltip("缩放速度与当前缩放比例的关系系数")] public float zoomScaleFactor = 0.75f;
[Tooltip("是否使用平滑缩放效果")] public bool smoothZoom = true;
[ShowIf("smoothZoom")] public float zoomSmoothTime = 0.2f;
public float leftMargin = 1.5f;
public float rightMargin = 1.5f;
public float topMargin = 0.9f;
public float bottomMargin = 0.9f;
[Header("Camera Movement Settings")] public float moveSpeedX = 0.19f;
public float moveSpeedY = 0.3f;
public float unitPerPixel = 0.01f;
[Tooltip("是否使用平滑移动效果")] public bool smoothPanning = false;
[ShowIf("smoothPanning")] [Tooltip("平移平滑时间,值越小响应越快")]
public float panSmoothTime = 0.08f;
[ShowIf("smoothPanning")] [Tooltip("惯性系数,控制释放后继续滑动的距离")] [Range(0, 1)]
public float panInertia = 0.8f;
[ShowIf("smoothPanning")] [Tooltip("最大平移速度限制")]
public float maxPanSpeed = 3000f;
public float CameraMinSize = 6;
public float CameraMaxSize = 12;
public BoxCollider boxBounds;
public float CameraMinFOV = 15;
public float CameraMaxFOV = 30;
private bool _isScaleChange;
2025-04-29 15:30:55 +08:00
private Camera mainCamera;
public Camera MainCamera
{
get { return mainCamera; }
}
private bool isIgnoreZoomLimit;
public Bounds CameraBounds { get; set; }
private bool isOrthographic => mainCamera.orthographic;
//Camera拖动控制
public static bool FingerMoveEnable = true;
private float targetZoomScale;
private float zoomVelocity;
private Vector3 targetPanPosition;
private Vector3 panVelocity;
private Vector2 lastDragDelta;
private float lastDragTime;
//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);
}
}
2025-04-28 15:26:51 +08:00
private void Awake()
{
2025-08-11 18:26:11 +08:00
if (BusyWrapper.isBusy)
return;
2025-04-29 15:30:55 +08:00
mainCamera = GetComponent<Camera>();
targetZoomScale = ZoomScale;
targetPanPosition = transform.position;
2025-05-08 12:36:01 +08:00
var useOrthographic = Application.isPlaying && CameraServiceLocator.CameraService.UseOrthographicMode;
2025-04-28 15:26:51 +08:00
2025-04-29 15:30:55 +08:00
if (useOrthographic)
{
mainCamera.orthographic = true;
mainCamera.orthographicSize = CameraStartSize;
}
else
2025-04-28 15:26:51 +08:00
{
2025-04-29 15:30:55 +08:00
mainCamera.orthographic = false;
mainCamera.fieldOfView = CameraStartFOV;
2025-04-28 15:26:51 +08:00
}
2025-04-29 15:30:55 +08:00
mainCamera.nearClipPlane = NearClipPlane;
mainCamera.farClipPlane = FarClipPlane;
CameraBounds = boxBounds != null ? boxBounds.bounds : CameraBounds;
AdjustSelf();
2025-04-28 15:26:51 +08:00
}
private void OnEnable()
{
2025-08-11 18:26:11 +08:00
if (BusyWrapper.isBusy)
return;
2025-04-29 13:16:40 +08:00
if (Application.isPlaying)
2025-04-28 15:26:51 +08:00
{
2025-04-29 13:16:40 +08:00
//InputManager.Instance.OnFingerMove += OnFingerMove2;
EventManager.Instance.Register<PointerEventData>(EventManager.EventName.Level_OnDrag, _OnDragMove);
2025-04-29 15:30:55 +08:00
EventManager.Instance.Register<PointerEventData>(EventManager.EventName.Level_OnEndDrag, OnEndDrag);
InputManager.Instance.OnFingersMove += OnFingersMove;
InputManager.Instance.OnFingerUp += _OnFingerUp;
2025-04-28 15:26:51 +08:00
}
}
private void OnDisable()
{
2025-08-11 18:26:11 +08:00
if (BusyWrapper.isBusy)
return;
2025-04-29 13:16:40 +08:00
if (Application.isPlaying)
2025-04-28 15:26:51 +08:00
{
2025-04-29 13:16:40 +08:00
if (InputManager.Instance == null) return;
2025-04-29 15:30:55 +08:00
//InputManager.Instance.OnFingerMove -= OnFingerMove2;
EventManager.Instance.Unregister<PointerEventData>(EventManager.EventName.Level_OnDrag, _OnDragMove);
EventManager.Instance.Unregister<PointerEventData>(EventManager.EventName.Level_OnEndDrag, OnEndDrag);
InputManager.Instance.OnFingersMove -= OnFingersMove;
InputManager.Instance.OnFingerUp -= _OnFingerUp;
2025-04-28 15:26:51 +08:00
}
}
2025-04-29 15:30:55 +08:00
private void _OnDragMove(PointerEventData pointerData)
2025-04-28 15:26:51 +08:00
{
2025-04-29 15:30:55 +08:00
if (!FingerMoveEnable)
return;
Vector3 newPosition;
var startPos = pointerData.pressPosition;
var delta = pointerData.delta;
// 记录拖动速度数据,用于惯性计算
lastDragDelta = delta;
lastDragTime = Time.time;
CameraManager.IsMainCameraMoving = true;
2025-04-29 15:30:55 +08:00
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))
2025-04-28 15:26:51 +08:00
{
2025-04-29 15:30:55 +08:00
var offset = oldHit.point - newHit.point;
if (smoothPanning)
{
// 在平滑模式下设置目标位置
targetPanPosition = transform.position + offset;
}
else
{
// 立即移动
transform.Translate(offset, Space.World);
AdjustSelf();
}
}
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;
if (smoothPanning)
{
// 在平滑模式下设置目标位置
targetPanPosition = transform.position + moveX + moveY;
}
else
{
// 立即移动
transform.Translate(moveX + moveY, Space.World);
AdjustSelf();
}
2025-04-28 15:26:51 +08:00
}
}
2025-04-29 15:30:55 +08:00
// 处理拖动结束时的惯性
public void OnEndDrag(PointerEventData pointerData)
2025-04-28 15:26:51 +08:00
{
2025-04-29 15:30:55 +08:00
if (smoothPanning && panInertia > 0 && Time.time - lastDragTime < 0.1f)
2025-04-28 15:26:51 +08:00
{
2025-04-29 15:30:55 +08:00
// 计算惯性位移
Vector2 touchDeltaPosition = -lastDragDelta * panInertia;
float ratio = Size * unitPerPixel;
Vector2 inertiaMove = new(touchDeltaPosition.x * moveSpeedX * ratio,
touchDeltaPosition.y * moveSpeedY * ratio);
var cameraTransform = transform;
var moveY = cameraTransform.up;
moveY.y = 0;
moveY = moveY.normalized * inertiaMove.y;
var moveX = cameraTransform.right;
moveX.y = 0;
moveX = moveX.normalized * inertiaMove.x;
// 应用惯性
targetPanPosition += moveX + moveY;
2025-04-28 15:26:51 +08:00
}
CameraManager.IsMainCameraMoving = false;
2025-04-28 15:26:51 +08:00
}
2025-04-29 15:30:55 +08:00
private void OnFingersMove(float delta)
2025-04-29 13:16:40 +08:00
{
2025-04-29 15:30:55 +08:00
if (!FingerMoveEnable)
{
return;
}
if (CameraServiceLocator.CameraService.IsGuiding) // 引导中禁止缩放
return;
// 缩放系数随当前缩放程度变化,缩放越小,缩放速度越慢,提供更精确的控制
float currentZoomFactor = ZoomStep * zoomSensitivity;
CameraManager.IsMainCameraMoving = true;
2025-07-10 14:59:35 +08:00
// DebugUtil.LogP($"OnFingersMove");
2025-04-29 15:30:55 +08:00
// 根据当前缩放比例调整缩放速度
if (zoomScaleFactor > 0)
{
float scaleModifier = Mathf.Lerp(0.5f, 1.5f, ZoomScale * zoomScaleFactor);
currentZoomFactor *= scaleModifier;
}
//delta小于0是缩小
var zoomDelta = delta < 0 ? -currentZoomFactor : currentZoomFactor;
//修改目标缩放值
_isScaleChange = true;
2025-04-29 15:30:55 +08:00
if (smoothZoom)
{
targetZoomScale = Mathf.Clamp01(targetZoomScale + zoomDelta);
}
else
{
//直接缩放,无平滑效果
ZoomScale += zoomDelta;
AdjustSelf();
}
2025-04-29 13:16:40 +08:00
}
private void _OnFingerUp(Vector2 screenPos)
{
CameraManager.IsMainCameraMoving = false;
}
2025-08-14 12:57:43 +08:00
private void LateUpdate()
2025-04-29 13:16:40 +08:00
{
2025-08-11 18:26:11 +08:00
if (BusyWrapper.isBusy)
return;
2025-04-29 15:30:55 +08:00
// 处理平滑缩放
bool isScaled = Math.Abs(ZoomScale - targetZoomScale) > 0.01f;
2025-06-10 16:31:09 +08:00
bool isFovChange = Math.Abs(_lastFOV - mainCamera.fieldOfView) > 0.01f;
if (smoothZoom && isScaled)
2025-04-29 15:30:55 +08:00
{
// 使用SmoothDamp实现平滑缩放效果
float newZoomScale = Mathf.SmoothDamp(ZoomScale, targetZoomScale, ref zoomVelocity, zoomSmoothTime);
ZoomScale = newZoomScale;
AdjustSelf();
}
// 处理平滑平移
if (smoothPanning && Vector3.Distance(transform.position, targetPanPosition) > 0.01f)
{
// 限制最大速度
Vector3 newPosition = Vector3.SmoothDamp(transform.position, targetPanPosition,
ref panVelocity, panSmoothTime, maxPanSpeed);
transform.position = newPosition;
AdjustSelf();
}
if (_lastPos != mainCamera.transform.position)
{
_lastPos = mainCamera.transform.position;
CameraManager.IsMainCameraMoving = true;
return;
}
else
{
CameraManager.IsMainCameraMoving = false;
}
if (_isScaleChange && !isScaled)
{
//DebugUtil.LogP($"--- Update _isScaleChange:{_isScaleChange}, isScaled{isScaled}");
CameraManager.IsMainCameraMoving = false;
_isScaleChange = false;
}
else if (isFovChange)
{
2025-06-10 16:43:27 +08:00
_lastFOV = mainCamera.fieldOfView;
// DebugUtil.LogP($"--- Update _isScaleChange:{_isScaleChange}, isScaled{isScaled}");
// DebugUtil.LogP($"==== Update isFovChange, isScaled{isScaled}");
CameraManager.IsMainCameraMoving = true;
2025-08-14 12:57:43 +08:00
EventManager.Instance.Send(EventManager.EventName.Camera_FovChanged);
2025-06-10 16:43:27 +08:00
}
else
{
CameraManager.IsMainCameraMoving = false;
}
2025-04-29 13:16:40 +08:00
}
2025-04-29 15:30:55 +08:00
public void KeepCenter(Vector3 centerPos)
2025-04-29 13:16:40 +08:00
{
2025-04-29 15:30:55 +08:00
var cacheTrans = mainCamera.transform;
var oldPos = cacheTrans.position;
oldPos.x = centerPos.x - 5f;
oldPos.z = centerPos.z - 25f;
// cacheTrans.position = oldPos;
CameraManager.IsMainCameraMoving = true;
cacheTrans.DOMove(oldPos, 0.3f).OnUpdate(AdjustSelf).OnComplete(() =>
{
CameraManager.IsMainCameraMoving = false;
});
2025-04-29 13:16:40 +08:00
}
2025-04-29 15:30:55 +08:00
/// <summary>
/// 营地相机保持中心位置
/// </summary>
/// <param name="centerPos"></param>
public void CampKeepCenter(Vector3 centerPos)
{
Transform cacheTrans = mainCamera.transform;
Vector3 newPos = cacheTrans.position;
newPos.x = centerPos.x + 17.5f;
newPos.z = centerPos.z + 30.5f;
2025-04-28 15:26:51 +08:00
2025-07-24 13:32:22 +08:00
cacheTrans.DOMove(newPos, 0.3f).OnUpdate(AdjustSelf);//.OnComplete(() => { targetZoomScale = 1f; });
targetZoomScale = 1f;
}
/// <summary>
/// 营地还原相机fov
/// </summary>
public void CampRevertFov()
{
targetZoomScale = 0f;
2025-04-29 15:30:55 +08:00
}
2025-04-28 15:26:51 +08:00
2025-04-29 15:30:55 +08:00
Vector3 CalculateHitHorizontalPosition(Vector3 startPosition, Vector3 dir, float y)
2025-04-28 15:26:51 +08:00
{
2025-04-29 15:30:55 +08:00
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);
2025-04-28 15:26:51 +08:00
}
2025-04-29 15:30:55 +08:00
public void InitializeCameraPosition(Vector3 targetPosition, MapData.CameraInfo cameraInfo = null)
2025-04-28 15:26:51 +08:00
{
2025-05-08 12:36:01 +08:00
var useOrthographic = Application.isPlaying && CameraServiceLocator.CameraService.UseOrthographicMode;
2025-04-29 15:30:55 +08:00
mainCamera.transform.position = CalculateCell2CameraPosition(targetPosition);
// 进入关卡初始化相机
if (cameraInfo != null)
{
rightMargin = cameraInfo.rightMarginInPre;
leftMargin = cameraInfo.leftMarginInPre;
topMargin = cameraInfo.topMarginInInPre;
bottomMargin = cameraInfo.bottomMarginInPre;
SetCameraFOVSize(cameraInfo.initialFOV);
}
var rotEuler = useOrthographic ? cameraStartRotationO : cameraStartRotationP;
// 修改为支持修改相机角度
/*mainCamera.transform.SetPositionAndRotation(
CalculateCell2CameraPosition(targetPosition), Quaternion.Euler(rotEuler));*/
AdjustSelf();
2025-04-28 15:26:51 +08:00
}
2025-04-29 15:30:55 +08:00
private Vector3 CalculateCell2CameraPosition(Vector3 targetPosition)
2025-04-28 15:26:51 +08:00
{
2025-04-29 15:30:55 +08:00
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);
2025-04-28 15:26:51 +08:00
}
public void AdjustSelf()
{
2025-04-29 15:30:55 +08:00
mainCamera.transform.position = AdjustPosition(Size, out var isInsideBounds);
2025-04-28 15:26:51 +08:00
}
public void SetCameraFOVSize(float initialFOV)
{
2025-04-29 15:30:55 +08:00
if (mainCamera == null) return;
var fov = Mathf.Clamp(initialFOV, CameraMinFOV, CameraMaxFOV);
if (!mainCamera.orthographic)
{
CameraStartFOV = fov;
mainCamera.fieldOfView = fov;
targetZoomScale = ZoomScale;
}
2025-04-28 15:26:51 +08:00
}
2025-04-29 15:30:55 +08:00
private Vector3 _startPos;
private float _cacheZoomScale;
2025-04-28 15:26:51 +08:00
public void RecordStartPos()
{
2025-04-29 15:30:55 +08:00
_startPos = mainCamera.transform.position;
_cacheZoomScale = targetZoomScale;
2025-04-28 15:26:51 +08:00
}
public void ResetToStartPos(float during = 0.3f)
{
2025-04-29 15:30:55 +08:00
if (mainCamera == null) return;
targetZoomScale = _cacheZoomScale;
mainCamera.transform.DOMove(_startPos, during).OnUpdate(AdjustSelf);
// .OnComplete(() => { targetZoomScale = 1f; });
2025-04-28 15:26:51 +08:00
}
2025-04-29 15:30:55 +08:00
private Vector3 AdjustPosition(float size, out bool IsInsideBounds)
{
var position = transform.position;
IsInsideBounds = true;
2025-04-28 15:26:51 +08:00
2025-04-29 15:30:55 +08:00
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;
2025-04-28 15:26:51 +08:00
2025-04-29 15:30:55 +08:00
if (right - left < CameraBounds.max.x - CameraBounds.min.x)
2025-04-28 15:26:51 +08:00
{
2025-04-29 15:30:55 +08:00
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;
}
2025-04-28 15:26:51 +08:00
}
2025-04-29 15:30:55 +08:00
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;
2025-04-28 15:26:51 +08:00
}
2025-04-29 15:30:55 +08:00
private void OnDrawGizmos()
{
2025-08-11 18:26:11 +08:00
if (BusyWrapper.isBusy)
return;
2025-04-29 15:30:55 +08:00
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);
}
}
2025-04-28 15:26:51 +08:00
}