MeowmentDebugTool/Packages/com.bywaystudios.meowmentdebugtool@0.1.4/Runtime/UniversalDebugTool.cs
张宏博 14f9f3f3f2 init
2025-12-18 10:17:46 +08:00

764 lines
24 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 System.Reflection;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
/// <summary>
/// 通用调试工具 - 支持多标签页的调试界面系统
/// 默认分辨率: 1080x2340
/// </summary>
public class UniversalDebugTool : MonoBehaviour
{
#region
[Header("主窗口设置")]
[SerializeField] private RectTransform mainWindow;
[SerializeField] private Canvas canvas;
[Header("标签页系统")]
[SerializeField] private RectTransform tabButtonContainer;
[SerializeField] private RectTransform contentContainer;
[SerializeField] private GameObject tabButtonPrefab;
[SerializeField] private Button closeButton;
[SerializeField] private Color activeTabColor = Color.green;
[SerializeField] private Color inactiveTabColor = Color.gray;
[Header("悬浮按钮")]
[SerializeField] private GameObject floatingButton;
[SerializeField] private DraggableFloatingButton draggableComponent;
[Header("参数查看页面")]
[SerializeField] private GameObject parametersPage;
[SerializeField] private TMP_Text deviceInfoText;
[SerializeField] private TMP_Text systemInfoText;
[SerializeField] private ScrollRect parametersScrollRect;
[Header("自定义按钮页面")]
[SerializeField] private GameObject customButtonsPage;
[SerializeField] private RectTransform buttonContainer;
[SerializeField] private GameObject buttonPrefab;
[SerializeField] private ScrollRect buttonsScrollRect;
[Header("工具栏页面")]
[SerializeField] private GameObject toolbarPage;
[SerializeField] private Slider timeAdjustSlider;
[SerializeField] private TMP_Text timeAdjustValueText;
[SerializeField] private Button timeIncreaseButton;
[SerializeField] private Button timeDecreaseButton;
[Header("设置页面")]
[SerializeField] private GameObject settingsPage;
[SerializeField] private TMP_InputField widthInputField;
[SerializeField] private TMP_InputField heightInputField;
[SerializeField] private Button applyResolutionButton;
[SerializeField] private Button resetResolutionButton;
[SerializeField] private TMP_Text currentResolutionText;
[Header("输入对话框")]
[SerializeField] private GameObject inputDialog;
[SerializeField] private TMP_Text inputDialogTitle;
[SerializeField] private TMP_InputField inputDialogInputField;
[SerializeField] private Button inputDialogConfirmBtn;
[SerializeField] private Button inputDialogCancelBtn;
#endregion
#region
private static UniversalDebugTool instance;
private Dictionary<string, GameObject> pages = new Dictionary<string, GameObject>();
private Dictionary<string, Button> tabButtons = new Dictionary<string, Button>();
private string currentPageKey = "";
// 默认分辨率
private Vector2 defaultResolution = new Vector2(1080, 2340);
private Vector2 currentCustomResolution;
// 自定义按钮回调
private static Action<Button, TMP_Text> customButtonCallback;
#endregion
#region
public static UniversalDebugTool Instance
{
get
{
if (instance == null)
{
instance = FindObjectOfType<UniversalDebugTool>();
}
return instance;
}
}
public static bool InstanceExists => instance != null;
#endregion
#region Unity生命周期
private void Awake()
{
if (instance == null)
{
instance = this;
}
else if (instance != this)
{
Destroy(gameObject);
return;
}
currentCustomResolution = defaultResolution;
InitializeDebugTool();
}
private void Start()
{
UpdateDeviceInfo();
UpdateSystemInfo();
LoadCustomButtons();
// 初始状态:显示主窗口,隐藏悬浮按钮
ShowMainWindow();
}
private void OnDestroy()
{
if (instance == this)
{
instance = null;
}
}
#endregion
#region
private void InitializeDebugTool()
{
Debug.Log("🚀 初始化UniversalDebugTool...");
// 注册所有页面
RegisterPage("参数", parametersPage);
RegisterPage("自定义按钮", customButtonsPage);
RegisterPage("工具栏", toolbarPage);
RegisterPage("设置", settingsPage);
Debug.Log($"📄 已注册{pages.Count}个页面");
// 创建标签按钮
CreateTabButtons();
// 设置按钮事件
if (closeButton != null)
closeButton.onClick.AddListener(CloseDebugWindow);
if (applyResolutionButton != null)
applyResolutionButton.onClick.AddListener(ApplyCustomResolution);
if (resetResolutionButton != null)
resetResolutionButton.onClick.AddListener(ResetToDefaultResolution);
// 设置时间调整工具
if (timeAdjustSlider != null)
{
timeAdjustSlider.onValueChanged.AddListener(OnTimeAdjustValueChanged);
// 初始化显示
UpdateTimeAdjustDisplay(timeAdjustSlider.value);
}
if (timeIncreaseButton != null)
timeIncreaseButton.onClick.AddListener(OnIncreaseTime);
if (timeDecreaseButton != null)
timeDecreaseButton.onClick.AddListener(OnDecreaseTime);
// 设置悬浮按钮点击事件
if (floatingButton != null)
{
Button floatBtn = floatingButton.GetComponent<Button>();
if (floatBtn != null)
{
floatBtn.onClick.AddListener(OnFloatingButtonClick);
}
}
// 默认显示第一个页面
if (pages.Count > 0)
{
ShowPage("参数");
}
// 应用默认分辨率
ApplyResolution(defaultResolution);
Debug.Log("✅ UniversalDebugTool初始化完成");
Debug.Log("💡 提示: 点击窗口顶部的标签切换页面或按F1-F4使用快捷键");
}
private void RegisterPage(string pageName, GameObject pageObject)
{
if (pageObject != null && !pages.ContainsKey(pageName))
{
pages[pageName] = pageObject;
pageObject.SetActive(false);
}
}
private void CreateTabButtons()
{
if (tabButtonPrefab == null || tabButtonContainer == null)
{
Debug.LogError("❌ 标签按钮预制件或容器未设置!请重新生成预制件。");
return;
}
Debug.Log($"📋 开始创建{pages.Count}个标签按钮...");
foreach (var page in pages)
{
GameObject buttonObj = Instantiate(tabButtonPrefab, tabButtonContainer);
buttonObj.name = $"Tab_{page.Key}";
Button button = buttonObj.GetComponent<Button>();
TMP_Text buttonText = buttonObj.GetComponentInChildren<TMP_Text>();
if (buttonText != null)
{
buttonText.text = page.Key;
Debug.Log($"✅ 创建标签: [{page.Key}]");
}
else
{
Debug.LogWarning($"⚠️ 标签按钮 [{page.Key}] 缺少文本组件");
}
string pageName = page.Key; // 闭包捕获
button.onClick.AddListener(() => ShowPage(pageName));
tabButtons[page.Key] = button;
}
Debug.Log("✅ 标签按钮创建完成!");
}
#endregion
#region
/// <summary>
/// 显示指定的页面
/// </summary>
public void ShowPage(string pageName)
{
if (!pages.ContainsKey(pageName))
{
Debug.LogWarning($"页面 '{pageName}' 不存在!");
return;
}
// 隐藏所有页面
foreach (var page in pages.Values)
{
page.SetActive(false);
}
// 显示目标页面
pages[pageName].SetActive(true);
currentPageKey = pageName;
// 更新标签按钮状态
UpdateTabButtonStates(pageName);
}
private void UpdateTabButtonStates(string activePageName)
{
foreach (var kvp in tabButtons)
{
Image buttonImage = kvp.Value.GetComponent<Image>();
if (buttonImage != null)
{
buttonImage.color = kvp.Key == activePageName ? activeTabColor : inactiveTabColor;
}
}
}
#endregion
#region
private void UpdateDeviceInfo()
{
if (deviceInfoText == null) return;
string info = "===== 设备信息 =====\n";
info += $"设备名称: {SystemInfo.deviceName}\n";
info += $"设备型号: {SystemInfo.deviceModel}\n";
info += $"设备类型: {SystemInfo.deviceType}\n";
info += $"操作系统: {SystemInfo.operatingSystem}\n";
info += $"处理器: {SystemInfo.processorType}\n";
info += $"处理器核心数: {SystemInfo.processorCount}\n";
info += $"系统内存: {SystemInfo.systemMemorySize} MB\n";
info += $"显存: {SystemInfo.graphicsMemorySize} MB\n";
info += $"设备标识符: {SystemInfo.deviceUniqueIdentifier}\n";
deviceInfoText.text = info;
}
private void UpdateSystemInfo()
{
if (systemInfoText == null) return;
string info = "===== 系统信息 =====\n";
info += $"Unity版本: {Application.unityVersion}\n";
info += $"平台: {Application.platform}\n";
info += $"产品名称: {Application.productName}\n";
info += $"公司名称: {Application.companyName}\n";
info += $"版本: {Application.version}\n";
info += $"数据路径: {Application.dataPath}\n";
info += $"持久化数据路径: {Application.persistentDataPath}\n";
info += $"临时缓存路径: {Application.temporaryCachePath}\n";
info += $"屏幕分辨率: {Screen.width} x {Screen.height} @ {Screen.currentResolution.refreshRateRatio.value:F2}Hz\n";
info += $"屏幕DPI: {Screen.dpi}\n";
info += $"是否全屏: {Screen.fullScreen}\n";
info += $"目标帧率: {Application.targetFrameRate}\n";
info += $"当前帧率: {(int)(1f / Time.smoothDeltaTime)}\n";
info += $"\n===== 图形信息 =====\n";
info += $"图形设备名称: {SystemInfo.graphicsDeviceName}\n";
info += $"图形设备供应商: {SystemInfo.graphicsDeviceVendor}\n";
info += $"图形设备类型: {SystemInfo.graphicsDeviceType}\n";
info += $"图形设备版本: {SystemInfo.graphicsDeviceVersion}\n";
info += $"着色器等级: {SystemInfo.graphicsShaderLevel}\n";
info += $"多线程渲染: {SystemInfo.graphicsMultiThreaded}\n";
systemInfoText.text = info;
}
/// <summary>
/// 复制设备信息到剪贴板
/// </summary>
public void CopyDeviceInfoToClipboard()
{
if (deviceInfoText != null)
{
GUIUtility.systemCopyBuffer = deviceInfoText.text;
Debug.Log("设备信息已复制到剪贴板");
}
}
/// <summary>
/// 复制系统信息到剪贴板
/// </summary>
public void CopySystemInfoToClipboard()
{
if (systemInfoText != null)
{
GUIUtility.systemCopyBuffer = systemInfoText.text;
Debug.Log("系统信息已复制到剪贴板");
}
}
/// <summary>
/// 刷新所有信息
/// </summary>
public void RefreshAllInfo()
{
UpdateDeviceInfo();
UpdateSystemInfo();
}
#endregion
#region
/// <summary>
/// 加载所有自定义按钮(使用反射)
/// </summary>
private void LoadCustomButtons()
{
if (buttonContainer == null || buttonPrefab == null)
{
Debug.LogWarning("按钮容器或按钮预制件未设置");
return;
}
// 清空现有按钮
foreach (Transform child in buttonContainer)
{
Destroy(child.gameObject);
}
// 查找所有标记为DebugButton的方法
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (var assembly in assemblies)
{
try
{
var types = assembly.GetTypes();
foreach (var type in types)
{
var methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
foreach (var method in methods)
{
var attribute = method.GetCustomAttribute<DebugButtonAttribute>();
if (attribute != null)
{
CreateCustomButton(method, attribute);
}
}
}
}
catch (Exception e)
{
// 某些程序集可能无法访问,跳过
Debug.LogWarning($"无法访问程序集 {assembly.FullName}: {e.Message}");
}
}
}
private void CreateCustomButton(MethodInfo method, DebugButtonAttribute attribute)
{
GameObject buttonObj = Instantiate(buttonPrefab, buttonContainer);
Button button = buttonObj.GetComponent<Button>();
TMP_Text buttonText = buttonObj.GetComponentInChildren<TMP_Text>();
Image buttonImage = buttonObj.GetComponent<Image>();
// 设置按钮文本
if (buttonText != null)
{
buttonText.text = string.IsNullOrEmpty(attribute.DisplayName) ? method.Name : attribute.DisplayName;
}
// 设置按钮颜色
if (buttonImage != null && attribute.ButtonColor != default(Color))
{
buttonImage.color = attribute.ButtonColor;
}
// 设置按钮点击事件
button.onClick.AddListener(() =>
{
try
{
method.Invoke(null, null);
Debug.Log($"执行调试方法: {method.Name}");
// 调用回调
customButtonCallback?.Invoke(button, buttonText);
customButtonCallback = null;
}
catch (Exception e)
{
Debug.LogError($"执行调试方法 {method.Name} 时出错: {e.Message}");
}
});
}
/// <summary>
/// 设置自定义按钮回调
/// </summary>
public static void SetCustomButtonCallback(Action<Button, TMP_Text> callback)
{
customButtonCallback = callback;
}
/// <summary>
/// 重新加载自定义按钮
/// </summary>
public void ReloadCustomButtons()
{
LoadCustomButtons();
}
#endregion
#region
private float timeAdjustValue = 60f; // 当前设置的时间调整值(秒)
/// <summary>
/// 时间调整Slider值改变回调只更新显示不改变时间
/// </summary>
private void OnTimeAdjustValueChanged(float value)
{
timeAdjustValue = value;
UpdateTimeAdjustDisplay(value);
}
/// <summary>
/// 更新时间调整显示
/// </summary>
private void UpdateTimeAdjustDisplay(float seconds)
{
if (timeAdjustValueText != null)
{
if (seconds < 60)
{
timeAdjustValueText.text = $"{seconds:F0}秒";
}
else
{
float minutes = seconds / 60f;
timeAdjustValueText.text = $"{minutes:F1}分钟";
}
}
}
/// <summary>
/// 增加时间
/// </summary>
private void OnIncreaseTime()
{
// 修改系统时间这里演示修改Time.time的偏移
float adjustSeconds = timeAdjustValue;
// 注意Time.time本身无法直接修改这里提供一个可以被重写的方法
// 实际项目中,你应该修改游戏内的时间变量
AdjustGameTime(adjustSeconds);
Debug.Log($"⏰ 增加时间: +{adjustSeconds}秒");
}
/// <summary>
/// 减少时间
/// </summary>
private void OnDecreaseTime()
{
float adjustSeconds = -timeAdjustValue;
AdjustGameTime(adjustSeconds);
Debug.Log($"⏰ 减少时间: {adjustSeconds}秒");
}
/// <summary>
/// 调整游戏时间(示例方法,需要根据实际项目修改)
/// </summary>
private void AdjustGameTime(float seconds)
{
// 方案1: 如果你的项目有全局时间管理器
// TimeManager.Instance?.AddTime(seconds);
// 方案2: 如果你使用DateTime
// GameTime.Current = GameTime.Current.AddSeconds(seconds);
// 方案3: 临时演示 - 修改Time.timeScale来模拟时间变化
// 实际项目中应该修改你自己的时间变量
Debug.Log($"💡 提示: 请在AdjustGameTime方法中实现你的时间调整逻辑");
Debug.Log($"💡 建议: 修改游戏内的时间变量,例如 GameTime.Current.AddSeconds({seconds})");
// 示例:如果你有一个静态的游戏时间偏移量
// GameTimeOffset += seconds;
}
#endregion
#region
private void ApplyCustomResolution()
{
if (widthInputField == null || heightInputField == null) return;
if (float.TryParse(widthInputField.text, out float width) &&
float.TryParse(heightInputField.text, out float height))
{
if (width > 0 && height > 0)
{
currentCustomResolution = new Vector2(width, height);
ApplyResolution(currentCustomResolution);
Debug.Log($"已应用自定义分辨率: {width} x {height}");
}
else
{
Debug.LogWarning("分辨率值必须大于0");
}
}
else
{
Debug.LogWarning("无效的分辨率值");
}
}
private void ResetToDefaultResolution()
{
currentCustomResolution = defaultResolution;
ApplyResolution(defaultResolution);
if (widthInputField != null)
widthInputField.text = defaultResolution.x.ToString();
if (heightInputField != null)
heightInputField.text = defaultResolution.y.ToString();
Debug.Log($"已重置为默认分辨率: {defaultResolution.x} x {defaultResolution.y}");
}
private void ApplyResolution(Vector2 resolution)
{
if (mainWindow != null)
{
mainWindow.sizeDelta = resolution;
}
if (currentResolutionText != null)
{
currentResolutionText.text = $"当前窗口尺寸: {resolution.x} x {resolution.y}";
}
// 强制刷新布局
if (canvas != null)
{
Canvas.ForceUpdateCanvases();
}
}
#endregion
#region
/// <summary>
/// 显示输入对话框
/// </summary>
public static void ShowInputDialog(string title, Action<string> onConfirmAction,
string initialValue = "", TMP_InputField.ContentType contentType = TMP_InputField.ContentType.Standard)
{
if (!InstanceExists) return;
var inst = Instance;
if (inst.inputDialog == null) return;
inst.inputDialogInputField.contentType = contentType;
inst.inputDialog.SetActive(true);
inst.inputDialogTitle.text = title;
inst.inputDialogInputField.text = initialValue;
inst.inputDialogConfirmBtn.onClick.RemoveAllListeners();
inst.inputDialogConfirmBtn.onClick.AddListener(() =>
{
onConfirmAction?.Invoke(inst.inputDialogInputField.text);
CloseInputDialog();
});
inst.inputDialogCancelBtn.onClick.RemoveAllListeners();
inst.inputDialogCancelBtn.onClick.AddListener(CloseInputDialog);
}
/// <summary>
/// 关闭输入对话框
/// </summary>
public static void CloseInputDialog()
{
if (!InstanceExists) return;
var inst = Instance;
if (inst.inputDialog == null) return;
inst.inputDialog.SetActive(false);
inst.inputDialogInputField.text = "";
inst.inputDialogConfirmBtn.onClick.RemoveAllListeners();
inst.inputDialogCancelBtn.onClick.RemoveAllListeners();
}
#endregion
#region API
/// <summary>
/// 显示调试工具
/// </summary>
public static void Show()
{
if (InstanceExists)
{
Instance.gameObject.SetActive(true);
}
}
/// <summary>
/// 隐藏调试工具
/// </summary>
public static void Hide()
{
if (InstanceExists)
{
Instance.gameObject.SetActive(false);
}
}
/// <summary>
/// 切换调试工具显示状态
/// </summary>
public static void Toggle()
{
if (InstanceExists)
{
if (Instance.mainWindow != null && Instance.mainWindow.gameObject.activeSelf)
{
Instance.CloseDebugWindow();
}
else
{
Instance.OpenDebugWindow();
}
}
}
/// <summary>
/// 关闭调试窗口,显示悬浮按钮
/// </summary>
public void CloseDebugWindow()
{
if (mainWindow != null)
mainWindow.gameObject.SetActive(false);
if (floatingButton != null)
floatingButton.SetActive(true);
Debug.Log("🔽 调试窗口已关闭");
}
/// <summary>
/// 悬浮按钮点击回调(检查是否为拖动)
/// </summary>
private void OnFloatingButtonClick()
{
// 检查是否刚拖动过
if (draggableComponent != null && draggableComponent.GetWasDragging())
{
// 如果刚拖动过,不打开窗口
Debug.Log("🚫 拖动操作,不打开窗口");
return;
}
// 否则打开窗口
OpenDebugWindow();
}
/// <summary>
/// 打开调试窗口,隐藏悬浮按钮
/// </summary>
public void OpenDebugWindow()
{
if (floatingButton != null)
floatingButton.SetActive(false);
if (mainWindow != null)
mainWindow.gameObject.SetActive(true);
Debug.Log("🔼 调试窗口已打开");
}
/// <summary>
/// 显示主窗口(初始化时调用)
/// </summary>
private void ShowMainWindow()
{
if (mainWindow != null)
mainWindow.gameObject.SetActive(true);
if (floatingButton != null)
floatingButton.SetActive(false);
}
#endregion
}
#region
/// <summary>
/// 调试按钮特性 - 标记方法为调试按钮
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class DebugButtonAttribute : Attribute
{
public string DisplayName { get; set; }
public Color ButtonColor { get; set; }
public DebugButtonAttribute(string displayName = "", float r = 0.8f, float g = 0.8f, float b = 0.8f)
{
DisplayName = displayName;
ButtonColor = new Color(r, g, b, 1f);
}
}
#endregion