(client) feat:添加游玩时UI相关贴图,添加3d模型场景按钮,添加多级调色进度条,添加SVG图片包,添加事件定义以及管理器,添加音频管理器,定义部分怪物,添加通信协议定义;fix:修复维度切换错误,修复LogUI显示不正确 (#55)

Co-authored-by: m0_75251201 <m0_75251201@noreply.gitcode.com>
Reviewed-on: #55
This commit is contained in:
2025-09-03 19:59:22 +08:00
parent 450b15e4df
commit 78849e0cc5
208 changed files with 16296 additions and 2228 deletions

View File

@ -1,233 +1,288 @@
using Base;
using Map;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace CameraControl
{
public class CameraControl : Utils.MonoSingleton<CameraControl>, ITick, ITickUI
/// <summary>
/// 控制游戏摄像机的移动、缩放和维度切换。
/// 继承自 MonoSingleton 以确保场景中只有一个实例,并实现 ITick 接口以在游戏循环中更新。
/// </summary>
public class CameraControl : Utils.MonoSingleton<CameraControl>, ITick
{
// Camera movement variables
[SerializeField] private float _zoomSpeed = 5f;
[SerializeField] private float _minZoom = 2f;
[SerializeField] private float _maxZoom = 20f;
[SerializeField] private float _focusLerpSpeed = 5f;
// 摄像机移动相关变量
[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 = false;
private Vector3 _dragOrigin; // 拖拽操作的起始世界坐标
private bool _isDragging; // 标记摄像机是否正在被拖拽
private Camera _camera;
private Camera _cachedCamera; // 缓存的场景摄像机引用
private int dimensionId;
private string[] dimensionList;
private void OnDestroy()
/// <summary>
/// 获取当前生效的场景摄像机。懒加载并缓存。
/// </summary>
public Camera CurrentCamera
{
// 移除事件监听
SceneManager.sceneLoaded -= OnSceneLoaded;
get
{
if (!_cachedCamera)
{
_cachedCamera = Camera.main; // 尝试获取主摄像机
if (!_cachedCamera) // 如果主摄像机不存在或未标记,则尝试查找场景中的第一个摄像机
{
Debug.LogWarning("[CameraControl] 未找到主摄像机正在尝试查找场景中第一个Camera类型的对象。");
_cachedCamera = FindFirstObjectByType<Camera>();
}
if (!_cachedCamera)
{
Debug.LogError("[CameraControl] 场景中未找到可用的摄像机!摄像机控制功能将受限。");
}
}
return _cachedCamera;
}
}
private int dimensionId; // 当前摄像机控制器关注的维度索引
private string[] dimensionList; // 维度名称列表
/// <summary>
/// MonoSingleton 的 OnStart 方法,在单例首次创建并激活时调用,早于普通的 Start 方法。
/// 用于初始化摄像机缓存。
/// </summary>
protected override void OnStart()
{
// 注册场景加载事件。请注意MonoSingleton中的OnStart通常由Awake调用。
// 真正的Init调用已移至Start方法以确保Program.Instance已完成其Awake阶段的初始化。
SceneManager.sceneLoaded += OnSceneLoaded;
Program.Instance.OnFocusedDimensionChanged += Init;
}
private void Start()
/// <summary>
/// 当脚本实例被销毁时调用。
/// 用于取消订阅 Program.Instance 的事件,防止内存泄漏。
/// </summary>
private void OnDestroy()
{
// 在Start中调用Init确保所有依赖的单例Program.Instance已完成Awake阶段的初始化。
Init();
}
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
// 场景加载完成后调用 Init 方法,重新获取相机引用并初始化维度状态。
// 这确保了如果CameraControl是DontDestroyOnLoad在切换场景后也能正确工作。
Init();
Program.Instance.OnFocusedDimensionChanged -= Init;
}
private void Init()
/// <summary>
/// 根据指定的维度对象初始化摄像机控制器。
/// 主要用于在聚焦维度改变时更新摄像机状态和内部的维度ID。
/// </summary>
/// <param name="obj">当前聚焦的维度对象。</param>
private void Init(Dimension obj)
{
// 确保相机引用有效
if (_camera == null)
{
_camera = Camera.main;
if (_camera == null)
{
_camera = FindFirstObjectByType<Camera>();
}
}
if (_camera == null)
{
Debug.LogError("No Camera found in the scene! CameraControl functionalities will be limited.");
return; // 如果没有相机,则无法进行后续初始化
}
// 初始化维度数据 (假设 Program.Instance 总是已初始化)
dimensionId = 0; // 默认从第一个维度开始
dimensionList = Program.Instance.Dimensions;
if (dimensionList != null && dimensionList.Length > 0)
if (!CurrentCamera) // 如果摄像机仍未找到,记录错误并返回
{
SetCameraPositionForDimension(0);
Debug.LogError("[CameraControl] 未找到摄像机!摄像机控制功能将无法完全初始化。");
return;
}
// 处理 obj 为 null 的情况 - 此时 dimensionList 已更新
if (!obj)
{
Debug.LogWarning("[CameraControl] Init方法在聚焦维度为空时调用。维度列表已更新但摄像机状态未基于特定维度设置。");
return;
}
// 根据当前聚焦维度同步 CameraControl 内部的 dimensionId
int focusedIndex = System.Array.IndexOf(dimensionList, obj.name);
if (focusedIndex != -1)
{
dimensionId = focusedIndex;
}
else
{
Debug.LogWarning("Dimension list is empty or null from Program.Instance. Cannot set initial camera position for dimensions.");
Debug.LogWarning($"[CameraControl] 聚焦维度 '{obj.name}' 未在维度列表中找到。回退到ID 0。");
dimensionId = 0; // 找不到时,回退到第一个维度,避免数组越界
}
// 设置摄像机位置到当前聚焦维度的位置
if (Program.Instance.FocusedDimension != null)
{
CurrentCamera.transform.position = Program.Instance.FocusedDimension.cameraPosition;
}
else
{
Debug.LogWarning("[CameraControl] 尝试设置摄像机位置时,聚焦维度为空。摄像机位置未明确根据维度更新。");
}
}
/// <summary>
/// 调用带参数的 Init 方法,使用 Program.Instance 中当前聚焦的维度。
/// </summary>
private void Init()
{
Init(Program.Instance.FocusedDimension);
}
/// <summary>
/// 切换到下一个维度。
/// 会保存当前摄像机位置到当前维度,然后加载下一个维度的摄像机位置。
/// </summary>
public void NextDimension()
{
if (_camera == null)
if (!CurrentCamera)
{
Debug.LogWarning("Camera reference is null, cannot switch dimensions.");
return;
}
if (dimensionList == null || dimensionList.Length == 0)
{
Debug.LogWarning("Dimension list is empty.");
Debug.LogWarning("[CameraControl] 摄像机引用为空,无法切换维度。");
return;
}
// 1. 保存当前相机的真实位置到当前维度 (假设 Program.Instance 总是已初始化)
// 维度ID范围检查仍然保留因为这是数组访问的安全保障
if (dimensionList == null || dimensionList.Length == 0)
{
Debug.LogWarning("[CameraControl] 维度列表为空或未初始化,无法切换维度。");
return;
}
// 1. 保存当前摄像机的实际位置到当前维度
if (dimensionId >= 0 && dimensionId < dimensionList.Length)
{
// Program.Instance 假设不会为 null
var currentDimension = Program.Instance.GetDimension(dimensionList[dimensionId]);
if (currentDimension != null)
{
currentDimension.cameraPosition = _camera.transform.position;
// Debug.Log($"Saved camera position {currentDimension.cameraPosition} for dimension {dimensionList[dimensionId]} (ID: {dimensionId})");
currentDimension.cameraPosition = CurrentCamera.transform.position;
}
else
{
Debug.LogWarning($"Could not find dimension object for ID {dimensionId} ({dimensionList[dimensionId]}) to save camera position.");
Debug.LogWarning(
$"[CameraControl] 无法找到ID为 {dimensionId} ({dimensionList[dimensionId]}) 的维度对象,无法保存摄像机位置。");
}
}
else
{
Debug.LogWarning($"Current dimensionId ({dimensionId}) is out of bounds for saving camera position (dimensionList length: {dimensionList.Length}).");
Debug.LogWarning(
$"[CameraControl] 当前维度ID ({dimensionId}) 超出范围,无法保存摄像机位置(维度列表长度: {dimensionList.Length})。");
}
// 2. 更新 dimensionId形成循环
// 2. 更新 dimensionId, 形成循环切换
dimensionId = (dimensionId + 1) % dimensionList.Length;
// 3. 更新相机位置到新维度
SetCameraPositionForDimension(dimensionId);
// 3. 更新聚焦维度,摄像机位置的更新将由事件触发的 Init 方法处理
SetFocusedDimensionById(dimensionId);
}
private void SetCameraPositionForDimension(int id)
/// <summary>
/// 设置 Program.Instance 中聚焦的维度。
/// 摄像机位置的实际更新将通过 Program.Instance.OnFocusedDimensionChanged 事件在 Init(Dimension obj) 方法中处理。
/// </summary>
/// <param name="id">要设置的维度ID。</param>
private void SetFocusedDimensionById(int id)
{
if (_camera == null)
if (!CurrentCamera)
{
Debug.LogWarning("Camera reference is null, cannot set camera position.");
Debug.LogWarning("[CameraControl] 摄像机引用为空,无法设置摄像机位置。");
return;
}
if (dimensionList == null || id < 0 || id >= dimensionList.Length)
{
Debug.LogWarning($"Dimension ID {id} is out of bounds or dimension list is null/empty.");
Debug.LogWarning($"[CameraControl] 维度ID {id} 超出范围或维度列表为空,无法设置聚焦维度。");
return;
}
// Program.Instance 假设不会为 null
// 确保获取到的 Dimension 对象不为 null
var dimension = Program.Instance.GetDimension(dimensionList[id]);
if (dimension)
{
Vector3 cameraPosition = dimension.cameraPosition;
_camera.transform.position = cameraPosition;
}
else
{
Debug.LogWarning($"Dimension object for ID {id} ({dimensionList[id]}) is null. Cannot set camera position.");
}
Program.Instance.SetFocusedDimension(dimensionList[id]);
}
/// <summary>
/// 在游戏循环中每帧调用,用于处理摄像机跟随聚焦实体和维度切换。
/// </summary>
public void Tick()
{
if (_camera == null) return; // 确保相机存在
if (!CurrentCamera) return; // 确保相机存在
// 假设 Program.Instance 总是已初始化
if (Program.Instance.FocusedEntity)
// 当没有拖拽且存在聚焦实体时,摄像机跟随聚焦实体
if (!_isDragging && Program.Instance.FocusedEntity)
{
var targetPosition = new Vector3(
Program.Instance.FocusedEntity.Position.x,
Program.Instance.FocusedEntity.Position.y,
_camera.transform.position.z);
CurrentCamera.transform.position.z);
// 使用 deltaTime 进行平滑的机跟随
_camera.transform.position = Vector3.Lerp(
_camera.transform.position,
// 使用 deltaTime 进行平滑的摄像机跟随
CurrentCamera.transform.position = Vector3.Lerp(
CurrentCamera.transform.position,
targetPosition,
Time.deltaTime * _focusLerpSpeed);
}
HandleMiddleMouseDrag(); // 处理鼠标中键拖拽
// HandleMouseZoom(); // 处理鼠标滚轮缩放
// 按下 Tab 键时切换到下一个维度
if (Input.GetKeyDown(KeyCode.Tab))
{
NextDimension();
}
}
public void TickUI()
{
if (!_camera) // 确保相机存在
return;
HandleMiddleMouseDrag();
// HandleMouseZoom();
}
/// <summary>
/// 设置摄像机的世界坐标位置。
/// </summary>
/// <param name="position">要设置的摄像机位置。</param>
public void SetPosition(Vector3 position)
{
if (_camera)
_camera.transform.position = position;
if (CurrentCamera)
{
CurrentCamera.transform.position = position;
}
else
Debug.LogWarning("Camera reference is null, cannot set position.");
{
Debug.LogWarning("[CameraControl] 摄像机引用为空,无法设置位置。");
}
}
/// <summary>
/// 处理鼠标中键拖拽摄像机的逻辑。
/// </summary>
private void HandleMiddleMouseDrag()
{
if (_camera == null) return; // 确保相机存在
if (!CurrentCamera) return; // 确保相机存在
// Start drag
if (Input.GetMouseButtonDown(2)) // Middle mouse button
// 开始拖拽:检测鼠标中键按下
if (Input.GetMouseButtonDown(2)) // 鼠标中键
{
_dragOrigin = _camera.ScreenToWorldPoint(Input.mousePosition);
_dragOrigin = CurrentCamera.ScreenToWorldPoint(Input.mousePosition);
_isDragging = true;
// 假设 Program.Instance 总是已初始化
// 如果有聚焦实体,则在开始拖拽时取消聚焦,暂停跟随
if (Program.Instance.FocusedEntity)
{
Program.Instance.FocusedEntity.PlayerControlled = false;
Program.Instance.SetFocusedEntity(null);
}
}
// During drag
// 拖拽中:根据鼠标移动更新摄像机位置
if (Input.GetMouseButton(2) && _isDragging)
{
var difference = _dragOrigin - _camera.ScreenToWorldPoint(Input.mousePosition);
_camera.transform.position += difference;
var difference = _dragOrigin - CurrentCamera.ScreenToWorldPoint(Input.mousePosition);
CurrentCamera.transform.position += difference;
}
// End drag
// 结束拖拽:检测鼠标中键抬起
if (Input.GetMouseButtonUp(2))
{
_isDragging = false;
}
}
/// <summary>
/// 处理鼠标滚轮缩放摄像机的逻辑。
/// </summary>
private void HandleMouseZoom()
{
if (_camera == null) return; // 确保相机存在
if (!CurrentCamera) return; // 确保相机存在
var scroll = Input.GetAxis("Mouse ScrollWheel");
if (scroll == 0) return;
var newSize = _camera.orthographicSize - scroll * _zoomSpeed;
_camera.orthographicSize = Mathf.Clamp(newSize, _minZoom, _maxZoom);
if (scroll == 0) return; // 没有滚轮滚动,则返回
// 根据滚轮输入调整正交摄像机的尺寸,并限制在最小和最大缩放之间
var newSize = CurrentCamera.orthographicSize - scroll * _zoomSpeed;
CurrentCamera.orthographicSize = Mathf.Clamp(newSize, _minZoom, _maxZoom);
}
}
}
}