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