Files
Gen_Hack-and-Slash-Roguelit…/Client/Assets/Scripts/CameraControl/CameraControl.cs

318 lines
14 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 Base;
using Map;
using UnityEngine;
namespace CameraControl
{
/// <summary>
/// 控制游戏摄像机的移动、缩放和维度切换。
/// 继承自 MonoSingleton 以确保场景中只有一个实例,并实现 ITick 和 ITickUI 接口以在游戏循环和UI循环中更新。
/// </summary>
public class CameraControl : Utils.MonoSingleton<CameraControl>, 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; // 缓存的场景摄像机引用
/// <summary>
/// 获取当前生效的场景摄像机。懒加载并缓存。
/// </summary>
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<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;
}
}
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;
}
/// <summary>
/// 当脚本实例被销毁时调用。
/// 用于取消订阅 Program.Instance 的事件,防止内存泄漏。
/// </summary>
private void OnDestroy()
{
Debug.Log("[CameraControl] OnDestroy called. Unsubscribing from OnFocusedDimensionChanged event.");
Program.Instance.OnFocusedDimensionChanged -= Init;
}
/// <summary>
/// 根据指定的维度对象初始化摄像机控制器。
/// 主要用于在聚焦维度改变时更新摄像机状态和内部的维度ID。
/// </summary>
/// <param name="obj">当前聚焦的维度对象。</param>
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.");
}
}
/// <summary>
/// 调用带参数的 Init 方法,使用 Program.Instance 中当前聚焦的维度。
/// </summary>
private void Init()
{
Debug.Log("[CameraControl] Init() called (no parameters). Calling Init(Program.Instance.FocusedDimension).");
Init(Program.Instance.FocusedDimension);
}
/// <summary>
/// 切换到下一个维度。
/// 会保存当前摄像机位置到当前维度,然后加载下一个维度的摄像机位置。
/// </summary>
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);
}
/// <summary>
/// 设置 Program.Instance 中聚焦的维度。
/// 摄像机位置的实际更新将通过 Program.Instance.OnFocusedDimensionChanged 事件在 Init(Dimension obj) 方法中处理。
/// </summary>
/// <param name="id">要设置的维度ID。</param>
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]}'.");
}
/// <summary>
/// 在游戏循环中每帧调用,用于处理摄像机跟随聚焦实体和维度切换。
/// </summary>
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();
}
}
/// <summary>
/// 在UI更新循环中每帧调用用于处理用户界面的摄像机操作如鼠标拖拽和缩放。
/// </summary>
public void TickUI()
{
if (!CurrentCamera) // 确保相机存在
return;
HandleMiddleMouseDrag(); // 处理鼠标中键拖拽
HandleMouseZoom(); // 处理鼠标滚轮缩放
}
/// <summary>
/// 设置摄像机的世界坐标位置。
/// </summary>
/// <param name="position">要设置的摄像机位置。</param>
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.");
}
}
/// <summary>
/// 处理鼠标中键拖拽摄像机的逻辑。
/// </summary>
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.");
}
}
/// <summary>
/// 处理鼠标滚轮缩放摄像机的逻辑。
/// </summary>
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
}
}
}