388 lines
15 KiB
C#
388 lines
15 KiB
C#
|
|
using System;
|
|||
|
|
using DG.Tweening;
|
|||
|
|
using Framework;
|
|||
|
|
using GameWrapper;
|
|||
|
|
using Gameplay.Guide;
|
|||
|
|
using UnityEngine;
|
|||
|
|
using UnityEngine.EventSystems;
|
|||
|
|
|
|||
|
|
namespace Gameplay
|
|||
|
|
{
|
|||
|
|
/// <summary>
|
|||
|
|
/// 相机逻辑的具体实现,实现ICameraLogic接口
|
|||
|
|
/// </summary>
|
|||
|
|
public class CameraLogic : ICameraLogic
|
|||
|
|
{
|
|||
|
|
private CameraData cameraData;
|
|||
|
|
|
|||
|
|
// 内部状态变量
|
|||
|
|
private float targetZoomScale;
|
|||
|
|
private float zoomVelocity;
|
|||
|
|
private Vector3 targetPanPosition;
|
|||
|
|
private Vector3 panVelocity;
|
|||
|
|
private Vector2 lastDragDelta;
|
|||
|
|
private float lastDragTime;
|
|||
|
|
private Vector3 _startPos;
|
|||
|
|
private float _cacheZoomScale;
|
|||
|
|
private bool isIgnoreZoomLimit;
|
|||
|
|
|
|||
|
|
// 缩放百分比,0表示放到最小,1表示放到最大
|
|||
|
|
private float ZoomScale
|
|||
|
|
{
|
|||
|
|
get
|
|||
|
|
{
|
|||
|
|
if (cameraData.IsOrthographic)
|
|||
|
|
return (cameraData.CameraMaxSize - cameraData.MainCamera.orthographicSize) / (cameraData.CameraMaxSize - cameraData.CameraMinSize);
|
|||
|
|
else
|
|||
|
|
return (cameraData.CameraMaxFOV - cameraData.MainCamera.fieldOfView) / (cameraData.CameraMaxFOV - cameraData.CameraMinFOV);
|
|||
|
|
}
|
|||
|
|
set
|
|||
|
|
{
|
|||
|
|
var t = Mathf.Clamp(value, 0, 1);
|
|||
|
|
if (Math.Abs(ZoomScale - t) < float.Epsilon)
|
|||
|
|
return;
|
|||
|
|
if (cameraData.IsOrthographic)
|
|||
|
|
cameraData.MainCamera.orthographicSize = Mathf.Lerp(cameraData.CameraMaxSize, cameraData.CameraMinSize, t);
|
|||
|
|
else
|
|||
|
|
cameraData.MainCamera.fieldOfView = Mathf.Lerp(cameraData.CameraMaxFOV, cameraData.CameraMinFOV, t);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//size,将透视和正交相机的fov和orthographic size整合为统一数值
|
|||
|
|
private float Size
|
|||
|
|
{
|
|||
|
|
get
|
|||
|
|
{
|
|||
|
|
if (cameraData.IsOrthographic)
|
|||
|
|
return cameraData.MainCamera.orthographicSize;
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
var fov = cameraData.MainCamera.fieldOfView;
|
|||
|
|
//这里乘(CameraMaxSize - CameraMinSize)/(CameraMaxFOV - CameraMinFOV)是将fov的区间变得和orthographic size区间一致
|
|||
|
|
return fov * (cameraData.CameraMaxSize - cameraData.CameraMinSize) / (cameraData.CameraMaxFOV - cameraData.CameraMinFOV);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public CameraLogic(CameraData cameraData)
|
|||
|
|
{
|
|||
|
|
this.cameraData = cameraData;
|
|||
|
|
targetZoomScale = ZoomScale;
|
|||
|
|
targetPanPosition = cameraData.transform.position;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 注册工厂方法,在Unity初始化时调用
|
|||
|
|
/// </summary>
|
|||
|
|
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
|
|||
|
|
private static void RegisterFactory()
|
|||
|
|
{
|
|||
|
|
CameraLogicFactory.RegisterFactory(data => new CameraLogic(data));
|
|||
|
|
Debug.Log("Registered CameraLogic factory method");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void Initialize()
|
|||
|
|
{
|
|||
|
|
if (Application.isPlaying)
|
|||
|
|
{
|
|||
|
|
EventManager.Instance.Register<PointerEventData>(EventManager.EventName.Level_OnDrag, OnDragMove);
|
|||
|
|
EventManager.Instance.Register<PointerEventData>(EventManager.EventName.Level_OnEndDrag, OnEndDrag);
|
|||
|
|
InputManager.Instance.OnFingersMove += OnFingersMove;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void Cleanup()
|
|||
|
|
{
|
|||
|
|
if (Application.isPlaying)
|
|||
|
|
{
|
|||
|
|
if (InputManager.Instance == null) return;
|
|||
|
|
EventManager.Instance.Unregister<PointerEventData>(EventManager.EventName.Level_OnDrag, OnDragMove);
|
|||
|
|
EventManager.Instance.Unregister<PointerEventData>(EventManager.EventName.Level_OnEndDrag, OnEndDrag);
|
|||
|
|
InputManager.Instance.OnFingersMove -= OnFingersMove;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void Update()
|
|||
|
|
{
|
|||
|
|
// 处理平滑缩放
|
|||
|
|
if (cameraData.smoothZoom && Math.Abs(ZoomScale - targetZoomScale) > 0.001f)
|
|||
|
|
{
|
|||
|
|
// 使用SmoothDamp实现平滑缩放效果
|
|||
|
|
float newZoomScale = Mathf.SmoothDamp(ZoomScale, targetZoomScale, ref zoomVelocity, cameraData.zoomSmoothTime);
|
|||
|
|
ZoomScale = newZoomScale;
|
|||
|
|
AdjustSelf();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 处理平滑平移
|
|||
|
|
if (cameraData.smoothPanning && Vector3.Distance(cameraData.transform.position, targetPanPosition) > 0.01f)
|
|||
|
|
{
|
|||
|
|
// 限制最大速度
|
|||
|
|
Vector3 newPosition = Vector3.SmoothDamp(cameraData.transform.position, targetPanPosition,
|
|||
|
|
ref panVelocity, cameraData.panSmoothTime, cameraData.maxPanSpeed);
|
|||
|
|
cameraData.transform.position = newPosition;
|
|||
|
|
AdjustSelf();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void OnDragMove(PointerEventData pointerData)
|
|||
|
|
{
|
|||
|
|
if (!cameraData.FingerMoveEnable)
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
Vector3 newPosition;
|
|||
|
|
var startPos = pointerData.pressPosition;
|
|||
|
|
var delta = pointerData.delta;
|
|||
|
|
|
|||
|
|
// 记录拖动速度数据,用于惯性计算
|
|||
|
|
lastDragDelta = delta;
|
|||
|
|
lastDragTime = Time.time;
|
|||
|
|
|
|||
|
|
var oldRay = cameraData.MainCamera.ScreenPointToRay(startPos);
|
|||
|
|
var newRay = cameraData.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;
|
|||
|
|
|
|||
|
|
if (cameraData.smoothPanning)
|
|||
|
|
{
|
|||
|
|
// 在平滑模式下设置目标位置
|
|||
|
|
targetPanPosition = cameraData.transform.position + offset;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
// 立即移动
|
|||
|
|
cameraData.transform.Translate(offset, Space.World);
|
|||
|
|
AdjustSelf();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
Vector2 touchDeltaPosition = -delta;
|
|||
|
|
float ratio = Size * cameraData.unitPerPixel;
|
|||
|
|
Vector2 cameraMove = new(touchDeltaPosition.x * cameraData.moveSpeedX * ratio,
|
|||
|
|
touchDeltaPosition.y * cameraData.moveSpeedY * ratio);
|
|||
|
|
|
|||
|
|
var cameraTransform = cameraData.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 (cameraData.smoothPanning)
|
|||
|
|
{
|
|||
|
|
// 在平滑模式下设置目标位置
|
|||
|
|
targetPanPosition = cameraData.transform.position + moveX + moveY;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
// 立即移动
|
|||
|
|
cameraData.transform.Translate(moveX + moveY, Space.World);
|
|||
|
|
AdjustSelf();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void OnEndDrag(PointerEventData pointerData)
|
|||
|
|
{
|
|||
|
|
if (cameraData.smoothPanning && cameraData.panInertia > 0 && Time.time - lastDragTime < 0.1f)
|
|||
|
|
{
|
|||
|
|
// 计算惯性位移
|
|||
|
|
Vector2 touchDeltaPosition = -lastDragDelta * cameraData.panInertia;
|
|||
|
|
float ratio = Size * cameraData.unitPerPixel;
|
|||
|
|
Vector2 inertiaMove = new(touchDeltaPosition.x * cameraData.moveSpeedX * ratio,
|
|||
|
|
touchDeltaPosition.y * cameraData.moveSpeedY * ratio);
|
|||
|
|
|
|||
|
|
var cameraTransform = cameraData.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;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void OnFingersMove(float delta)
|
|||
|
|
{
|
|||
|
|
if (!cameraData.FingerMoveEnable)
|
|||
|
|
{
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (GuideManager.Instance.IsGuiding) // 引导中禁止缩放
|
|||
|
|
return;
|
|||
|
|
|
|||
|
|
// 缩放系数随当前缩放程度变化,缩放越小,缩放速度越慢,提供更精确的控制
|
|||
|
|
float currentZoomFactor = CameraData.ZoomStep * cameraData.zoomSensitivity;
|
|||
|
|
|
|||
|
|
// 根据当前缩放比例调整缩放速度
|
|||
|
|
if (cameraData.zoomScaleFactor > 0)
|
|||
|
|
{
|
|||
|
|
float scaleModifier = Mathf.Lerp(0.5f, 1.5f, ZoomScale * cameraData.zoomScaleFactor);
|
|||
|
|
currentZoomFactor *= scaleModifier;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//delta小于0是缩小
|
|||
|
|
var zoomDelta = delta < 0 ? -currentZoomFactor : currentZoomFactor;
|
|||
|
|
|
|||
|
|
//修改目标缩放值
|
|||
|
|
if (cameraData.smoothZoom)
|
|||
|
|
{
|
|||
|
|
targetZoomScale = Mathf.Clamp01(targetZoomScale + zoomDelta);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
//直接缩放,无平滑效果
|
|||
|
|
ZoomScale += zoomDelta;
|
|||
|
|
AdjustSelf();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void AdjustSelf()
|
|||
|
|
{
|
|||
|
|
cameraData.transform.position = AdjustPosition(Size, out var isInsideBounds);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private Vector3 AdjustPosition(float size, out bool IsInsideBounds)
|
|||
|
|
{
|
|||
|
|
var position = cameraData.transform.position;
|
|||
|
|
IsInsideBounds = true;
|
|||
|
|
|
|||
|
|
var center = cameraData.CalculateHitHorizontalPosition(position, cameraData.transform.forward, 0);
|
|||
|
|
var left = center.x - cameraData.leftMargin * size;
|
|||
|
|
var right = center.x + cameraData.rightMargin * size;
|
|||
|
|
var top = center.z + cameraData.topMargin * size;
|
|||
|
|
var bottom = center.z - cameraData.bottomMargin * size;
|
|||
|
|
|
|||
|
|
if (right - left < cameraData.CameraBounds.max.x - cameraData.CameraBounds.min.x)
|
|||
|
|
{
|
|||
|
|
if (left < cameraData.CameraBounds.min.x)
|
|||
|
|
{
|
|||
|
|
center.x = cameraData.CameraBounds.min.x + cameraData.leftMargin * size;
|
|||
|
|
IsInsideBounds = false;
|
|||
|
|
}
|
|||
|
|
else if (right > cameraData.CameraBounds.max.x)
|
|||
|
|
{
|
|||
|
|
center.x = cameraData.CameraBounds.max.x - cameraData.rightMargin * size;
|
|||
|
|
IsInsideBounds = false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
center.x = cameraData.CameraBounds.center.x;
|
|||
|
|
IsInsideBounds = false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (top - bottom < cameraData.CameraBounds.max.z - cameraData.CameraBounds.min.z)
|
|||
|
|
{
|
|||
|
|
if (bottom < cameraData.CameraBounds.min.z)
|
|||
|
|
{
|
|||
|
|
center.z = cameraData.CameraBounds.min.z + cameraData.bottomMargin * size;
|
|||
|
|
IsInsideBounds = false;
|
|||
|
|
}
|
|||
|
|
else if (top > cameraData.CameraBounds.max.z)
|
|||
|
|
{
|
|||
|
|
center.z = cameraData.CameraBounds.max.z - cameraData.topMargin * size;
|
|||
|
|
IsInsideBounds = false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
center.z = cameraData.CameraBounds.center.z;
|
|||
|
|
IsInsideBounds = false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
position = cameraData.CalculateHitHorizontalPosition(center, cameraData.transform.forward, position.y);
|
|||
|
|
return position;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void KeepCenter(Vector3 centerPos)
|
|||
|
|
{
|
|||
|
|
var cacheTrans = cameraData.MainCamera.transform;
|
|||
|
|
var oldPos = cacheTrans.position;
|
|||
|
|
oldPos.x = centerPos.x - 5f;
|
|||
|
|
oldPos.z = centerPos.z - 25f;
|
|||
|
|
cacheTrans.DOMove(oldPos, 0.3f).OnUpdate(AdjustSelf);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 营地相机保持中心位置
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="centerPos"></param>
|
|||
|
|
public void CampKeepCenter(Vector3 centerPos)
|
|||
|
|
{
|
|||
|
|
Transform cacheTrans = cameraData.MainCamera.transform;
|
|||
|
|
Vector3 newPos = cacheTrans.position;
|
|||
|
|
newPos.x = centerPos.x + 17.5f;
|
|||
|
|
newPos.z = centerPos.z + 30.5f;
|
|||
|
|
|
|||
|
|
cacheTrans.DOMove(newPos, 0.3f).OnUpdate(AdjustSelf).OnComplete(() => { targetZoomScale = 1f; });
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void RecordStartPos()
|
|||
|
|
{
|
|||
|
|
_startPos = cameraData.MainCamera.transform.position;
|
|||
|
|
_cacheZoomScale = targetZoomScale;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void ResetToStartPos(float during = 0.3f)
|
|||
|
|
{
|
|||
|
|
if (cameraData.MainCamera == null) return;
|
|||
|
|
targetZoomScale = _cacheZoomScale;
|
|||
|
|
cameraData.MainCamera.transform.DOMove(_startPos, during).OnUpdate(AdjustSelf);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private Vector3 CalculateCell2CameraPosition(Vector3 targetPosition)
|
|||
|
|
{
|
|||
|
|
var cameraTransform = cameraData.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 InitializeCameraPosition(Vector3 targetPosition, CameraInfoData cameraInfo = null)
|
|||
|
|
{
|
|||
|
|
cameraData.MainCamera.transform.position = CalculateCell2CameraPosition(targetPosition);
|
|||
|
|
|
|||
|
|
// 进入关卡初始化相机
|
|||
|
|
if (cameraInfo != null)
|
|||
|
|
{
|
|||
|
|
cameraData.rightMargin = cameraInfo.rightMarginInPre;
|
|||
|
|
cameraData.leftMargin = cameraInfo.leftMarginInPre;
|
|||
|
|
cameraData.topMargin = cameraInfo.topMarginInInPre;
|
|||
|
|
cameraData.bottomMargin = cameraInfo.bottomMarginInPre;
|
|||
|
|
SetCameraFOVSize(cameraInfo.initialFOV);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var rotEuler = cameraData.UseOrthographicMode ? cameraData.CameraStartRotationO : cameraData.CameraStartRotationP;
|
|||
|
|
AdjustSelf();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void SetCameraFOVSize(float initialFOV)
|
|||
|
|
{
|
|||
|
|
if (cameraData.MainCamera == null) return;
|
|||
|
|
var fov = Mathf.Clamp(initialFOV, cameraData.CameraMinFOV, cameraData.CameraMaxFOV);
|
|||
|
|
if (!cameraData.IsOrthographic)
|
|||
|
|
{
|
|||
|
|
cameraData.CameraStartFOV = fov;
|
|||
|
|
cameraData.MainCamera.fieldOfView = fov;
|
|||
|
|
targetZoomScale = ZoomScale;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|