using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
namespace MeowmentDebugTool
{
///
/// 通用调试工具 - 支持多标签页的调试界面系统
/// 默认分辨率: 1080x2340
/// 使用方法:
/// 1. 场景中放置 UniversalDebugTool 预制件
/// 2. 在代码中调用 UniversalDebugTool.Init() 初始化
/// 3. 未调用 Init() 前不会显示任何UI
///
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 customCheckBoxesPage;
[SerializeField] private RectTransform checkBoxContainer;
[SerializeField] private GameObject checkBoxPrefab;
[SerializeField] private ScrollRect checkBoxesScrollRect;
[Header("数值页面")]
[SerializeField] private GameObject customValuesPage;
[SerializeField] private RectTransform valueContainer;
[SerializeField] private GameObject valuePrefab;
[SerializeField] private ScrollRect valuesScrollRect;
[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;
[SerializeField] private TMP_InputField infoBufferInputField;
[SerializeField] private TMP_InputField warningBufferInputField;
[SerializeField] private TMP_InputField errorBufferInputField;
[SerializeField] private TMP_InputField fatalBufferInputField;
[SerializeField] private Button applyBufferButton;
[SerializeField] private Button resetBufferButton;
[Header("控制台页面")]
[SerializeField] private GameObject consolePage;
[SerializeField] private ScrollRect consoleLogScrollRect;
[SerializeField] private RectTransform consoleLogContent;
[SerializeField] private ScrollRect consoleDetailScrollRect;
[SerializeField] private TMP_Text consoleDetailText;
[SerializeField] private Button consoleClearButton;
[SerializeField] private Toggle consoleLockScrollToggle;
[SerializeField] private Toggle consoleInfoFilterToggle;
[SerializeField] private Toggle consoleWarningFilterToggle;
[SerializeField] private Toggle consoleErrorFilterToggle;
[SerializeField] private Toggle consoleFatalFilterToggle;
[SerializeField] private TMP_InputField consoleTextFilterInputField;
[SerializeField] private GameObject consoleLogItemPrefab;
[Header("输入对话框")]
[SerializeField] private GameObject inputDialog;
[SerializeField] private TMP_Text inputDialogTitle;
[SerializeField] private TMP_Text inputDialogDescription;
[SerializeField] private RectTransform inputDialogFieldContainer;
[SerializeField] private Button inputDialogConfirmBtn;
[SerializeField] private Button inputDialogCancelBtn;
#endregion
#region 私有变量
private static UniversalDebugTool instance;
private static bool isInitialized = false;
private Dictionary pages = new Dictionary();
private Dictionary tabButtons = new Dictionary();
private string currentPageKey = "";
// Canvas层级设置
private const int TOP_SORT_ORDER = 30000;
// 保存的SDF字体资源
private static TMP_FontAsset savedFontAsset = null;
// 模块实例
private ParametersModule parametersModule;
private CustomButtonsModule customButtonsModule;
private CustomCheckBoxesModule customCheckBoxesModule;
private CustomValuesModule customValuesModule;
private SettingsModule settingsModule;
private ConsoleModule consoleModule;
private List allModules = new List();
// 暂时隐藏状态标志
private bool isHiding = false;
// 四指点击检测
private bool wasFourFingerTouch = false;
private const float fourFingerTapTime = 0.3f; // 四指同时按下的时间阈值
private float fourFingerTouchStartTime = 0f;
// 键盘测试模式(用于测试,可以用F键代替四指点击)
private static bool useKeyboardTestMode = false; // 默认开启键盘测试模式 f
private readonly List inputDialogGeneratedObjects = new List();
#endregion
#region 单例
public static UniversalDebugTool Instance
{
get
{
if (instance == null)
{
instance = FindObjectOfType();
}
return instance;
}
}
public static bool InstanceExists => instance != null;
#endregion
#region Unity生命周期
private void Awake()
{
// 如果未初始化,直接销毁GameObject,不执行任何操作
if (!isInitialized)
{
Destroy(gameObject);
return;
}
if (instance == null)
{
instance = this;
DontDestroyOnLoad(gameObject);
}
else if (instance != this)
{
Destroy(gameObject);
return;
}
// 设置Canvas为最上层
SetCanvasToTop();
// 隐藏所有UI
HideAllUI();
// 注意:如果isInitialized为true,不在Awake中调用InitializeDebugTool
// 因为此时反射设置的字段可能还没生效,延迟到Start中调用
}
private void Start()
{
// 如果未初始化,不执行任何操作
if (!isInitialized)
return;
// 只有在已初始化但还没调用过InitializeDebugTool时才执行
// 这是通过Init()创建的实例,需要在Start中完成初始化
// 此时反射设置的字段已经生效
if (allModules.Count == 0)
{
InitializeDebugTool();
InitializeAllModules();
ShowMainWindow();
}
}
private void Update()
{
// 只有初始化后才执行任何逻辑
if (!isInitialized)
return;
// 键盘测试模式:按F键触发
if (useKeyboardTestMode && Input.GetKeyDown(KeyCode.F))
{
OnFourFingerTap();
}
// 四指点击检测已禁用,如需使用请在主程序中通过公共API控制显示/隐藏
// if (!useKeyboardTestMode)
// {
// DetectFourFingerTap();
// }
// 仅在控制台页实际显示时更新控制台UI
if (consoleModule != null && currentPageKey == "控制台" && mainWindow != null && mainWindow.gameObject.activeInHierarchy)
{
consoleModule.Update();
}
// 参数页 Profiler 实时刷新
if (parametersModule != null && currentPageKey == "参数" && mainWindow != null && mainWindow.gameObject.activeInHierarchy)
{
parametersModule.Update();
}
}
private void OnDestroy()
{
// 如果未初始化,不执行任何操作
if (!isInitialized)
return;
if (instance == this)
{
instance = null;
// 销毁控制台模块
if (consoleModule != null)
{
consoleModule.Shutdown();
}
}
}
#endregion
#region 初始化
///
/// 初始化调试工具(自动创建UI)
///
public static void Init()
{
if (isInitialized)
{
Debug.LogWarning("[MeowmentDebugTool] 已经初始化过了");
return;
}
isInitialized = true;
Debug.Log("[MeowmentDebugTool] 开始初始化调试工具...");
// 如果场景中没有实例,运行时自动创建UI
if (!InstanceExists)
{
Debug.Log("[MeowmentDebugTool] 运行时自动创建UI...");
instance = RuntimeUIGenerator.CreateDebugToolUI();
}
if (InstanceExists)
{
Instance.InitializeDebugTool();
Instance.InitializeAllModules();
Instance.ShowMainWindow();
Debug.Log("[MeowmentDebugTool] 初始化完成!");
}
}
///
/// 设置所有TextMeshProUGUI的SDF字体
///
/// TMP字体资源
public static void SetSDFFont(TMP_FontAsset fontAsset)
{
if (!isInitialized || !InstanceExists)
{
Debug.LogWarning("[MeowmentDebugTool] 调试工具未初始化,无法设置字体");
return;
}
if (fontAsset == null)
{
Debug.LogWarning("[MeowmentDebugTool] 字体资源为空");
return;
}
int count = 0;
// 1. 获取调试工具Canvas下的所有TextMeshProUGUI组件(包括隐藏的)
TextMeshProUGUI[] allTexts = Instance.GetComponentsInChildren(true);
foreach (var text in allTexts)
{
text.font = fontAsset;
count++;
}
// 2. 查找预制件模板(_TempPrefabHolder)下的TextMeshProUGUI
GameObject tempPrefabHolder = GameObject.Find("_TempPrefabHolder");
if (tempPrefabHolder != null)
{
TextMeshProUGUI[] prefabTexts = tempPrefabHolder.GetComponentsInChildren(true);
foreach (var text in prefabTexts)
{
text.font = fontAsset;
count++;
}
Debug.Log($"[MeowmentDebugTool] 已设置预制件模板中的 {prefabTexts.Length} 个文本组件");
}
Debug.Log($"[MeowmentDebugTool] 共将 {count} 个文本组件的字体设置为: {fontAsset.name}");
// 3. 保存字体资源,供后续创建的按钮使用
savedFontAsset = fontAsset;
Debug.Log("[MeowmentDebugTool] 字体资源已保存,后续创建的按钮将自动应用此字体");
// 4. 更新按钮模块字体,不再整页重建
if (Instance.customButtonsModule != null)
{
Debug.Log("[MeowmentDebugTool] 应用自定义按钮字体...");
Instance.customButtonsModule.SetSDFFont(fontAsset);
}
// 4.5. 更新自定义复选框字体,不再整页重建
if (Instance.customCheckBoxesModule != null)
{
Debug.Log("[MeowmentDebugTool] 应用自定义复选框字体...");
Instance.customCheckBoxesModule.SetSDFFont(fontAsset);
}
// 4.6. 更新数值模块字体,不再整页重建
if (Instance.customValuesModule != null)
{
Debug.Log("[MeowmentDebugTool] 应用数值模块字体...");
Instance.customValuesModule.SetSDFFont(fontAsset);
}
// 5. 为控制台模块应用字体
if (Instance.consoleModule != null)
{
Instance.consoleModule.SetSDFFont(fontAsset);
}
}
///
/// 设置Canvas为最上层
///
private void SetCanvasToTop()
{
if (canvas != null)
{
canvas.sortingOrder = TOP_SORT_ORDER;
canvas.overrideSorting = true;
Debug.Log($"[MeowmentDebugTool] Canvas层级设置为: {TOP_SORT_ORDER}");
}
}
///
/// 隐藏所有UI
///
private void HideAllUI()
{
if (mainWindow != null)
mainWindow.gameObject.SetActive(false);
if (floatingButton != null)
floatingButton.SetActive(false);
}
private void InitializeDebugTool()
{
Debug.Log("[MeowmentDebugTool] 初始化UniversalDebugTool...");
// 参数页按需初始化,避免影响启动速度
parametersModule = null;
if (parametersPage != null)
{
RegisterPage("参数", parametersPage);
}
customButtonsModule = new CustomButtonsModule(
customButtonsPage, buttonContainer, buttonPrefab, buttonsScrollRect, CloseDebugWindow);
customCheckBoxesModule = new CustomCheckBoxesModule(
customCheckBoxesPage, checkBoxContainer, checkBoxPrefab, checkBoxesScrollRect);
customValuesModule = new CustomValuesModule(
customValuesPage, valueContainer, valuePrefab, valuesScrollRect, CloseDebugWindow);
settingsModule = new SettingsModule(
settingsPage, widthInputField, heightInputField,
applyResolutionButton, resetResolutionButton, currentResolutionText,
infoBufferInputField, warningBufferInputField,
errorBufferInputField, fatalBufferInputField,
applyBufferButton, resetBufferButton,
mainWindow);
consoleModule = new ConsoleModule(
consolePage, consoleLogScrollRect, consoleLogContent,
consoleDetailScrollRect, consoleDetailText,
consoleClearButton, consoleLockScrollToggle,
consoleInfoFilterToggle, consoleWarningFilterToggle,
consoleErrorFilterToggle, consoleFatalFilterToggle,
consoleTextFilterInputField, consoleLogItemPrefab);
// 添加会显示在顶部标签栏的模块(顺序:控制台、按钮、开关、数值、设置)
allModules.Add(consoleModule);
allModules.Add(customButtonsModule);
allModules.Add(customCheckBoxesModule);
allModules.Add(customValuesModule);
allModules.Add(settingsModule);
// 注册所有页面
foreach (var module in allModules)
{
RegisterPage(module.GetModuleName(), module.GetPage());
}
Debug.Log($"[MeowmentDebugTool] 已注册{pages.Count}个页面");
// 创建标签按钮
CreateTabButtons();
// 设置按钮事件
if (closeButton != null)
closeButton.onClick.AddListener(CloseDebugWindow);
// 注意:浮窗点击事件现在由DraggableFloatingButton的OnPointerClick处理
// 默认页仅记录,不在初始化阶段真正构建页面内容
if (pages.Count > 0)
{
currentPageKey = pages.ContainsKey("按钮") ? "按钮" : pages.Keys.First();
}
Debug.Log("[OK] UniversalDebugTool初始化完成!");
Debug.Log("[MeowmentDebugTool] 提示: 点击窗口顶部的标签切换页面");
}
///
/// 初始化所有模块
///
private void InitializeAllModules()
{
Debug.Log("🔧 初始化所有模块...");
foreach (var module in allModules)
{
module.Initialize();
}
// 设置ConsoleModule引用到SettingsModule
if (settingsModule != null && consoleModule != null)
{
settingsModule.SetConsoleModule(consoleModule);
}
Debug.Log("[OK] 所有模块初始化完成!");
}
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)
{
string errorDetails = $"tabButtonPrefab={(tabButtonPrefab == null ? "null" : "已设置")}, " +
$"tabButtonContainer={(tabButtonContainer == null ? "null" : "已设置")}";
Debug.LogWarning($"⚠ [MeowmentDebugTool] 标签按钮预制件或容器未完全初始化:{errorDetails}");
Debug.LogWarning("[!] 这可能是Unity初始化顺序问题,如果标签正常显示则可以忽略此警告");
return;
}
Debug.Log($"[MeowmentDebugTool] 开始创建{pages.Count}个标签按钮...");
foreach (var page in pages)
{
GameObject buttonObj = Instantiate(tabButtonPrefab, tabButtonContainer);
buttonObj.name = $"Tab_{page.Key}";
Button button = buttonObj.GetComponent