MeowmentDebugTool/Packages/com.bywaystudios.meowmentdebugtool/Runtime/UniversalDebugTool.cs
2026-02-03 10:32:06 +08:00

1136 lines
38 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;
namespace MeowmentDebugTool
{
/// <summary>
/// 通用调试工具 - 支持多标签页的调试界面系统
/// 默认分辨率: 1080x2340
/// 使用方法:
/// 1. 场景中放置 UniversalDebugTool 预制件
/// 2. 在代码中调用 UniversalDebugTool.Init() 初始化
/// 3. 未调用 Init() 前不会显示任何UI
/// </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 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_InputField inputDialogInputField;
// [SerializeField] private Button inputDialogConfirmBtn;
// [SerializeField] private Button inputDialogCancelBtn;
#endregion
#region
private static UniversalDebugTool instance;
private static bool isInitialized = false;
private Dictionary<string, GameObject> pages = new Dictionary<string, GameObject>();
private Dictionary<string, Button> tabButtons = new Dictionary<string, Button>();
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<IDebugModule> allModules = new List<IDebugModule>();
// 暂时隐藏状态标志
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
#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()
{
// 如果未初始化直接销毁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();
// }
// 更新控制台模块
if (consoleModule != null)
{
consoleModule.Update();
}
}
private void OnDestroy()
{
// 如果未初始化,不执行任何操作
if (!isInitialized)
return;
if (instance == this)
{
instance = null;
// 销毁控制台模块
if (consoleModule != null)
{
consoleModule.Shutdown();
}
}
}
#endregion
#region
/// <summary>
/// 初始化调试工具自动创建UI
/// </summary>
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] 初始化完成!");
}
}
/// <summary>
/// 设置所有TextMeshProUGUI的SDF字体
/// </summary>
/// <param name="fontAsset">TMP字体资源</param>
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<TextMeshProUGUI>(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<TextMeshProUGUI>(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);
Instance.customButtonsModule.ReloadCustomButtons();
}
// 4.5. 通过模块重新加载自定义复选框
if (Instance.customCheckBoxesModule != null)
{
Debug.Log("[MeowmentDebugTool] 重新加载自定义复选框以应用新字体...");
Instance.customCheckBoxesModule.SetSDFFont(fontAsset);
Instance.customCheckBoxesModule.ReloadCustomCheckBoxes();
}
// 4.6. 通过模块重新加载数值
if (Instance.customValuesModule != null)
{
Debug.Log("[MeowmentDebugTool] 重新加载数值以应用新字体...");
Instance.customValuesModule.SetSDFFont(fontAsset);
Instance.customValuesModule.ReloadCustomValues();
}
// 5. 为控制台模块应用字体
if (Instance.consoleModule != null)
{
Instance.consoleModule.SetSDFFont(fontAsset);
}
}
/// <summary>
/// 设置Canvas为最上层
/// </summary>
private void SetCanvasToTop()
{
if (canvas != null)
{
canvas.sortingOrder = TOP_SORT_ORDER;
canvas.overrideSorting = true;
Debug.Log($"[MeowmentDebugTool] Canvas层级设置为: {TOP_SORT_ORDER}");
}
}
/// <summary>
/// 隐藏所有UI
/// </summary>
private void HideAllUI()
{
if (mainWindow != null)
mainWindow.gameObject.SetActive(false);
if (floatingButton != null)
floatingButton.SetActive(false);
}
private void InitializeDebugTool()
{
Debug.Log("[MeowmentDebugTool] 初始化UniversalDebugTool...");
// 创建模块实例
parametersModule = new ParametersModule(
parametersPage, deviceInfoText, systemInfoText, parametersScrollRect);
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, canvas);
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(parametersModule);
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)
{
ShowPage("按钮");
}
Debug.Log("[OK] UniversalDebugTool初始化完成");
Debug.Log("[MeowmentDebugTool] 提示: 点击窗口顶部的标签切换页面或按F1-F4使用快捷键");
}
/// <summary>
/// 初始化所有模块
/// </summary>
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<Button>();
TMP_Text buttonText = buttonObj.GetComponentInChildren<TMP_Text>();
if (buttonText != null)
{
buttonText.text = page.Key;
Debug.Log($"[OK] 创建标签: [{page.Key}]");
}
else
{
Debug.LogWarning($"⚠️ 标签按钮 [{page.Key}] 缺少文本组件");
}
string pageName = page.Key; // 闭包捕获
button.onClick.AddListener(() => ShowPage(pageName));
tabButtons[page.Key] = button;
}
Debug.Log("[OK] 标签按钮创建完成!");
}
#endregion
#region
/// <summary>
/// 显示指定的页面
/// </summary>
public void ShowPage(string pageName)
{
if (!isInitialized)
{
Debug.LogWarning("[MeowmentDebugTool] 调试工具未初始化,无法显示页面");
return;
}
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 ParametersModule
/// <summary>
/// 复制设备信息到剪贴板
/// </summary>
public void CopyDeviceInfoToClipboard()
{
if (!isInitialized)
{
Debug.LogWarning("[MeowmentDebugTool] 调试工具未初始化");
return;
}
parametersModule?.CopyDeviceInfoToClipboard();
}
/// <summary>
/// 复制系统信息到剪贴板
/// </summary>
public void CopySystemInfoToClipboard()
{
if (!isInitialized)
{
Debug.LogWarning("[MeowmentDebugTool] 调试工具未初始化");
return;
}
parametersModule?.CopySystemInfoToClipboard();
}
/// <summary>
/// 刷新所有信息
/// </summary>
public void RefreshAllInfo()
{
if (!isInitialized)
{
Debug.LogWarning("[MeowmentDebugTool] 调试工具未初始化");
return;
}
parametersModule?.RefreshAllInfo();
}
#endregion
#region CustomButtonsModule
/// <summary>
/// 设置自定义按钮回调
/// </summary>
public static void SetCustomButtonCallback(Action<Button, TMP_Text> callback)
{
if (!isInitialized || !InstanceExists)
{
Debug.LogWarning("[MeowmentDebugTool] 调试工具未初始化,无法设置按钮回调");
return;
}
if (Instance.customButtonsModule != null)
{
Instance.customButtonsModule.SetCustomButtonCallback(callback);
}
}
/// <summary>
/// 重新加载自定义按钮
/// </summary>
public void ReloadCustomButtons()
{
if (!isInitialized)
{
Debug.LogWarning("[MeowmentDebugTool] 调试工具未初始化");
return;
}
customButtonsModule?.ReloadCustomButtons();
}
#endregion
#region CustomValuesModule
/// <summary>
/// 更新指定方法的数值范围
/// </summary>
/// <param name="methodName">方法名称(完整路径如"ClassName.MethodName"或简单的"MethodName"</param>
/// <param name="minValue">新的最小值</param>
/// <param name="maxValue">新的最大值</param>
/// <returns>是否成功更新</returns>
public static bool UpdateDebugValueRange(string methodName, int minValue, int maxValue)
{
if (!isInitialized || !InstanceExists)
{
Debug.LogWarning("[MeowmentDebugTool] 调试工具未初始化,无法更新数值范围");
return false;
}
if (Instance.customValuesModule == null)
{
Debug.LogWarning("[MeowmentDebugTool] 数值模块未初始化");
return false;
}
return Instance.customValuesModule.UpdateValueRange(methodName, minValue, maxValue);
}
/// <summary>
/// 更新指定方法的当前值
/// </summary>
/// <param name="methodName">方法名称(完整路径如"ClassName.MethodName"或简单的"MethodName"</param>
/// <param name="value">新的值</param>
/// <returns>是否成功更新</returns>
public static bool UpdateDebugValue(string methodName, int value)
{
if (!isInitialized || !InstanceExists)
{
Debug.LogWarning("[MeowmentDebugTool] 调试工具未初始化,无法更新数值");
return false;
}
if (Instance.customValuesModule == null)
{
Debug.LogWarning("[MeowmentDebugTool] 数值模块未初始化");
return false;
}
return Instance.customValuesModule.UpdateValue(methodName, value);
}
#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>
/// 设置键盘测试模式用F键代替四指点击
/// </summary>
/// <param name="enable">true=使用F键测试, false=使用四指点击</param>
public static void SetKeyboardTestMode(bool enable)
{
useKeyboardTestMode = enable;
Debug.Log($"[MeowmentDebugTool] 键盘测试模式: {(enable ? " (F键触发)" : " (使)")}");
}
/// <summary>
/// 获取当前是否为键盘测试模式
/// </summary>
public static bool IsKeyboardTestMode()
{
return useKeyboardTestMode;
}
/// <summary>
/// 获取Debugger是否正在显示
/// </summary>
/// <returns>true=Debugger正在显示主窗口或悬浮按钮false=完全隐藏或未初始化</returns>
public static bool IsDebuggerVisible()
{
if (!isInitialized || !InstanceExists)
{
return false;
}
// 首先检查整个GameObject是否激活
if (!Instance.gameObject.activeSelf)
{
return false;
}
// 如果GameObject激活再检查主窗口或悬浮按钮是否可见
// 注意:使用 activeInHierarchy 而不是 activeSelf因为需要考虑父对象的状态
bool mainWindowVisible = Instance.mainWindow != null && Instance.mainWindow.gameObject.activeInHierarchy;
bool floatingButtonVisible = Instance.floatingButton != null && Instance.floatingButton.activeInHierarchy;
return mainWindowVisible || floatingButtonVisible;
}
/// <summary>
/// 显示调试工具
/// </summary>
public static void Show()
{
if (!isInitialized || !InstanceExists)
{
Debug.LogWarning("[MeowmentDebugTool] 调试工具未初始化,无法显示");
return;
}
Instance.gameObject.SetActive(true);
}
/// <summary>
/// 隐藏调试工具
/// </summary>
public static void Hide()
{
if (!isInitialized || !InstanceExists)
{
Debug.LogWarning("[MeowmentDebugTool] 调试工具未初始化,无法隐藏");
return;
}
Instance.gameObject.SetActive(false);
}
/// <summary>
/// 切换调试工具显示状态
/// </summary>
public static void Toggle()
{
if (!isInitialized || !InstanceExists)
{
Debug.LogWarning("[MeowmentDebugTool] 调试工具未初始化,无法切换状态");
return;
}
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>
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(false);
if (floatingButton != null)
floatingButton.SetActive(true);
}
/// <summary>
/// 暂时隐藏调试工具UI用于截图等场景
/// </summary>
/// <param name="seconds">隐藏的秒数默认5秒</param>
public static void HideTemporarily(float seconds = 5f)
{
if (!isInitialized || !InstanceExists)
{
Debug.LogWarning("[MeowmentDebugTool] 调试工具未初始化,无法隐藏");
return;
}
if (Instance.isHiding)
{
Debug.LogWarning("[MeowmentDebugTool] 已经在隐藏状态中,请等待恢复");
return;
}
Instance.StartCoroutine(Instance.HideTemporarilyCoroutine(seconds));
}
private IEnumerator HideTemporarilyCoroutine(float seconds)
{
isHiding = true;
Debug.Log($"[MeowmentDebugTool] 开始暂时隐藏流程,时长: {seconds}秒");
// 保存Canvas初始状态
if (canvas == null)
{
Debug.LogError("[MeowmentDebugTool] Canvas为空");
isHiding = false;
yield break;
}
bool canvasWasEnabled = canvas.enabled;
Debug.Log($"[MeowmentDebugTool] Canvas初始状态: {(canvasWasEnabled ? "" : "")}");
// 1. 先关闭主窗口(如果是打开状态)
bool wasMainWindowOpen = mainWindow != null && mainWindow.gameObject.activeSelf;
Debug.Log($"[MeowmentDebugTool] 主窗口初始状态: {(wasMainWindowOpen ? "" : "")}");
if (wasMainWindowOpen)
{
CloseDebugWindow();
yield return null;
Debug.Log("[MeowmentDebugTool] 主窗口已关闭");
}
// 2. 隐藏Canvas包括浮窗
canvas.enabled = false;
Debug.Log($"[MeowmentDebugTool] Canvas已禁用UI完全隐藏");
// 3. 等待指定时间
yield return new WaitForSeconds(seconds);
// 4. 恢复Canvas
canvas.enabled = true;
Debug.Log($"[MeowmentDebugTool] Canvas已启用UI恢复显示");
// 5. 验证恢复结果
if (canvas.enabled)
{
Debug.Log("[MeowmentDebugTool] [OK] UI已成功恢复浮窗状态");
}
else
{
Debug.LogError("[MeowmentDebugTool] ✗ Canvas恢复失败");
}
isHiding = false;
Debug.Log("[MeowmentDebugTool] 暂时隐藏流程结束");
}
#endregion
// #region 四指点击检测(已禁用)
// /// <summary>
// /// 检测四指点击屏幕
// /// </summary>
// private void DetectFourFingerTap()
// {
// // 只在移动设备或Unity编辑器模拟触摸上检测
// #if UNITY_ANDROID || UNITY_IOS || UNITY_EDITOR
//
// // 检测四指同时按下
// if (Input.touchCount == 4)
// {
// // 检查是否所有触摸都刚开始
// bool allBegan = true;
// foreach (Touch touch in Input.touches)
// {
// if (touch.phase != TouchPhase.Began)
// {
// allBegan = false;
// break;
// }
// }
//
// if (allBegan && !wasFourFingerTouch)
// {
// wasFourFingerTouch = true;
// fourFingerTouchStartTime = Time.time;
// }
// }
//
// // 检测四指抬起(点击完成)
// if (wasFourFingerTouch && Input.touchCount == 0)
// {
// float touchDuration = Time.time - fourFingerTouchStartTime;
//
// // 如果触摸时间小于阈值,认为是点击(而非长按)
// if (touchDuration < fourFingerTapTime)
// {
// OnFourFingerTap();
// }
//
// wasFourFingerTouch = false;
// }
//
// // 如果触摸数量变化,重置状态
// if (wasFourFingerTouch && Input.touchCount != 4)
// {
// wasFourFingerTouch = false;
// }
//
// #endif
// }
/// <summary>
/// 四指点击触发的事件 - 切换所有UI的显示/隐藏(包括主窗口和浮窗)
/// 注意此方法保留用于键盘测试模式按F键四指点击检测已禁用
/// </summary>
private void OnFourFingerTap()
{
Debug.Log("[MeowmentDebugTool] 检测到F键触发测试模式");
// 检查主窗口或浮窗是否有任意一个显示
bool anyUIVisible = (mainWindow != null && mainWindow.gameObject.activeSelf) ||
(floatingButton != null && floatingButton.activeSelf);
if (anyUIVisible)
{
// 隐藏所有UI
if (mainWindow != null)
mainWindow.gameObject.SetActive(false);
if (floatingButton != null)
floatingButton.SetActive(false);
Debug.Log("[MeowmentDebugTool] 所有UI已隐藏");
}
else
{
// 显示浮窗
if (mainWindow != null)
mainWindow.gameObject.SetActive(false);
if (floatingButton != null)
floatingButton.SetActive(true);
Debug.Log("[MeowmentDebugTool] 已显示浮窗");
}
}
// #endregion
}
#region
/// <summary>
/// 调试按钮特性 - 标记方法为调试按钮
///
/// 使用说明:
/// 1. 在主项目中使用此特性时,请用条件编译包裹:
///
/// #if MEOWMENT_DEBUG_TOOL
/// [DebugButton("默认", "我的调试按钮")]
/// public static void MyDebugMethod()
/// {
/// Debug.Log("调试方法被执行");
/// }
/// #endif
///
/// 2. 这样当包被卸载时,代码不会报错,也不会影响主工程的正常运行
/// 3. MEOWMENT_DEBUG_TOOL 宏会在包安装时自动定义,卸载时自动移除
/// 4. 组名参数用于对按钮进行分组显示,未指定组名的按钮会归入"默认"组
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class DebugButtonAttribute : Attribute
{
public string GroupName { get; set; }
public string DisplayName { get; set; }
public Color ButtonColor { get; set; }
public DebugButtonAttribute(string groupName = "默认", string displayName = "", float r = 0.8f, float g = 0.8f, float b = 0.8f)
{
GroupName = string.IsNullOrEmpty(groupName) ? "默认" : groupName;
DisplayName = displayName;
ButtonColor = new Color(r, g, b, 1f);
}
}
/// <summary>
/// 调试复选框特性 - 标记方法为调试复选框开关
///
/// 使用说明:
/// 1. 在主项目中使用此特性时,请用条件编译包裹:
///
/// #if MEOWMENT_DEBUG_TOOL
/// [DebugCheckBox("开关名称")]
/// public static void MyToggleMethod(bool isOn)
/// {
/// Debug.Log($"开关状态: {isOn}");
/// // 在这里设置某些参数的值
/// }
/// #endif
///
/// 2. 方法必须接受一个 bool 参数,表示复选框的状态
/// 3. 这样当包被卸载时,代码不会报错,也不会影响主工程的正常运行
/// 4. MEOWMENT_DEBUG_TOOL 宏会在包安装时自动定义,卸载时自动移除
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class DebugCheckBoxAttribute : Attribute
{
public string DisplayName { get; set; }
public Color CheckBoxColor { get; set; }
public DebugCheckBoxAttribute(string displayName = "", float r = 0.8f, float g = 0.8f, float b = 0.8f)
{
DisplayName = displayName;
CheckBoxColor = new Color(r, g, b, 1f);
}
}
/// <summary>
/// 调试数值特性 - 标记方法为调试数值调整器
///
/// 使用说明:
/// 1. 在主项目中使用此特性时,请用条件编译包裹:
///
/// #if MEOWMENT_DEBUG_TOOL
/// [DebugValue("数值名称", 最小值, 最大值)]
/// public static void MyValueMethod(int value)
/// {
/// Debug.Log($"设置数值: {value}");
/// // 在这里使用value来设置游戏参数
/// }
/// #endif
///
/// 2. 方法必须接受一个 int 参数,表示调整后的数值
/// 3. 可以指定最小值和最大值来限制范围
/// 4. 可以设置自定义颜色
/// 5. MEOWMENT_DEBUG_TOOL 宏会在包安装时自动定义,卸载时自动移除
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class DebugValueAttribute : Attribute
{
public string DisplayName { get; set; }
public Color ValueColor { get; set; }
public int MinValue { get; set; }
public int MaxValue { get; set; }
public int DefaultValue { get; set; }
public DebugValueAttribute(string displayName = "", int minValue = 0, int maxValue = 100, int defaultValue = -1, float r = 0.8f, float g = 0.8f, float b = 0.8f)
{
DisplayName = displayName;
MinValue = minValue;
MaxValue = maxValue;
// 如果defaultValue为-1未设置则使用minValue作为默认值
DefaultValue = defaultValue == -1 ? minValue : defaultValue;
ValueColor = new Color(r, g, b, 1f);
}
}
#endregion
}