2025-08-07 16:44:43 +08:00
using Base ;
2025-08-29 11:45:12 +08:00
using Map ;
2025-07-21 13:58:58 +08:00
using UnityEngine ;
namespace CameraControl
{
2025-08-29 11:45:12 +08:00
/// <summary>
/// 控制游戏摄像机的移动、缩放和维度切换。
/// 继承自 MonoSingleton 以确保场景中只有一个实例,并实现 ITick 和 ITickUI 接口以在游戏循环和UI循环中更新。
/// </summary>
2025-08-07 16:44:43 +08:00
public class CameraControl : Utils . MonoSingleton < CameraControl > , ITick , ITickUI
2025-07-21 13:58:58 +08:00
{
2025-08-29 11:45:12 +08:00
// 摄像机移动相关变量
[SerializeField] private float _zoomSpeed = 5f ; // 摄像机缩放速度
[SerializeField] private float _minZoom = 2f ; // 摄像机最小缩放级别(正交摄像机的 orthographicSize)
[SerializeField] private float _maxZoom = 20f ; // 摄像机最大缩放级别
[SerializeField] private float _focusLerpSpeed = 5f ; // 摄像机跟随目标时的平滑插值速度
2025-08-27 19:56:49 +08:00
2025-08-29 11:45:12 +08:00
private Vector3 _dragOrigin ; // 拖拽操作的起始世界坐标
private bool _isDragging ; // 标记摄像机是否正在被拖拽
2025-08-07 16:44:43 +08:00
2025-08-30 00:26:27 +08:00
private Camera _cachedCamera ; // 缓存的场景摄像机引用
2025-08-27 19:56:49 +08:00
2025-08-29 11:45:12 +08:00
/// <summary>
2025-08-30 00:26:27 +08:00
/// 获取当前生效的场景摄像机。懒加载并缓存。
2025-08-29 11:45:12 +08:00
/// </summary>
2025-08-30 00:26:27 +08:00
public Camera CurrentCamera
2025-08-27 19:56:49 +08:00
{
2025-08-30 00:26:27 +08:00
get
2025-08-29 11:45:12 +08:00
{
2025-08-30 00:26:27 +08:00
if ( ! _cachedCamera )
{
Debug . Log ( "[CameraControl] Attempting to get main camera..." ) ;
_cachedCamera = Camera . main ; // 尝试获取主摄像机
if ( ! _cachedCamera ) // 如果主摄像机不存在或未标记,则尝试查找场景中的第一个摄像机
{
Debug . LogWarning ( "[CameraControl] Main camera not found, attempting to find first object of type Camera." ) ;
_cachedCamera = FindFirstObjectByType < Camera > ( ) ;
}
if ( ! _cachedCamera )
{
Debug . LogError ( "[CameraControl] No available camera found in scene! CameraControl functionality will be limited." ) ;
}
else
{
Debug . Log ( $"[CameraControl] Camera cached: {_cachedCamera.name}" ) ;
}
}
return _cachedCamera ;
2025-08-29 11:45:12 +08:00
}
2025-08-27 19:56:49 +08:00
}
2025-08-29 11:45:12 +08:00
2025-08-30 00:26:27 +08:00
private int dimensionId ; // 当前摄像机控制器关注的维度索引
private string [ ] dimensionList ; // 维度名称列表
/// <summary>
/// MonoSingleton 的 OnStart 方法,在单例首次创建并激活时调用,早于普通的 Start 方法。
/// 用于初始化摄像机缓存。
/// </summary>
protected override void OnStart ( )
{
Debug . Log ( "[CameraControl] OnStart called. Subscribing to OnFocusedDimensionChanged event." ) ;
Program . Instance . OnFocusedDimensionChanged + = Init ;
}
2025-08-29 11:45:12 +08:00
/// <summary>
2025-08-30 00:26:27 +08:00
/// 当脚本实例被销毁时调用。
/// 用于取消订阅 Program.Instance 的事件,防止内存泄漏。
2025-08-29 11:45:12 +08:00
/// </summary>
2025-08-30 00:26:27 +08:00
private void OnDestroy ( )
2025-08-27 19:56:49 +08:00
{
2025-08-30 00:26:27 +08:00
Debug . Log ( "[CameraControl] OnDestroy called. Unsubscribing from OnFocusedDimensionChanged event." ) ;
Program . Instance . OnFocusedDimensionChanged - = Init ;
2025-08-27 19:56:49 +08:00
}
2025-08-30 00:26:27 +08:00
2025-08-27 19:56:49 +08:00
2025-08-29 11:45:12 +08:00
/// <summary>
/// 根据指定的维度对象初始化摄像机控制器。
/// 主要用于在聚焦维度改变时更新摄像机状态和内部的维度ID。
/// </summary>
/// <param name="obj">当前聚焦的维度对象。</param>
private void Init ( Dimension obj )
2025-08-27 19:56:49 +08:00
{
2025-08-30 00:26:27 +08:00
Debug . Log ( $"[CameraControl] Init(Dimension obj) called. Focused Dimension: {(obj != null ? obj.name : " null ")}" ) ;
2025-08-27 19:56:49 +08:00
2025-08-30 00:26:27 +08:00
dimensionList = Program . Instance . Dimensions ;
Debug . Log ( $"[CameraControl] Dimension list updated. Count: {(dimensionList != null ? dimensionList.Length.ToString() : " null ")}" ) ;
if ( ! CurrentCamera ) // 如果摄像机仍未找到,记录错误并返回
2025-08-27 19:56:49 +08:00
{
2025-08-30 00:26:27 +08:00
Debug . LogError ( "[CameraControl] No camera found! CameraControl functionality will not be fully initialized." ) ;
2025-08-29 11:45:12 +08:00
return ;
2025-08-27 19:56:49 +08:00
}
2025-08-30 00:26:27 +08:00
// 处理 obj 为 null 的情况 - 此时 dimensionList 已更新
2025-08-29 11:45:12 +08:00
if ( ! obj )
2025-08-27 19:56:49 +08:00
{
2025-08-30 00:26:27 +08:00
Debug . LogWarning ( "[CameraControl] Init method called with a null focused dimension. Dimension list updated, but camera state not set based on a specific dimension." ) ;
2025-08-29 11:45:12 +08:00
return ;
}
// 根据当前聚焦维度同步 CameraControl 内部的 dimensionId
int focusedIndex = System . Array . IndexOf ( dimensionList , obj . name ) ;
if ( focusedIndex ! = - 1 )
{
dimensionId = focusedIndex ;
2025-08-30 00:26:27 +08:00
Debug . Log ( $"[CameraControl] Focused dimension '{obj.name}' found at index {dimensionId}." ) ;
2025-08-27 19:56:49 +08:00
}
else
{
2025-08-30 00:26:27 +08:00
Debug . LogWarning ( $"[CameraControl] Focused dimension '{obj.name}' not found in the dimension list. Falling back to ID 0." ) ;
2025-08-29 11:45:12 +08:00
dimensionId = 0 ; // 找不到时,回退到第一个维度,避免数组越界
2025-08-27 19:56:49 +08:00
}
2025-08-29 11:45:12 +08:00
2025-08-30 00:26:27 +08:00
// 设置摄像机位置到当前聚焦维度的位置
if ( Program . Instance . FocusedDimension ! = null )
{
CurrentCamera . transform . position = Program . Instance . FocusedDimension . cameraPosition ;
Debug . Log ( $"[CameraControl] Camera position set to focused dimension's camera position: {CurrentCamera.transform.position}" ) ;
}
else
{
Debug . LogWarning ( "[CameraControl] Focused dimension is null when trying to set camera position. Camera position not explicitly updated based on a dimension." ) ;
}
2025-08-29 11:45:12 +08:00
}
/// <summary>
/// 调用带参数的 Init 方法,使用 Program.Instance 中当前聚焦的维度。
/// </summary>
private void Init ( )
{
2025-08-30 00:26:27 +08:00
Debug . Log ( "[CameraControl] Init() called (no parameters). Calling Init(Program.Instance.FocusedDimension)." ) ;
2025-08-29 11:45:12 +08:00
Init ( Program . Instance . FocusedDimension ) ;
2025-08-27 19:56:49 +08:00
}
2025-08-29 11:45:12 +08:00
/// <summary>
/// 切换到下一个维度。
/// 会保存当前摄像机位置到当前维度,然后加载下一个维度的摄像机位置。
/// </summary>
2025-08-27 19:56:49 +08:00
public void NextDimension ( )
{
2025-08-30 00:26:27 +08:00
Debug . Log ( "[CameraControl] NextDimension called." ) ;
if ( ! CurrentCamera )
2025-08-27 19:56:49 +08:00
{
2025-08-30 00:26:27 +08:00
Debug . LogWarning ( "[CameraControl] Camera reference is null, cannot switch dimensions." ) ;
2025-08-27 19:56:49 +08:00
return ;
}
if ( dimensionList = = null | | dimensionList . Length = = 0 )
{
2025-08-30 00:26:27 +08:00
Debug . LogWarning ( "[CameraControl] Dimension list is empty or uninitialized, cannot switch dimensions." ) ;
2025-08-27 19:56:49 +08:00
return ;
}
2025-08-30 00:26:27 +08:00
// 1. 保存当前摄像机的实际位置到当前维度
2025-08-27 19:56:49 +08:00
if ( dimensionId > = 0 & & dimensionId < dimensionList . Length )
{
var currentDimension = Program . Instance . GetDimension ( dimensionList [ dimensionId ] ) ;
2025-08-30 00:26:27 +08:00
if ( currentDimension ! = null )
2025-08-27 19:56:49 +08:00
{
2025-08-30 00:26:27 +08:00
currentDimension . cameraPosition = CurrentCamera . transform . position ;
Debug . Log ( $"[CameraControl] Saved current camera position {CurrentCamera.transform.position} to dimension '{dimensionList[dimensionId]}'." ) ;
2025-08-27 19:56:49 +08:00
}
else
{
2025-08-30 00:26:27 +08:00
Debug . LogWarning ( $"[CameraControl] Could not find Dimension object for ID {dimensionId} ({dimensionList[dimensionId]}), cannot save camera position." ) ;
2025-08-27 19:56:49 +08:00
}
}
else
{
2025-08-30 00:26:27 +08:00
Debug . LogWarning ( $"[CameraControl] Current dimension ID ({dimensionId}) is out of range, cannot save camera position (Dimension list length: {dimensionList.Length})." ) ;
2025-08-27 19:56:49 +08:00
}
2025-08-30 00:26:27 +08:00
// 2. 更新 dimensionId, 形成循环切换
2025-08-27 19:56:49 +08:00
dimensionId = ( dimensionId + 1 ) % dimensionList . Length ;
2025-08-30 00:26:27 +08:00
Debug . Log ( $"[CameraControl] Updated dimensionId to {dimensionId}." ) ;
2025-08-27 19:56:49 +08:00
2025-08-29 11:45:12 +08:00
// 3. 更新聚焦维度,摄像机位置的更新将由事件触发的 Init 方法处理
2025-08-30 00:26:27 +08:00
SetFocusedDimensionById ( dimensionId ) ;
2025-08-27 19:56:49 +08:00
}
2025-08-29 11:45:12 +08:00
/// <summary>
/// 设置 Program.Instance 中聚焦的维度。
/// 摄像机位置的实际更新将通过 Program.Instance.OnFocusedDimensionChanged 事件在 Init(Dimension obj) 方法中处理。
/// </summary>
/// <param name="id">要设置的维度ID。</param>
2025-08-30 00:26:27 +08:00
private void SetFocusedDimensionById ( int id )
2025-08-27 19:56:49 +08:00
{
2025-08-30 00:26:27 +08:00
Debug . Log ( $"[CameraControl] SetFocusedDimensionById called with ID: {id}." ) ;
if ( ! CurrentCamera )
2025-08-07 16:44:43 +08:00
{
2025-08-30 00:26:27 +08:00
Debug . LogWarning ( "[CameraControl] Camera reference is null, cannot set camera position." ) ;
2025-08-27 19:56:49 +08:00
return ;
}
if ( dimensionList = = null | | id < 0 | | id > = dimensionList . Length )
{
2025-08-30 00:26:27 +08:00
Debug . LogWarning ( $"[CameraControl] Dimension ID {id} is out of range or dimension list is empty, cannot set focused dimension." ) ;
2025-08-27 19:56:49 +08:00
return ;
}
2025-08-29 11:45:12 +08:00
Program . Instance . SetFocusedDimension ( dimensionList [ id ] ) ;
2025-08-30 00:26:27 +08:00
Debug . Log ( $"[CameraControl] Program.Instance.SetFocusedDimension called for '{dimensionList[id]}'." ) ;
2025-08-07 16:44:43 +08:00
}
2025-08-27 19:56:49 +08:00
2025-08-29 11:45:12 +08:00
/// <summary>
/// 在游戏循环中每帧调用,用于处理摄像机跟随聚焦实体和维度切换。
/// </summary>
2025-07-21 13:58:58 +08:00
public void Tick ( )
{
2025-08-30 00:26:27 +08:00
if ( ! CurrentCamera ) return ; // 确保相机存在
2025-08-27 19:56:49 +08:00
2025-08-30 00:26:27 +08:00
// 当没有拖拽且存在聚焦实体时,摄像机跟随聚焦实体
2025-08-29 11:45:12 +08:00
if ( ! _isDragging & & Program . Instance . FocusedEntity )
2025-08-07 16:44:43 +08:00
{
var targetPosition = new Vector3 (
2025-08-27 19:56:49 +08:00
Program . Instance . FocusedEntity . Position . x ,
Program . Instance . FocusedEntity . Position . y ,
2025-08-30 00:26:27 +08:00
CurrentCamera . transform . position . z ) ;
2025-08-07 16:44:43 +08:00
2025-08-29 11:45:12 +08:00
// 使用 deltaTime 进行平滑的摄像机跟随
2025-08-30 00:26:27 +08:00
CurrentCamera . transform . position = Vector3 . Lerp (
CurrentCamera . transform . position ,
2025-08-07 16:44:43 +08:00
targetPosition ,
2025-08-27 19:56:49 +08:00
Time . deltaTime * _focusLerpSpeed ) ;
2025-08-07 16:44:43 +08:00
}
2025-08-29 11:45:12 +08:00
// 按下 Tab 键时切换到下一个维度
2025-08-27 19:56:49 +08:00
if ( Input . GetKeyDown ( KeyCode . Tab ) )
{
2025-08-30 00:26:27 +08:00
Debug . Log ( "[CameraControl] Tab key pressed. Calling NextDimension()." ) ;
2025-08-27 19:56:49 +08:00
NextDimension ( ) ;
}
}
2025-08-29 11:45:12 +08:00
/// <summary>
/// 在UI更新循环中每帧调用, 用于处理用户界面的摄像机操作, 如鼠标拖拽和缩放。
/// </summary>
2025-08-07 16:44:43 +08:00
public void TickUI ( )
{
2025-08-30 00:26:27 +08:00
if ( ! CurrentCamera ) // 确保相机存在
2025-08-27 19:56:49 +08:00
return ;
2025-08-29 11:45:12 +08:00
HandleMiddleMouseDrag ( ) ; // 处理鼠标中键拖拽
HandleMouseZoom ( ) ; // 处理鼠标滚轮缩放
2025-08-27 19:56:49 +08:00
}
2025-08-29 11:45:12 +08:00
/// <summary>
/// 设置摄像机的世界坐标位置。
/// </summary>
/// <param name="position">要设置的摄像机位置。</param>
2025-08-27 19:56:49 +08:00
public void SetPosition ( Vector3 position )
{
2025-08-30 00:26:27 +08:00
if ( CurrentCamera )
{
CurrentCamera . transform . position = position ;
Debug . Log ( $"[CameraControl] Camera position explicitly set to: {position}." ) ;
}
2025-08-27 19:56:49 +08:00
else
2025-08-30 00:26:27 +08:00
{
Debug . LogWarning ( "[CameraControl] Camera reference is null, cannot set position." ) ;
}
2025-07-21 13:58:58 +08:00
}
2025-08-07 16:44:43 +08:00
2025-08-29 11:45:12 +08:00
/// <summary>
/// 处理鼠标中键拖拽摄像机的逻辑。
/// </summary>
2025-08-07 16:44:43 +08:00
private void HandleMiddleMouseDrag ( )
{
2025-08-30 00:26:27 +08:00
if ( ! CurrentCamera ) return ; // 确保相机存在
2025-08-27 19:56:49 +08:00
2025-08-29 11:45:12 +08:00
// 开始拖拽:检测鼠标中键按下
if ( Input . GetMouseButtonDown ( 2 ) ) // 鼠标中键
2025-08-07 16:44:43 +08:00
{
2025-08-30 00:26:27 +08:00
_dragOrigin = CurrentCamera . ScreenToWorldPoint ( Input . mousePosition ) ;
2025-08-07 16:44:43 +08:00
_isDragging = true ;
2025-08-30 00:26:27 +08:00
Debug . Log ( $"[CameraControl] Mouse middle button down. Starting drag from world point: {_dragOrigin}." ) ;
// 如果有聚焦实体,则在开始拖拽时取消聚焦,暂停跟随
2025-08-27 19:56:49 +08:00
if ( Program . Instance . FocusedEntity )
{
2025-08-30 00:26:27 +08:00
Debug . Log ( "[CameraControl] Focused entity detected. Setting FocusedEntity to null to pause follow." ) ;
2025-08-27 19:56:49 +08:00
Program . Instance . SetFocusedEntity ( null ) ;
}
2025-08-07 16:44:43 +08:00
}
2025-08-29 11:45:12 +08:00
// 拖拽中:根据鼠标移动更新摄像机位置
2025-08-07 16:44:43 +08:00
if ( Input . GetMouseButton ( 2 ) & & _isDragging )
{
2025-08-30 00:26:27 +08:00
var difference = _dragOrigin - CurrentCamera . ScreenToWorldPoint ( Input . mousePosition ) ;
CurrentCamera . transform . position + = difference ;
2025-08-07 16:44:43 +08:00
}
2025-08-29 11:45:12 +08:00
// 结束拖拽:检测鼠标中键抬起
2025-08-07 16:44:43 +08:00
if ( Input . GetMouseButtonUp ( 2 ) )
{
_isDragging = false ;
2025-08-30 00:26:27 +08:00
Debug . Log ( "[CameraControl] Mouse middle button up. Dragging ended." ) ;
2025-08-07 16:44:43 +08:00
}
}
2025-08-29 11:45:12 +08:00
/// <summary>
/// 处理鼠标滚轮缩放摄像机的逻辑。
/// </summary>
2025-08-07 16:44:43 +08:00
private void HandleMouseZoom ( )
{
2025-08-30 00:26:27 +08:00
if ( ! CurrentCamera ) return ; // 确保相机存在
2025-08-27 19:56:49 +08:00
2025-08-07 16:44:43 +08:00
var scroll = Input . GetAxis ( "Mouse ScrollWheel" ) ;
2025-08-29 11:45:12 +08:00
if ( scroll = = 0 ) return ; // 没有滚轮滚动,则返回
// 根据滚轮输入调整正交摄像机的尺寸,并限制在最小和最大缩放之间
2025-08-30 00:26:27 +08:00
var newSize = CurrentCamera . orthographicSize - scroll * _zoomSpeed ;
CurrentCamera . orthographicSize = Mathf . Clamp ( newSize , _minZoom , _maxZoom ) ;
// Debug.Log($"[CameraControl] Mouse scroll wheel. New orthographicSize: {CurrentCamera.orthographicSize}"); // Too frequent for general logging
2025-08-29 11:45:12 +08:00
}
2025-07-21 13:58:58 +08:00
}
2025-08-27 19:56:49 +08:00
}