MeowmentDebugTool/Packages/com.bywaystudios.meowmentdebugtool/Runtime/UniversalDebugTool.cs
2025-12-19 12:14:42 +08:00

930 lines
30 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 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 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 = "";
// 默认分辨率
private Vector2 defaultResolution = new Vector2(1080, 2340);
private Vector2 currentCustomResolution;
// 自定义按钮回调
private static Action<Button, TMP_Text> customButtonCallback;
// Canvas层级设置
private const int TOP_SORT_ORDER = 30000;
// 保存的SDF字体资源
private static TMP_FontAsset savedFontAsset = null;
#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;
DontDestroyOnLoad(gameObject);
}
else if (instance != this)
{
Destroy(gameObject);
return;
}
currentCustomResolution = defaultResolution;
// 设置Canvas为最上层
SetCanvasToTop();
// 未初始化前隐藏所有UI
if (!isInitialized)
{
HideAllUI();
}
// 注意如果isInitialized为true不在Awake中调用InitializeDebugTool
// 因为此时反射设置的字段可能还没生效延迟到Start中调用
}
private void Start()
{
// 只有在已初始化但还没调用过InitializeDebugTool时才执行
// 这避免了Awake和Init()的重复调用问题
if (isInitialized && pages.Count == 0)
{
// 这是通过Init()创建的实例需要在Start中完成初始化
// 此时反射设置的字段已经生效
InitializeDebugTool();
UpdateDeviceInfo();
UpdateSystemInfo();
LoadCustomButtons();
ShowMainWindow();
}
}
private void OnDestroy()
{
if (instance == this)
{
instance = null;
}
}
#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.UpdateDeviceInfo();
Instance.UpdateSystemInfo();
Instance.LoadCustomButtons();
Instance.ShowMainWindow();
Debug.Log("[MeowmentDebugTool] ✅ 初始化完成!");
}
}
/// <summary>
/// 设置所有TextMeshProUGUI的SDF字体
/// </summary>
/// <param name="fontAsset">TMP字体资源</param>
public static void SetSDFFont(TMP_FontAsset fontAsset)
{
if (!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. 重新加载自定义按钮使新字体应用到已存在的CustomButton
Debug.Log("[MeowmentDebugTool] 重新加载自定义按钮以应用新字体...");
Instance.ReloadCustomButtons();
}
/// <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("🚀 初始化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)
{
string errorDetails = $"tabButtonPrefab={(tabButtonPrefab == null ? "null" : "")}, " +
$"tabButtonContainer={(tabButtonContainer == null ? "null" : "")}";
Debug.LogWarning($"⚠️ [MeowmentDebugTool] 标签按钮预制件或容器未完全初始化:{errorDetails}");
Debug.LogWarning("💡 这可能是Unity初始化顺序问题如果标签正常显示则可以忽略此警告");
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 (savedFontAsset != null)
{
buttonText.font = savedFontAsset;
}
}
// 设置按钮颜色
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;
// 点击按钮后关闭主窗口
CloseDebugWindow();
}
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(false);
if (floatingButton != null)
floatingButton.SetActive(true);
}
#endregion
}
#region
/// <summary>
/// 调试按钮特性 - 标记方法为调试按钮
///
/// 使用说明:
/// 1. 在主项目中使用此特性时,请用条件编译包裹:
///
/// #if MEOWMENT_DEBUG_TOOL
/// [DebugButton("我的调试按钮")]
/// public static void MyDebugMethod()
/// {
/// Debug.Log("调试方法被执行");
/// }
/// #endif
///
/// 2. 这样当包被卸载时,代码不会报错,也不会影响主工程的正常运行
/// 3. MEOWMENT_DEBUG_TOOL 宏会在包安装时自动定义,卸载时自动移除
/// </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
}