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