TH1/Unity/Assets/Scripts/TH1_UI/CameraController.cs
2026-06-11 00:34:50 +08:00

306 lines
11 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using RuntimeData;
using TH1_Logic.Core;
public class CameraController : MonoBehaviour
{
public float zoomSpeed = 5f; // 滚轮缩放速度
public float minZoom = 3f; // 最小缩放值
public float maxZoom = 15f; // 最大缩放值
public float panSpeed = 0.5f; // 拖动地图的速度
//相机移动上下边界(会根据地图尺寸动态计算)
public float minX = -240f;
public float maxX = 240f;
public float minY = -80f;
public float maxY = 280f;
// 边界边距(额外留白)
public float boundaryMargin = 50f;
//相机移动动画相关参数
public float moveSpeed = 0.5f; // 平滑移动速度
private Vector3? _smoothTargetPosition = null; //记录相机平滑移动的起点
private Vector3 _startPos;
private float _moveProgress = 0f;
private float _moveDuration; // 可调节移动总时长
private bool _isMoving = false;
private const float TARGET_ASPECT = 16f / 9f; // 目标宽高比
private Camera _camera;
private int _lastWidth, _lastHeight;
void Start()
{
_camera = GetComponent<Camera>();
UpdateViewport();
}
/// <summary>
/// 根据地图尺寸动态计算相机移动边界
/// </summary>
public void CalculateBoundsFromMapSize(uint mapWidth, uint mapHeight)
{
// 计算地图四个角的网格坐标对应的世界坐标
// 左下角 (0, 0)
Vector3 bottomLeft = Table.Instance.GridPosToWorld(new Vector2Int(0, 0));
// 右上角 (width-1, height-1)
Vector3 topRight = Table.Instance.GridPosToWorld(new Vector2Int((int)mapWidth - 1, (int)mapHeight - 1));
// 左上角 (0, height-1)
Vector3 topLeft = Table.Instance.GridPosToWorld(new Vector2Int(0, (int)mapHeight - 1));
// 右下角 (width-1, 0)
Vector3 bottomRight = Table.Instance.GridPosToWorld(new Vector2Int((int)mapWidth - 1, 0));
// 计算所有角的 X 和 Y 范围
float minWorldX = Mathf.Min(bottomLeft.x, topRight.x, topLeft.x, bottomRight.x);
float maxWorldX = Mathf.Max(bottomLeft.x, topRight.x, topLeft.x, bottomRight.x);
float minWorldY = Mathf.Min(bottomLeft.y, topRight.y, topLeft.y, bottomRight.y);
float maxWorldY = Mathf.Max(bottomLeft.y, topRight.y, topLeft.y, bottomRight.y);
// 添加边距
minX = minWorldX - boundaryMargin;
maxX = maxWorldX + boundaryMargin;
minY = minWorldY - boundaryMargin;
maxY = maxWorldY + boundaryMargin;
Debug.Log($"Camera bounds calculated: X[{minX}, {maxX}], Y[{minY}, {maxY}] for map size {mapWidth}x{mapHeight}");
}
void Update()
{
HandleSmoothMove();
HandleZoom();
if (Screen.width != _lastWidth || Screen.height != _lastHeight)
UpdateViewport();
//if (_camera.orthographic) _camera.orthographicSize = 20f;
}
private void LateUpdate()
{
HandlePan();
}
void UpdateViewport()
{
float currentAspect = (float)Screen.width / Screen.height;
Rect rect = new Rect(0, 0, 1, 1); // 默认全屏
if (currentAspect > TARGET_ASPECT)
{
// 窗口过宽:左右黑边
rect.width = TARGET_ASPECT / currentAspect;
rect.x = (1f - rect.width) / 2f;
}
else if (currentAspect < TARGET_ASPECT)
{
// 窗口过高:上下黑边
rect.height = currentAspect / TARGET_ASPECT;
rect.y = (1f - rect.height) / 2f;
}
_camera.rect = rect; // 应用视口矩形
_lastWidth = Screen.width;
_lastHeight = Screen.height;
}
void HandleSmoothMove()
{
if (!UIBlockCameraDrag.MoveEvent && _isMoving && _smoothTargetPosition.HasValue)
{
_moveProgress += Time.deltaTime / _moveDuration;
float t = Mathf.SmoothStep(0, 1, _moveProgress); // 缓动曲线
Vector3 targetPos = _smoothTargetPosition.Value;
Camera.main.transform.position = Vector3.Lerp(_startPos, targetPos, t);
if (_moveProgress >= 1f)
{
Camera.main.transform.position = targetPos;
_smoothTargetPosition = null;
_isMoving = false;
}
}
}
private void CancelSmoothMove()
{
_smoothTargetPosition = null;
_isMoving = false;
_moveProgress = 0f;
}
void HandleZoom()
{
// 鼠标不在地图可交互区域时不响应滚轮缩放ShouldBlockDrag 兜住正在拖动地图、鼠标暂时滑到UI上的情况
if (!UIBlockCameraDrag.IsPointerOnUI && !UIBlockCameraDrag.ShouldBlockDrag) return;
// 获取滚轮输入
float scroll = Input.GetAxis("Mouse ScrollWheel");
if (scroll != 0f)
{
Camera.main.orthographicSize -= scroll * zoomSpeed;
Camera.main.orthographicSize = Mathf.Clamp(Camera.main.orthographicSize, minZoom, maxZoom);
}
}
void CameraMove(Vector3 deltaVector)
{
Vector3 targetPosition = Camera.main.transform.position + deltaVector;
// 计算当前相机视野的一半宽高
float vertExtent = Camera.main.orthographicSize;
float horzExtent = vertExtent * Camera.main.aspect;
// 限制相机的中心点,使得视野边缘不超出边界
float minXClamp = minX + horzExtent;
float maxXClamp = maxX - horzExtent;
float minYClamp = minY + vertExtent;
float maxYClamp = maxY - vertExtent;
targetPosition.x = Mathf.Clamp(targetPosition.x, minXClamp, maxXClamp);
targetPosition.y = Mathf.Clamp(targetPosition.y, minYClamp, maxYClamp);
Camera.main.transform.position = targetPosition;
}
void HandlePan()
{
if (UIBlockCameraDrag.MoveEvent)
{
CancelSmoothMove();
CameraMove(UIBlockCameraDrag.MoveVector);
/*Vector3 targetPosition = Camera.main.transform.position + ;
// 计算当前相机视野的一半宽高
float vertExtent = Camera.main.orthographicSize;
float horzExtent = vertExtent * Camera.main.aspect;
// 限制相机的中心点,使得视野边缘不超出边界
float minXClamp = minX + horzExtent;
float maxXClamp = maxX - horzExtent;
float minYClamp = minY + vertExtent;
float maxYClamp = maxY - vertExtent;
targetPosition.x = Mathf.Clamp(targetPosition.x, minXClamp, maxXClamp);
targetPosition.y = Mathf.Clamp(targetPosition.y, minYClamp, maxYClamp);
Camera.main.transform.position = targetPosition;*/
UIBlockCameraDrag.DragOrigin = Camera.main.ScreenToWorldPoint(Input.mousePosition);
return;
}
var main = Main.Instance;
var inputLogic = main != null ? main.InputLogic : null;
if (inputLogic != null && inputLogic.WASDMapMover)
{
inputLogic.WASDMapMover = false;
CameraMove(inputLogic.WASDMapMoverVector);
}
}
public void CameraFocusOnGrid(GridData gridData, bool Center = false)
{
Vector3 worldPosition = Table.Instance.GridToWorld(gridData);
float vertExtent = Camera.main.orthographicSize;
float horzExtent = vertExtent * Camera.main.aspect;
_moveDuration = Table.Instance.AnimDataAssets.AIAnimCameraMoveTime;
//瞬间移到中心
if (Center)
{
float clampedX = Mathf.Clamp(worldPosition.x, minX + horzExtent, maxX - horzExtent);
float clampedY = Mathf.Clamp(worldPosition.y, minY + vertExtent, maxY - vertExtent);
Vector3 finalTarget = new Vector3(clampedX, clampedY, Camera.main.transform.position.z);
_startPos = Camera.main.transform.position;
_smoothTargetPosition = finalTarget;
_moveProgress = 0f;
_isMoving = true;
}
//平滑移动到视觉框内
else
{
Vector3 camPos = Camera.main.transform.position;
// 获取世界坐标 → 屏幕坐标
Vector3 screenPos = Camera.main.WorldToScreenPoint(worldPosition);
float screenWidth = Screen.width;
float screenHeight = Screen.height;
// 原始区域尺寸
float boxWidth = 1400f;
float boxHeight = 600f;
// 削减左侧150像素后的区域
float reducedLeft = 550f;
//如果是AI行动给画面中心位置
if (!Main.MapData.CurPlayer?.IsSelfPlayer() ?? false)
{
boxWidth = 400f;
boxHeight = 200f;
reducedLeft = 0f;
}
float adjustedBoxWidth = boxWidth - reducedLeft;
float xMax = (screenWidth + boxWidth) / 2f; // 保持右侧不变
float xMin = xMax - adjustedBoxWidth; // 左侧往右收缩150像素
float yMin = (screenHeight - boxHeight) / 2f;
float yMax = (screenHeight + boxHeight) / 2f;
// 如果点已经在框中,不动
if (screenPos.x >= xMin && screenPos.x <= xMax &&
screenPos.y >= yMin && screenPos.y <= yMax)
{
return;
}
// 计算目标点应该在屏幕中央框内的“目标屏幕位置”
float targetScreenX = Mathf.Clamp(screenPos.x, xMin, xMax);
float targetScreenY = Mathf.Clamp(screenPos.y, yMin, yMax);
Vector3 desiredScreenPos = new Vector3(targetScreenX, targetScreenY, screenPos.z);
// 将这个屏幕位置转回世界空间
Vector3 desiredWorldPos = Camera.main.ScreenToWorldPoint(desiredScreenPos);
// 计算需要相机移动的 delta = 当前格子位置 - 应该出现在的位置
Vector3 offset = worldPosition - desiredWorldPos;
Vector3 targetPos = camPos + offset;
// 限制相机移动范围
vertExtent = Camera.main.orthographicSize;
horzExtent = vertExtent * Camera.main.aspect;
float minXClamp = minX + horzExtent;
float maxXClamp = maxX - horzExtent;
float minYClamp = minY + vertExtent;
float maxYClamp = maxY - vertExtent;
targetPos.x = Mathf.Clamp(targetPos.x, minXClamp, maxXClamp);
targetPos.y = Mathf.Clamp(targetPos.y, minYClamp, maxYClamp);
// 平滑移动
_startPos = camPos;
_smoothTargetPosition = new Vector3(targetPos.x, targetPos.y, camPos.z);
_moveProgress = 0f;
_isMoving = true;
}
}
}