From 62f990f70276fad3763ebd5956b872b35d45fa75 Mon Sep 17 00:00:00 2001 From: zhang hongbo Date: Mon, 22 Dec 2025 15:29:55 +0800 Subject: [PATCH] =?UTF-8?q?0.3=E6=9B=B4=E6=96=B0=EF=BC=8C=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E6=8E=A7=E5=88=B6=E5=8F=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MeowMentDebugTool.slnx | 2 +- .../BUGFIX_REBUILD_LOOP.md | 215 ++++++ .../BUGFIX_REBUILD_LOOP.md.meta | 7 + .../CHANGELOG.md | 44 ++ .../CONSOLE_INTEGRATION.md | 236 +++++++ .../CONSOLE_INTEGRATION.md.meta | 7 + .../CONSOLE_MODULE_GUIDE.md | 171 +++++ .../CONSOLE_MODULE_GUIDE.md.meta | 7 + .../CONSOLE_QUICK_SETUP.md | 233 +++++++ .../CONSOLE_QUICK_SETUP.md.meta | 7 + .../CONSOLE_RUNTIME_READY.md | 186 ++++++ .../CONSOLE_RUNTIME_READY.md.meta | 7 + .../README.md | 616 ++++++++++++----- .../REFACTORING_NOTES.md | 111 +++ .../REFACTORING_NOTES.md.meta | 7 + .../Runtime/ConsoleModule.cs | 579 ++++++++++++++++ .../Runtime/ConsoleModule.cs.meta | 11 + .../Runtime/CustomButtonsModule.cs | 180 +++++ .../Runtime/CustomButtonsModule.cs.meta | 11 + .../Runtime/IDebugModule.cs | 25 + .../Runtime/IDebugModule.cs.meta | 11 + .../Runtime/LogNode.cs | 50 ++ .../Runtime/LogNode.cs.meta | 11 + .../Runtime/ParametersModule.cs | 132 ++++ .../Runtime/ParametersModule.cs.meta | 11 + .../Runtime/RuntimeUIGenerator.cs | 372 ++++++++++- .../Runtime/SettingsModule.cs | 128 ++++ .../Runtime/SettingsModule.cs.meta | 11 + .../Runtime/ToolbarModule.cs | 142 ++++ .../Runtime/ToolbarModule.cs.meta | 11 + .../Runtime/UniversalDebugTool.cs | 632 +++++++----------- .../SampleDebugFunctions.cs | 4 +- .../package.json | 2 +- 33 files changed, 3611 insertions(+), 568 deletions(-) create mode 100644 Packages/com.bywaystudios.meowmentdebugtool/BUGFIX_REBUILD_LOOP.md create mode 100644 Packages/com.bywaystudios.meowmentdebugtool/BUGFIX_REBUILD_LOOP.md.meta create mode 100644 Packages/com.bywaystudios.meowmentdebugtool/CONSOLE_INTEGRATION.md create mode 100644 Packages/com.bywaystudios.meowmentdebugtool/CONSOLE_INTEGRATION.md.meta create mode 100644 Packages/com.bywaystudios.meowmentdebugtool/CONSOLE_MODULE_GUIDE.md create mode 100644 Packages/com.bywaystudios.meowmentdebugtool/CONSOLE_MODULE_GUIDE.md.meta create mode 100644 Packages/com.bywaystudios.meowmentdebugtool/CONSOLE_QUICK_SETUP.md create mode 100644 Packages/com.bywaystudios.meowmentdebugtool/CONSOLE_QUICK_SETUP.md.meta create mode 100644 Packages/com.bywaystudios.meowmentdebugtool/CONSOLE_RUNTIME_READY.md create mode 100644 Packages/com.bywaystudios.meowmentdebugtool/CONSOLE_RUNTIME_READY.md.meta create mode 100644 Packages/com.bywaystudios.meowmentdebugtool/REFACTORING_NOTES.md create mode 100644 Packages/com.bywaystudios.meowmentdebugtool/REFACTORING_NOTES.md.meta create mode 100644 Packages/com.bywaystudios.meowmentdebugtool/Runtime/ConsoleModule.cs create mode 100644 Packages/com.bywaystudios.meowmentdebugtool/Runtime/ConsoleModule.cs.meta create mode 100644 Packages/com.bywaystudios.meowmentdebugtool/Runtime/CustomButtonsModule.cs create mode 100644 Packages/com.bywaystudios.meowmentdebugtool/Runtime/CustomButtonsModule.cs.meta create mode 100644 Packages/com.bywaystudios.meowmentdebugtool/Runtime/IDebugModule.cs create mode 100644 Packages/com.bywaystudios.meowmentdebugtool/Runtime/IDebugModule.cs.meta create mode 100644 Packages/com.bywaystudios.meowmentdebugtool/Runtime/LogNode.cs create mode 100644 Packages/com.bywaystudios.meowmentdebugtool/Runtime/LogNode.cs.meta create mode 100644 Packages/com.bywaystudios.meowmentdebugtool/Runtime/ParametersModule.cs create mode 100644 Packages/com.bywaystudios.meowmentdebugtool/Runtime/ParametersModule.cs.meta create mode 100644 Packages/com.bywaystudios.meowmentdebugtool/Runtime/SettingsModule.cs create mode 100644 Packages/com.bywaystudios.meowmentdebugtool/Runtime/SettingsModule.cs.meta create mode 100644 Packages/com.bywaystudios.meowmentdebugtool/Runtime/ToolbarModule.cs create mode 100644 Packages/com.bywaystudios.meowmentdebugtool/Runtime/ToolbarModule.cs.meta diff --git a/MeowMentDebugTool.slnx b/MeowMentDebugTool.slnx index 4be91a3..bdc551b 100644 --- a/MeowMentDebugTool.slnx +++ b/MeowMentDebugTool.slnx @@ -1,5 +1,5 @@  - + diff --git a/Packages/com.bywaystudios.meowmentdebugtool/BUGFIX_REBUILD_LOOP.md b/Packages/com.bywaystudios.meowmentdebugtool/BUGFIX_REBUILD_LOOP.md new file mode 100644 index 0000000..0592dcb --- /dev/null +++ b/Packages/com.bywaystudios.meowmentdebugtool/BUGFIX_REBUILD_LOOP.md @@ -0,0 +1,215 @@ +# Console模块错误修复说明 + +## 🐛 已修复的问题 + +### 1. Rebuild Loop 错误 + +**问题描述**: +``` +Trying to remove/add Background (UnityEngine.UI.Image) from/to rebuild list +while we are already inside a rebuild loop. This is not supported. +``` + +**原因**: +- 在 `OnLogMessageReceived` 回调中立即调用 `RefreshLogDisplay()` 刷新UI +- 在Unity的Canvas rebuild循环中修改UI元素会触发此错误 +- `Canvas.ForceUpdateCanvases()` 在初始化时也会导致rebuild loop + +**修复方案**: + +#### ConsoleModule.cs +1. ✅ **延迟刷新机制**: 添加 `needRefresh` 标志,不在回调中立即刷新UI +```csharp +private bool needRefresh = false; + +// 在OnLogMessageReceived中 +needRefresh = true; // 标记需要刷新 + +// 在Update中 +if (needRefresh) +{ + needRefresh = false; + RefreshLogDisplay(); // 延迟到下一帧刷新 +} +``` + +2. ✅ **滚动位置延迟设置**: 使用帧计数延迟设置滚动位置 +```csharp +private int lastRefreshFrame = -1; + +// 只在刷新后的第2帧设置滚动位置 +if (lockScroll && Time.frameCount > lastRefreshFrame + 1) +{ + logScrollRect.verticalNormalizedPosition = 0f; +} +``` + +3. ✅ **过滤器事件优化**: 过滤器改变时也使用延迟刷新 +```csharp +private void OnInfoFilterChanged(bool value) +{ + infoFilter = value; + needRefresh = true; // 而不是立即RefreshLogDisplay() +} +``` + +#### SettingsModule.cs +4. ✅ **移除ForceUpdateCanvases**: Canvas会自动在下一帧更新 +```csharp +private void ApplyResolution(Vector2 resolution) +{ + mainWindow.sizeDelta = resolution; + currentResolutionText.text = $"当前窗口尺寸: {resolution.x} x {resolution.y}"; + + // 移除了 Canvas.ForceUpdateCanvases(); + // Canvas会自动更新,不需要强制刷新 +} +``` + +### 2. 中文字体警告 + +**问题描述**: +``` +The character with Unicode value \u5F84 was not found in the [LiberationSans SDF] +font asset or any potential fallbacks. +``` + +**原因**: +- RuntimeUIGenerator使用的默认字体 `LiberationSans SDF` 不支持中文字符 +- TextMeshPro需要专门的中文字体资源 + +**解决方案** (选择其一): + +#### 方案1: 使用中文字体 (推荐) +```csharp +// 在游戏初始化时设置中文字体 +UniversalDebugTool.Init(); + +// 加载支持中文的TMP字体 +TMP_FontAsset chineseFont = Resources.Load("Fonts/ChineseFont SDF"); +UniversalDebugTool.SetSDFFont(chineseFont); +``` + +#### 方案2: 禁用字体警告 +在RuntimeUIGenerator的GetDefaultFont方法中添加过滤: +```csharp +private static TMP_FontAsset GetDefaultFont() +{ + // 禁用TMP字体警告 + TMPro.TMP_Settings.warningsDisabled = true; + + // 查找默认字体... +} +``` + +#### 方案3: 创建中文字体资源 +1. 在Unity中:Window > TextMeshPro > Font Asset Creator +2. 选择支持中文的字体(如Arial Unicode MS、Microsoft YaHei等) +3. 设置Character Set为Unicode或Custom Characters +4. 添加常用中文字符 +5. 生成字体资源并在代码中加载 + +## 📋 修改总结 + +### 文件变更 + +#### ConsoleModule.cs +- ✅ 添加 `needRefresh` 标志 +- ✅ 添加 `lastRefreshFrame` 帧计数 +- ✅ 修改 `OnLogMessageReceived` 使用延迟刷新 +- ✅ 修改 `Update` 方法处理延迟刷新和滚动 +- ✅ 修改所有过滤器事件使用延迟刷新 + +#### SettingsModule.cs +- ✅ 移除 `Canvas.ForceUpdateCanvases()` 调用 + +### 性能影响 + +✅ **更好**: 延迟刷新减少了不必要的UI重建 +✅ **更稳定**: 避免了rebuild loop错误 +✅ **无副作用**: 延迟一帧刷新对用户体验无影响 + +## 🧪 测试建议 + +### 1. 测试Rebuild Loop修复 +```csharp +void Start() +{ + UniversalDebugTool.Init(); + + // 快速生成大量日志 + for (int i = 0; i < 100; i++) + { + Debug.Log($"测试日志 {i}"); + Debug.LogWarning($"警告 {i}"); + Debug.LogError($"错误 {i}"); + } + + // 不应该再有rebuild loop错误 +} +``` + +### 2. 测试滚动功能 +```csharp +void Update() +{ + if (Input.GetKeyDown(KeyCode.Space)) + { + Debug.Log($"新日志 - 帧数: {Time.frameCount}"); + // 应该自动滚动到底部(如果锁定滚动已启用) + } +} +``` + +### 3. 测试过滤器 +1. 生成多种类型的日志 +2. 点击Console标签页 +3. 切换Info/Warning/Error/Fatal过滤器 +4. 不应该有rebuild loop错误 + +### 4. 测试分辨率设置 +1. 进入Settings标签页 +2. 修改宽度和高度 +3. 点击"应用"按钮 +4. 不应该有rebuild loop错误 + +## ⚠️ 注意事项 + +### 关于中文字体 +如果你的游戏需要显示中文,必须: +1. 创建或导入支持中文的TMP字体资源 +2. 在初始化后调用 `UniversalDebugTool.SetSDFFont(yourChineseFont)` +3. 或者在RuntimeUIGenerator中修改GetDefaultFont方法返回中文字体 + +### 最佳实践 +```csharp +public class GameInit : MonoBehaviour +{ + [SerializeField] private TMP_FontAsset chineseFont; + + void Start() + { + // 1. 初始化调试工具 + UniversalDebugTool.Init(); + + // 2. 设置中文字体(如果需要) + if (chineseFont != null) + { + UniversalDebugTool.SetSDFFont(chineseFont); + } + + // 3. 其他初始化... + } +} +``` + +## ✅ 预期结果 + +修复后,你应该看到: +- ✅ 没有 "Trying to remove/add ... from/to rebuild list" 错误 +- ✅ Console正常显示日志 +- ✅ 过滤器正常工作 +- ✅ 滚动功能正常 +- ⚠️ 可能仍有字体警告(如果没有设置中文字体) + +字体警告不影响功能,只是文本显示为方块。如果需要中文显示,请按照上述方案添加中文字体。 diff --git a/Packages/com.bywaystudios.meowmentdebugtool/BUGFIX_REBUILD_LOOP.md.meta b/Packages/com.bywaystudios.meowmentdebugtool/BUGFIX_REBUILD_LOOP.md.meta new file mode 100644 index 0000000..a555966 --- /dev/null +++ b/Packages/com.bywaystudios.meowmentdebugtool/BUGFIX_REBUILD_LOOP.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 3d6ac60c7ac1db84299fc8dde7508c38 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.bywaystudios.meowmentdebugtool/CHANGELOG.md b/Packages/com.bywaystudios.meowmentdebugtool/CHANGELOG.md index a69f5fb..c92480c 100644 --- a/Packages/com.bywaystudios.meowmentdebugtool/CHANGELOG.md +++ b/Packages/com.bywaystudios.meowmentdebugtool/CHANGELOG.md @@ -5,6 +5,50 @@ 格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/), 版本号遵循 [Semantic Versioning](https://semver.org/lang/zh-CN/)。 +## [0.3.0] - 2025-12-22 + +### Added +- **控制台模块 (Console)** + - 实时捕获并显示Unity日志(Info、Warning、Error、Fatal) + - 支持日志类型过滤(可单独开关各类型日志显示) + - 支持日志点击查看详细信息(堆栈跟踪) + - 日志数量统计和实时更新 + - 支持清空日志功能 + - 支持锁定滚动功能(自动滚动到最新日志) + - 日志项对象池优化,提升性能 + - 最大日志数限制(默认100条,可配置) + - 日志详情区域显示完整堆栈信息 + - 使用UGUI实现,支持ScrollRect滚动 + +- **暂时隐藏功能** + - 工具栏新增"暂时隐藏(5秒)"按钮 + - 用于截图时临时隐藏调试工具UI + - 自动在指定时间后恢复显示 + - 支持防重入保护,避免重复触发 + - 恢复后保持浮窗状态,不自动打开主窗口 + +### Changed +- 重构代码架构,将功能模块化 + - 新增 `IDebugModule` 接口 + - 拆分为独立模块:`ParametersModule`、`CustomButtonsModule`、`ToolbarModule`、`SettingsModule`、`ConsoleModule` +- 优化控制台UI布局 + - ControlPanel高度优化为40像素 + - LogArea和DetailArea固定高度 + - 移除不常用的Spacer元素 +- 优化日志显示 + - 日志项高度从30增加到50像素 + - 字体大小从22增加到28 + - 添加上下边距提升可读性 +- 移除所有emoji字符,避免字体缺失警告 +- 锁定滚动功能默认开启 + +### Fixed +- 修复Canvas rebuild loop错误(延迟刷新机制) +- 修复日志项RectTransform锚点设置问题 +- 修复VerticalLayoutGroup子元素高度控制问题 +- 修复页面未激活时不必要的更新操作 +- 修复暂时隐藏功能第二次执行失败的问题 + ## [0.2.2] - 2025-12-19 ### Changed diff --git a/Packages/com.bywaystudios.meowmentdebugtool/CONSOLE_INTEGRATION.md b/Packages/com.bywaystudios.meowmentdebugtool/CONSOLE_INTEGRATION.md new file mode 100644 index 0000000..8cd95d1 --- /dev/null +++ b/Packages/com.bywaystudios.meowmentdebugtool/CONSOLE_INTEGRATION.md @@ -0,0 +1,236 @@ +# Console 控制台模块 - 添加说明 + +## ✅ 已完成的工作 + +### 1. 新增文件 + +#### [LogNode.cs](Runtime/LogNode.cs) +日志节点数据结构,包含: +- LogType: 日志类型(Info/Warning/Error/Exception) +- LogMessage: 日志消息 +- StackTrace: 堆栈跟踪 +- LogTime: 日志时间 +- LogFrameCount: 日志帧数 + +#### [ConsoleModule.cs](Runtime/ConsoleModule.cs) +控制台模块实现,基于UGUI,包含以下功能: +- ✅ 实时捕获Unity日志 +- ✅ 日志分类和过滤(Info/Warning/Error/Fatal) +- ✅ 锁定滚动到底部 +- ✅ 点击查看详细堆栈信息 +- ✅ 清除日志功能 +- ✅ 日志颜色区分 +- ✅ 对象池性能优化 +- ✅ 最大日志行数限制(500行) +- ✅ SDF字体支持 + +### 2. 修改的文件 + +#### [UniversalDebugTool.cs](Runtime/UniversalDebugTool.cs) +添加了以下内容: +1. **序列化字段** - Console页面的所有UI组件引用 +2. **ConsoleModule实例** - 控制台模块对象 +3. **初始化代码** - 在InitializeDebugTool中创建和注册Console模块 +4. **Update方法** - 调用consoleModule.Update()更新UI +5. **OnDestroy方法** - 清理控制台模块资源 +6. **SetSDFFont支持** - 为Console模块应用字体 + +## 📋 需要在Unity中配置的UI + +### Unity Inspector 配置清单 + +在UniversalDebugTool组件的Inspector中,需要配置以下字段: + +``` +[控制台页面] +├── Console Page (GameObject) +├── Console Log Scroll Rect (ScrollRect) +├── Console Log Content (RectTransform) +├── Console Detail Scroll Rect (ScrollRect) +├── Console Detail Text (TMP_Text) +├── Console Clear Button (Button) +├── Console Lock Scroll Toggle (Toggle) +├── Console Info Filter Toggle (Toggle) +├── Console Warning Filter Toggle (Toggle) +├── Console Error Filter Toggle (Toggle) +├── Console Fatal Filter Toggle (Toggle) +└── Console Log Item Prefab (GameObject) +``` + +### UI层次结构示例 + +``` +ConsolePage +├── ControlPanel (HorizontalLayoutGroup) +│ ├── ClearButton +│ ├── LockScrollToggle +│ ├── Spacer (LayoutElement - FlexibleWidth) +│ ├── InfoFilterToggle +│ ├── WarningFilterToggle +│ ├── ErrorFilterToggle +│ └── FatalFilterToggle +│ +├── LogArea (60% 高度) +│ └── LogScrollView (ScrollRect) +│ ├── Viewport +│ └── Content (VerticalLayoutGroup) +│ └── [日志项动态生成在这里] +│ +└── DetailArea (40% 高度) + └── DetailScrollView (ScrollRect) + ├── Viewport + └── Content + └── DetailText +``` + +### 日志项预制件 (ConsoleLogItemPrefab) + +创建一个预制件,结构如下: +``` +LogItem (GameObject) +├── Toggle (Toggle组件) +├── Background (Image) +└── Label (TextMeshPro - Text) + - RichText: 启用 + - Word Wrapping: 启用 + - Overflow: Overflow +``` + +## 🎨 UI组件配置建议 + +### ControlPanel +- Component: HorizontalLayoutGroup +- Padding: (10, 10, 10, 10) +- Spacing: 10 +- Child Force Expand: Width = false, Height = false + +### LogScrollView +- Component: ScrollRect +- Content: 指向LogContent +- Vertical: ✅ +- Horizontal: ❌ +- Movement Type: Elastic +- Inertia: ✅ +- Scrollbar Visibility: Auto Hide + +### LogContent +- Component: VerticalLayoutGroup +- Child Alignment: Upper Center +- Child Control Size: Height = ✅ +- Child Force Expand: Width = ✅ +- Spacing: 2 + +### DetailScrollView +- Component: ScrollRect +- Vertical: ✅ +- Horizontal: ❌ +- Movement Type: Clamped + +### DetailText +- Rich Text: ✅ +- Wrapping: ✅ +- Overflow: Overflow +- Font Size: 14-16 + +### Toggle组件 +- Is On: 根据默认值 +- Toggle Transition: Fade +- 每个Toggle添加Label (TMP_Text)显示文本 + +## 🔧 代码集成 + +### 模块已自动集成 +ConsoleModule已经在UniversalDebugTool中自动初始化和管理,无需额外代码。 + +### 使用示例 + +```csharp +// 1. 初始化调试工具(会自动初始化Console) +UniversalDebugTool.Init(); + +// 2. 设置字体(可选) +UniversalDebugTool.SetSDFFont(myFont); + +// 3. 获取日志统计(可选) +if (UniversalDebugTool.InstanceExists) +{ + var console = UniversalDebugTool.Instance.consoleModule; + if (console != null) + { + console.GetLogCount(out int info, out int warning, + out int error, out int fatal); + Debug.Log($"日志: Info={info}, Warn={warning}, " + + $"Error={error}, Fatal={fatal}"); + } +} +``` + +## 📊 与GameFramework对比 + +### 主要差异 + +| 项目 | GameFramework | MeowmentDebugTool | +|------|---------------|-------------------| +| UI系统 | OnGUI | UGUI | +| 渲染方式 | 即时模式(每帧重绘) | 保留模式(按需刷新) | +| 性能 | 中等 | 更优(对象池优化) | +| 布局方式 | GUILayout | LayoutGroup + RectTransform | +| 交互组件 | GUILayout.Button/Toggle | Button/Toggle组件 | +| 滚动视图 | GUILayout.BeginScrollView | ScrollRect组件 | +| 定制性 | 通过GUISkin | 完全UGUI定制 | + +### 实现等价性 + +✅ **完全实现的功能**: +- 日志捕获(Application.logMessageReceived) +- 日志分类(Info/Warning/Error/Fatal) +- 过滤器(4种日志类型独立过滤) +- 锁定滚动 +- 选中查看详情 +- 清除日志 +- 日志颜色区分 +- 最大行数限制 + +✅ **优化功能**: +- 对象池(避免频繁创建销毁) +- UGUI性能更好 +- 更灵活的布局系统 + +❌ **未实现的功能**: +- 复制到剪贴板(可以通过DetailText选择复制) +- SettingComponent持久化(使用PlayerPrefs替代即可) + +## 🎯 后续步骤 + +1. **在Unity中创建UI** + - 在Scene或Prefab中创建Console页面UI + - 按照层次结构创建各个组件 + - 创建日志项预制件 + +2. **配置引用** + - 在UniversalDebugTool组件Inspector中 + - 将所有Console相关UI组件拖拽到对应字段 + +3. **测试** + - 运行游戏 + - 调用 `UniversalDebugTool.Init()` + - 查看Console标签页 + - 测试各个过滤器和功能 + +4. **调整样式**(可选) + - 修改颜色、字体大小 + - 调整布局间距 + - 优化日志项高度 + +## ⚠️ 注意事项 + +1. **日志项预制件必须有Toggle组件** +2. **ScrollRect的Content必须正确设置** +3. **VerticalLayoutGroup的设置影响滚动性能** +4. **大量日志时注意性能(已有500行限制)** +5. **确保Console Page在初始化时是隐藏的** + +## 📚 参考文档 + +- [CONSOLE_MODULE_GUIDE.md](CONSOLE_MODULE_GUIDE.md) - 详细的UI结构和配置指南 +- [REFACTORING_NOTES.md](REFACTORING_NOTES.md) - 模块化重构说明 diff --git a/Packages/com.bywaystudios.meowmentdebugtool/CONSOLE_INTEGRATION.md.meta b/Packages/com.bywaystudios.meowmentdebugtool/CONSOLE_INTEGRATION.md.meta new file mode 100644 index 0000000..84de8e0 --- /dev/null +++ b/Packages/com.bywaystudios.meowmentdebugtool/CONSOLE_INTEGRATION.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 9bc4f10ea5e6a9d49b550c5aec275f1a +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.bywaystudios.meowmentdebugtool/CONSOLE_MODULE_GUIDE.md b/Packages/com.bywaystudios.meowmentdebugtool/CONSOLE_MODULE_GUIDE.md new file mode 100644 index 0000000..8cf5eca --- /dev/null +++ b/Packages/com.bywaystudios.meowmentdebugtool/CONSOLE_MODULE_GUIDE.md @@ -0,0 +1,171 @@ +# Console 控制台模块 UI 结构说明 + +## 概述 + +Console模块是一个基于UGUI的日志控制台,用于实时显示Unity的日志信息(Info/Warning/Error/Exception)。 + +## 功能特性 + +- ✅ 实时捕获Unity日志(Application.logMessageReceived) +- ✅ 日志分类显示(Info/Warning/Error/Fatal) +- ✅ 日志过滤器(可以单独开关各类型日志) +- ✅ 锁定滚动到底部 +- ✅ 点击日志查看详细堆栈信息 +- ✅ 清除所有日志 +- ✅ 日志颜色区分 +- ✅ 最大日志行数限制(默认500行) +- ✅ 对象池优化性能 + +## UI结构 + +### Console Page 层次结构 +``` +ConsolePage (GameObject) +├── ControlPanel (水平布局) +│ ├── ClearButton (Button) - 清空日志按钮 +│ ├── LockScrollToggle (Toggle) - 锁定滚动 +│ ├── Spacer (空白区域) +│ ├── InfoFilterToggle (Toggle) - Info过滤器 +│ ├── WarningFilterToggle (Toggle) - Warning过滤器 +│ ├── ErrorFilterToggle (Toggle) - Error过滤器 +│ └── FatalFilterToggle (Toggle) - Fatal过滤器 +│ +├── LogScrollView (ScrollRect) - 日志列表区域 +│ └── LogContent (RectTransform) - 日志内容容器 +│ └── [动态生成的日志项] +│ +└── DetailScrollView (ScrollRect) - 详细信息区域 + └── DetailText (TMP_Text) - 显示选中日志的详细信息和堆栈 +``` + +### 日志项预制件结构 (ConsoleLogItemPrefab) +``` +LogItem (GameObject) +├── Toggle (Toggle组件) - 用于选中日志 +├── Background (Image) - 背景 +└── Label (TMP_Text) - 显示日志文本 +``` + +## 序列化字段配置 + +在 UniversalDebugTool 的 Inspector 中需要配置以下字段: + +### Console页面字段 +```csharp +[Header("控制台页面")] +consolePage // 控制台页面根对象 +consoleLogScrollRect // 日志列表ScrollRect +consoleLogContent // 日志内容容器RectTransform +consoleDetailScrollRect // 详情ScrollRect +consoleDetailText // 详情文本TMP_Text +consoleClearButton // 清空按钮 +consoleLockScrollToggle // 锁定滚动Toggle +consoleInfoFilterToggle // Info过滤Toggle +consoleWarningFilterToggle // Warning过滤Toggle +consoleErrorFilterToggle // Error过滤Toggle +consoleFatalFilterToggle // Fatal过滤Toggle +consoleLogItemPrefab // 日志项预制件 +``` + +## UI组件要求 + +### 1. ControlPanel +- 使用 HorizontalLayoutGroup +- 建议添加 ContentSizeFitter +- Padding: 10, 10, 10, 10 +- Spacing: 10 + +### 2. LogScrollView +- ScrollRect 设置: + - Vertical: true + - Horizontal: false + - Movement Type: Elastic + - Scrollbar Visibility: Automatic If Needed +- LogContent 需要 VerticalLayoutGroup: + - Child Alignment: Upper Center + - Child Force Expand: Width + - Child Control Size: Height + - Spacing: 2 + +### 3. DetailScrollView +- ScrollRect 设置: + - Vertical: true + - Horizontal: false + - Movement Type: Clamped +- 建议高度: 150-200 +- DetailText 设置: + - Enable Word Wrapping + - Overflow: Overflow + - Enable Rich Text + +### 4. Toggles +- 每个Toggle需要一个Label (TMP_Text) +- 建议宽度: 100-120 +- Toggle Group: 不需要(可以多选) + +## 日志颜色配置 + +在 ConsoleModule 中默认的颜色: +```csharp +Info: Color.white (255, 255, 255) +Warning: Color.yellow (255, 255, 0) +Error: Color.red (255, 0, 0) +Fatal: Color(0.7f, 0.2f, 0.2f) (178, 51, 51) +``` + +## 性能优化 + +1. **对象池机制**: 日志项使用对象池,避免频繁创建销毁 +2. **最大行数限制**: 默认500行,防止内存溢出 +3. **按需刷新**: 只在日志变化或过滤器改变时刷新显示 +4. **Toggle优化**: 使用Toggle而不是Button,减少重绘 + +## 使用示例 + +### 在代码中获取日志统计 +```csharp +if (UniversalDebugTool.InstanceExists) +{ + var console = UniversalDebugTool.Instance.consoleModule; + if (console != null) + { + console.GetLogCount(out int info, out int warning, out int error, out int fatal); + Debug.Log($"日志统计 - Info:{info}, Warning:{warning}, Error:{error}, Fatal:{fatal}"); + } +} +``` + +### 设置字体 +```csharp +UniversalDebugTool.SetSDFFont(myFont); +// 会自动应用到控制台模块 +``` + +## 注意事项 + +1. **日志项预制件**: 必须包含 Toggle 和 TMP_Text 组件 +2. **ScrollRect**: 必须正确设置 Content 和 Viewport +3. **性能**: 大量日志时建议增加日志项的高度以减少可见数量 +4. **线程安全**: 日志回调在主线程执行,无需担心线程问题 +5. **内存管理**: 超过最大行数的日志会自动删除 + +## 与GameFramework的差异 + +| 特性 | GameFramework (GUI) | MeowmentDebugTool (UGUI) | +|------|---------------------|--------------------------| +| UI系统 | OnGUI (即时模式) | UGUI (保留模式) | +| 性能 | 每帧重绘 | 按需刷新 | +| 布局 | GUILayout自动布局 | RectTransform + LayoutGroup | +| 交互 | GUILayout.Button/Toggle | Button/Toggle组件 | +| 对象池 | 不需要 | 使用对象池优化 | +| 滚动 | GUILayout.BeginScrollView | ScrollRect组件 | + +## 未来扩展 + +可以考虑添加的功能: +- [ ] 搜索/过滤日志内容 +- [ ] 导出日志到文件 +- [ ] 日志标签/分类 +- [ ] 日志统计图表 +- [ ] 复制日志到剪贴板 +- [ ] 日志时间范围过滤 diff --git a/Packages/com.bywaystudios.meowmentdebugtool/CONSOLE_MODULE_GUIDE.md.meta b/Packages/com.bywaystudios.meowmentdebugtool/CONSOLE_MODULE_GUIDE.md.meta new file mode 100644 index 0000000..90d1a6f --- /dev/null +++ b/Packages/com.bywaystudios.meowmentdebugtool/CONSOLE_MODULE_GUIDE.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 2e77813ce2d912b42b65134e2ce344f9 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.bywaystudios.meowmentdebugtool/CONSOLE_QUICK_SETUP.md b/Packages/com.bywaystudios.meowmentdebugtool/CONSOLE_QUICK_SETUP.md new file mode 100644 index 0000000..633fff1 --- /dev/null +++ b/Packages/com.bywaystudios.meowmentdebugtool/CONSOLE_QUICK_SETUP.md @@ -0,0 +1,233 @@ +# Console模块快速设置指南 + +## 🚀 5分钟快速设置 + +### 步骤1: 创建Console页面GameObject + +在UniversalDebugTool的预制件中,创建以下结构: + +``` +[添加到ContentContainer下] +└── ConsolePage + ├── ControlPanel + │ ├── ClearButton (Button) + │ ├── LockScrollToggle (Toggle + Label) + │ ├── Spacer (Empty GameObject + LayoutElement) + │ ├── InfoToggle (Toggle + Label) + │ ├── WarningToggle (Toggle + Label) + │ ├── ErrorToggle (Toggle + Label) + │ └── FatalToggle (Toggle + Label) + │ + ├── LogArea + │ └── LogScrollView (ScrollRect) + │ └── Viewport + │ └── Content (VerticalLayoutGroup) + │ + └── DetailArea + └── DetailScrollView (ScrollRect) + └── Viewport + └── Content + └── DetailText (TMP_Text) +``` + +### 步骤2: 配置组件 + +#### ConsolePage +- RectTransform: Stretch (全屏) +- 默认禁用(Active = false) + +#### ControlPanel +- Height: 50 +- Add Component: HorizontalLayoutGroup + - Padding: 10, 10, 10, 10 + - Spacing: 10 + - Child Force Expand: 都不勾选 + +#### Spacer (占位符) +- Add Component: LayoutElement + - Flexible Width: 1 + +#### LogArea +- Height: 60% (~400) +- Add Component: VerticalLayoutGroup + +#### LogScrollView +- Add Component: ScrollRect + - Content: LogContent + - Vertical: ✅ + - Horizontal: ❌ + - Scrollbar Visibility: Auto Hide And Expand Viewport + +#### LogContent +- Add Component: VerticalLayoutGroup + - Child Alignment: Upper Center + - Spacing: 2 + - Child Control Size: Height ✅ + - Child Force Expand: Width ✅ +- Add Component: ContentSizeFitter + - Vertical Fit: Preferred Size + +#### DetailArea +- Height: 40% (~200) + +#### DetailScrollView +- 同LogScrollView配置 + +#### DetailText +- Font Size: 14 +- Color: White +- Alignment: Top Left +- Overflow: Overflow +- Wrapping: Enabled +- Rich Text: ✅ + +### 步骤3: 创建日志项预制件 + +创建一个新的预制件 `ConsoleLogItem`: + +``` +LogItem +├── Toggle (Toggle组件) +│ - Is On: false +│ - Transition: None或Fade +│ +├── Background (Image) +│ - Color: (30, 30, 30, 255) +│ +└── Label (TextMeshPro) + - Font Size: 12 + - Color: White + - Alignment: Middle Left + - Overflow: Truncate + - Rich Text: ✅ +``` + +组件设置: +- RectTransform: + - Anchor: Stretch Horizontal + - Height: 25 + - Left: 0, Right: 0 +- Toggle: + - Target Graphic: Background + - Graphic: Background + +### 步骤4: 配置UniversalDebugTool + +在Inspector中找到 `[控制台页面]` 部分,拖拽对应的对象: + +``` +✅ Console Page → ConsolePage +✅ Console Log Scroll Rect → LogScrollView +✅ Console Log Content → LogContent +✅ Console Detail Scroll Rect → DetailScrollView +✅ Console Detail Text → DetailText +✅ Console Clear Button → ClearButton +✅ Console Lock Scroll Toggle → LockScrollToggle +✅ Console Info Filter Toggle → InfoToggle +✅ Console Warning Filter Toggle → WarningToggle +✅ Console Error Filter Toggle → ErrorToggle +✅ Console Fatal Filter Toggle → FatalToggle +✅ Console Log Item Prefab → ConsoleLogItem预制件 +``` + +### 步骤5: 测试 + +```csharp +// 在游戏开始时调用 +UniversalDebugTool.Init(); + +// 测试日志 +Debug.Log("这是一条Info日志"); +Debug.LogWarning("这是一条Warning日志"); +Debug.LogError("这是一条Error日志"); +``` + +运行游戏,点击调试工具,切换到"控制台"标签页,应该能看到日志了! + +## 🎨 样式建议 + +### 颜色方案(深色主题) +``` +Background: (20, 20, 20) +ControlPanel: (40, 40, 40) +LogItem: (30, 30, 30) +LogItem Selected: (60, 60, 60) +Text: (220, 220, 220) + +日志颜色: +Info: White (255, 255, 255) +Warning: Yellow (255, 235, 0) +Error: Red (255, 80, 80) +Fatal: Dark Red (180, 50, 50) +``` + +### 字体大小 +``` +ControlPanel按钮: 14 +Toggle标签: 12 +日志项: 11-12 +详情文本: 13-14 +``` + +### 间距 +``` +ControlPanel Spacing: 10 +LogContent Spacing: 2 +Padding: 10 +``` + +## ⚡ 性能优化提示 + +1. **限制可见日志项**: 调整LogArea高度,减少同时渲染的日志数量 +2. **日志项高度**: 25-30像素最佳,太高会影响滚动性能 +3. **最大日志数**: 默认500行,可在ConsoleModule构造函数中修改maxLine +4. **禁用不需要的过滤器**: 减少日志显示可以提升性能 + +## 🐛 常见问题 + +### Q: 日志不显示? +A: 检查: +1. Console Page是否已添加到pages字典 +2. 是否调用了UniversalDebugTool.Init() +3. 日志项预制件是否正确配置 + +### Q: 滚动不工作? +A: 检查: +1. ScrollRect的Content是否正确设置 +2. Content是否有VerticalLayoutGroup +3. Content Size Fitter是否启用 + +### Q: 点击日志无反应? +A: 检查: +1. 日志项是否有Toggle组件 +2. Toggle的Target Graphic是否设置 +3. EventSystem是否存在于场景中 + +### Q: 性能问题? +A: 尝试: +1. 减少maxLine值(默认500) +2. 减少LogArea高度 +3. 使用Text而非TextMeshPro(性能更好但效果差) +4. 关闭不需要的日志类型 + +## 📱 移动端优化 + +如果在移动设备上使用: + +1. 增大日志项高度(35-40像素) +2. 增大字体(14-16) +3. 减少最大日志数(200-300行) +4. 使用简化的日志格式(去掉帧数显示) + +## ✨ 完成! + +现在你已经有一个功能完整的Console控制台了! + +测试所有功能: +- ✅ 清除日志 +- ✅ 锁定/解锁滚动 +- ✅ 过滤不同类型日志 +- ✅ 点击查看详情 +- ✅ 滚动浏览日志 + +享受调试吧! 🎉 diff --git a/Packages/com.bywaystudios.meowmentdebugtool/CONSOLE_QUICK_SETUP.md.meta b/Packages/com.bywaystudios.meowmentdebugtool/CONSOLE_QUICK_SETUP.md.meta new file mode 100644 index 0000000..365385b --- /dev/null +++ b/Packages/com.bywaystudios.meowmentdebugtool/CONSOLE_QUICK_SETUP.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 1531af79f3e63374182c727d39576258 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.bywaystudios.meowmentdebugtool/CONSOLE_RUNTIME_READY.md b/Packages/com.bywaystudios.meowmentdebugtool/CONSOLE_RUNTIME_READY.md new file mode 100644 index 0000000..dd51251 --- /dev/null +++ b/Packages/com.bywaystudios.meowmentdebugtool/CONSOLE_RUNTIME_READY.md @@ -0,0 +1,186 @@ +# Console模块 - 运行时UI创建完成 + +## ✅ 已完成 + +Console模块的UI已经添加到RuntimeUIGenerator中,会在运行时自动创建。 + +## 🎯 使用方法 + +### 1. 直接运行测试 + +```csharp +using UnityEngine; +using MeowmentDebugTool; + +public class TestDebugTool : MonoBehaviour +{ + void Start() + { + // 初始化调试工具(会自动创建所有UI包括Console) + UniversalDebugTool.Init(); + + // 测试各种日志 + Debug.Log("这是一条普通日志"); + Debug.LogWarning("这是一条警告日志"); + Debug.LogError("这是一条错误日志"); + + // 测试异常 + try + { + throw new System.Exception("测试异常"); + } + catch (System.Exception e) + { + Debug.LogException(e); + } + } + + void Update() + { + // 按空格键生成测试日志 + if (Input.GetKeyDown(KeyCode.Space)) + { + Debug.Log($"测试日志 - 帧数: {Time.frameCount}"); + } + } +} +``` + +### 2. 运行游戏 + +1. 创建一个空GameObject +2. 添加TestDebugTool脚本 +3. 运行游戏 +4. Console页面会自动出现在标签页中 + +### 3. 测试功能 + +- ✅ **清空按钮**: 清除所有日志 +- ✅ **锁定滚动**: 自动滚动到最新日志 +- ✅ **过滤器**: 显示/隐藏不同类型的日志 + - Info (白色) + - Warning (黄色) + - Error (红色) + - Fatal (深红色) +- ✅ **点击日志**: 在下方显示详细信息和堆栈跟踪 +- ✅ **实时计数**: Toggle显示每种日志的数量 + +## 📋 UI结构说明 + +RuntimeUIGenerator会创建以下结构: + +``` +ConsolePage +├── ControlPanel (控制面板 - 高度80) +│ ├── ConsoleClearButton (清空按钮) +│ ├── ConsoleLockScrollToggle (锁定滚动) +│ ├── Spacer (弹性空白) +│ ├── ConsoleInfoFilterToggle (Info过滤) +│ ├── ConsoleWarningFilterToggle (Warning过滤) +│ ├── ConsoleErrorFilterToggle (Error过滤) +│ └── ConsoleFatalFilterToggle (Fatal过滤) +│ +├── LogArea (60% 高度) +│ └── ConsoleLogScrollView +│ └── Viewport +│ └── ConsoleLogContent (垂直布局组) +│ └── [日志项动态生成] +│ +└── DetailArea (40% 高度) + └── ConsoleDetailScrollView + └── Viewport + └── Content + └── ConsoleDetailText (详细信息) +``` + +## 🎨 样式配置 + +### 颜色方案 +- **背景**: (12, 12, 12) - 深灰色 +- **日志区域**: (5, 5, 5) - 更深的灰色 +- **详情区域**: (8, 8, 8) - 中等深灰色 +- **日志项背景**: (12, 12, 12) +- **日志项选中**: (25, 35, 45) - 蓝灰色 + +### 日志颜色(ConsoleModule中定义) +- **Info**: (255, 255, 255) - 白色 +- **Warning**: (255, 255, 0) - 黄色 +- **Error**: (255, 0, 0) - 红色 +- **Fatal**: (178, 51, 51) - 深红色 + +### 字体大小 +- Toggle标签: 24 +- 日志项: 22 +- 详情文本: 24 + +## 🔧 自定义配置 + +如果需要修改样式,可以在RuntimeUIGenerator.cs中找到CreateConsolePage方法进行调整: + +### 修改日志项高度 +```csharp +// 在CreateConsoleLogItemPrefab方法中 +rect.sizeDelta = new Vector2(0, 30); // 改为你想要的高度 +``` + +### 修改区域比例 +```csharp +// 在CreateConsolePage方法中 +logAreaLayout.flexibleHeight = 3; // 日志区域权重 +detailAreaLayout.flexibleHeight = 2; // 详情区域权重 +``` + +### 修改最大日志数 +```csharp +// 在ConsoleModule.cs构造函数中 +private int maxLine = 500; // 改为你想要的数量 +``` + +## 🚀 性能提示 + +1. **默认500行限制**: 超过的日志会自动删除 +2. **对象池优化**: 日志项会被重用,不会频繁创建销毁 +3. **按需刷新**: 只在日志变化或过滤器改变时刷新UI +4. **适中的日志项高度**: 30像素平衡了可读性和性能 + +## 📱 移动端建议 + +如果在移动设备上运行: + +```csharp +// 在CreateConsoleLogItemPrefab中调整 +labelTmp.fontSize = 28; // 增大字体 +rect.sizeDelta = new Vector2(0, 40); // 增加高度 +``` + +## 🐛 调试技巧 + +### 查看Console模块是否初始化成功 +```csharp +if (UniversalDebugTool.InstanceExists) +{ + var console = UniversalDebugTool.Instance.GetType() + .GetField("consoleModule", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance) + ?.GetValue(UniversalDebugTool.Instance); + + if (console != null) + { + Debug.Log("✅ Console模块已初始化"); + } +} +``` + +### 检查UI是否创建 +在Hierarchy中查找: +- UniversalDebugTool_Canvas + - MainWindow + - ContentContainer + - ConsolePage ← 应该能找到这个 + +## ✨ 完成! + +现在你可以直接运行游戏测试Console模块了! + +所有的UI都会在调用 `UniversalDebugTool.Init()` 时自动创建。 + +享受调试吧! 🎉 diff --git a/Packages/com.bywaystudios.meowmentdebugtool/CONSOLE_RUNTIME_READY.md.meta b/Packages/com.bywaystudios.meowmentdebugtool/CONSOLE_RUNTIME_READY.md.meta new file mode 100644 index 0000000..0c0063e --- /dev/null +++ b/Packages/com.bywaystudios.meowmentdebugtool/CONSOLE_RUNTIME_READY.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: aa6bf6f6937d0ff4da7b444f776bf41e +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.bywaystudios.meowmentdebugtool/README.md b/Packages/com.bywaystudios.meowmentdebugtool/README.md index 571fd28..809741e 100644 --- a/Packages/com.bywaystudios.meowmentdebugtool/README.md +++ b/Packages/com.bywaystudios.meowmentdebugtool/README.md @@ -1,59 +1,129 @@ -# MeowMent Debug Tool(通用调试工具) +# MeowMent Debug Tool(喵刻调试工具) -一个面向运行时的 Unity 调试工具,内置多标签页调试面板、可拖拽悬浮按钮、设备/系统信息查看,以及通过特性自动生成的“自定义调试按钮”。 +一个强大的 Unity 运行时调试工具,提供多标签页调试面板、控制台日志、可拖拽悬浮按钮、系统信息查看以及自定义调试按钮等功能。 -核心脚本: - -- `Runtime/UniversalDebugTool.cs` -- `Runtime/DraggableFloatingButton.cs` -- `Editor/DebugToolPrefabGenerator.cs` -- `Editor/CreateTabButtonPrefab.cs` +**版本:** 0.3.0 +**适用平台:** 全平台(Windows、Android、iOS等) +**Unity版本:** 2021.3 及以上 --- -## 功能概览 +## 📋 目录 -- 多标签页调试面板(默认分辨率 1080x2340): - - 参数:设备信息、系统信息,一键复制到剪贴板。 - - 自定义按钮:通过特性自动收集项目中的调试方法并生成按钮。 - - 工具栏:简单的“时间调整”示例入口,可按项目需要扩展。 - - 设置:运行时修改调试窗口尺寸、重置为默认分辨率。 -- 悬浮调试按钮: - - 关闭主窗口后,会显示一个可拖拽、自动吸附边缘的浮动按钮。 - - 点击可再次打开调试主窗口,拖动不会误触打开(内部做了拖拽判定)。 -- 运行时公共 API: - - `UniversalDebugTool.Show() / Hide() / Toggle()` 控制显示。 - - `UniversalDebugTool.ShowInputDialog(...)` 弹出输入对话框。 - - `UniversalDebugTool.Instance.ReloadCustomButtons()` 重新扫描并生成自定义按钮。 +- [功能特性](#功能特性) +- [给策划使用 - 基础功能](#给策划使用---基础功能) +- [给客户端使用 - 初始化配置](#给客户端使用---初始化配置) +- [给后端使用 - 添加自定义按钮](#给后端使用---添加自定义按钮) +- [常见问题](#常见问题) --- -## 安装 +## 🎯 功能特性 -### 方式一:本地拷贝(推荐在开发环境使用) +### 核心功能模块 +- **参数查看**:实时查看设备信息、系统信息,支持一键复制 +- **自定义按钮**:通过特性自动生成调试按钮,无需手动添加 +- **工具栏**:时间调整工具、截图隐藏功能 +- **控制台**:实时捕获Unity日志,支持过滤、搜索、查看堆栈 +- **设置**:运行时修改分辨率 -1. 将 `com.bywaystudios.meowmentdebugtool@0.1.4` 整个文件夹放到你项目的 `Packages/` 目录下。 -2. 打开 Unity,等待重新导入即可在菜单中看到调试工具相关项。 - -### 方式二:通过自建 Git / 私有仓库 - -如果你已经把此包发布到自己的 Git 仓库或私有 Package Registry,可参考 Unity 官方文档,通过修改 `Packages/manifest.json` 的 `dependencies` / `scopedRegistries` 来添加: - -```jsonc -"dependencies": { - "com.bywaystudios.meowmentdebugtool": "0.1.4" -} -``` - -> 具体部署方式视团队实际环境而定,这里不展开。 +### UI特性 +- 可拖拽悬浮按钮,自动吸附边缘 +- 多标签页设计,清晰分类 +- Canvas层级30000,始终在最上层 +- 支持自定义TextMeshPro字体 --- -## 快速开始 +## 👥 给策划使用 - 基础功能 -### 1. 在代码中初始化调试工具 +### 1. 如何打开调试工具 -调试工具现在支持**运行时自动生成UI**,无需手动创建预制体。只需在代码中调用: +游戏运行后,屏幕左侧会显示一个**蓝色圆形浮窗按钮**: + +- **点击浮窗** → 打开调试主窗口 +- **拖动浮窗** → 改变位置(会自动吸附到屏幕边缘) +- **点击左上角X** → 关闭主窗口,回到浮窗状态 + +### 2. 功能页面说明 + +调试窗口包含5个标签页: + +#### 📊 参数查看 +- **设备信息**:设备型号、系统版本、处理器、内存等 +- **系统信息**:Unity版本、图形API、显卡信息等 +- **复制按钮**:点击可复制信息到剪贴板,方便粘贴到bug报告 + +#### 🎮 自定义按钮 +- 显示后端程序员添加的调试功能按钮 +- 点击按钮即可执行对应功能(如清理存档、添加道具等) +- 不同按钮有不同颜色,方便区分 + +**常见按钮示例:** +- "清空玩家数据" - 重置游戏进度 +- "添加金币" - 增加游戏货币 +- "解锁所有关卡" - 开启所有内容 +- "重置今日任务" - 刷新每日任务 + +#### 🛠️ 工具栏 +- **时间调整**:模拟时间流逝(用于测试时间相关功能) +- **暂时隐藏(5秒)**:截图前点击此按钮,调试工具会隐藏5秒,方便截取干净的游戏画面 + +**使用场景:** +1. 需要截图游戏画面时 +2. 点击"暂时隐藏(5秒)" +3. 调试工具消失,快速截图 +4. 5秒后自动恢复,继续调试 + +#### 📝 控制台 +实时显示游戏日志,帮助定位问题: + +- **Info(白色)**:普通信息日志 +- **Warning(黄色)**:警告信息 +- **Error(红色)**:错误信息 +- **Fatal(深红)**:严重错误/异常 + +**功能按钮:** +- **清空**:清除所有日志 +- **锁定滚动**:勾选后自动滚动到最新日志 +- **过滤器**:点击Info/Warning/Error切换显示对应类型日志 + +**查看详情:** +- 点击任意日志条目 → 下方显示完整堆栈信息 +- 方便向程序员反馈详细错误 + +#### ⚙️ 设置 +- **分辨率调整**:修改游戏窗口大小(仅编辑器/Windows有效) +- **当前分辨率显示**:显示当前屏幕分辨率 + +### 3. 日常测试工作流 + +**测试新功能:** +1. 打开调试工具 +2. 切换到"自定义按钮"页面 +3. 点击相关测试按钮(如"开启活动") +4. 观察游戏效果 +5. 如有问题,切换到"控制台"查看错误 + +**截图报bug:** +1. 复现问题 +2. 点击"工具栏" → "暂时隐藏(5秒)" +3. 截取游戏画面 +4. 切换回"控制台"截取错误日志 +5. 一起提交给程序 + +**收集设备信息:** +1. 切换到"参数查看" +2. 点击"复制设备信息"或"复制系统信息" +3. 粘贴到bug报告中 + +--- + +## 💻 给客户端使用 - 初始化配置 + +### 1. 基础初始化 + +在游戏启动时(通常在启动场景的初始化脚本中)调用: ```csharp using UnityEngine; @@ -62,14 +132,14 @@ using TMPro; public class GameInitializer : MonoBehaviour { - public TMP_FontAsset customFont; // 可选:自定义字体 + [SerializeField] private TMP_FontAsset customFont; // 拖入自定义字体 void Start() { - // 初始化调试工具(自动创建UI) + // 初始化调试工具 UniversalDebugTool.Init(); - // 可选:设置自定义SDF字体 + // 设置自定义字体(可选) if (customFont != null) { UniversalDebugTool.SetSDFFont(customFont); @@ -78,27 +148,34 @@ public class GameInitializer : MonoBehaviour } ``` -**初始化说明:** +### 2. API说明 -- `UniversalDebugTool.Init()` 会自动创建完整的UI系统,包括: - - 独立的 Canvas(排序顺序 30000,保证在最上层渲染) - - 主调试窗口(含标签页、参数页、自定义按钮页、工具栏页、设置页) - - 可拖拽的调试悬浮按钮 - - EventSystem(如果场景中没有) -- `UniversalDebugTool.SetSDFFont()` 可以为所有UI文本设置自定义字体(可选) +#### `UniversalDebugTool.Init()` +初始化调试工具,自动创建: +- Canvas(排序顺序30000) +- 主调试窗口 +- 悬浮按钮 +- EventSystem(如果没有) -### 2. 运行时使用 +**注意:** +- 只需调用一次 +- 工具会自动DontDestroyOnLoad,场景切换不销毁 +- 未调用Init()前不显示任何UI -进入 Play 模式后: +#### `UniversalDebugTool.SetSDFFont(TMP_FontAsset fontAsset)` +设置所有UI文本的字体: +- 包括已创建的UI和后续创建的按钮 +- 支持中文、特殊字符 +- 建议使用支持中文的SDF字体 -- 默认显示主调试窗口在左上角 -- 点击左上角的 **X** 按钮,会隐藏主窗口并显示悬浮按钮 -- 拖动悬浮按钮改变位置(自动吸附边缘) -- 点击悬浮按钮可重新打开主窗口(拖动不会误触) +**字体推荐:** +- 思源黑体 SDF +- 阿里巴巴普惠体 SDF +- 或项目现有的中文字体 ### 3. 条件编译(推荐) -为了确保在移除包后不影响主工程,建议使用条件编译: +为了确保打包时不包含调试代码: ```csharp void Start() @@ -110,166 +187,361 @@ void Start() } ``` -`MEOWMENT_DEBUG_TOOL` 宏会在包安装时自动定义,卸载时自动移除。 +**说明:** +- `MEOWMENT_DEBUG_TOOL` 宏在包安装时自动定义 +- 移除包时自动移除,不影响项目编译 +- 正式包不会包含调试工具代码 ---- - -## 自定义调试按钮 - -调试工具会在运行时通过反射扫描所有程序集,查找带有 `DebugButtonAttribute` 特性的方法,并在“自定义按钮”标签页中为其自动生成一个按钮。 - -### 编写一个自定义调试方法 - -> 要求: -> - 方法必须是 `static`。 -> - 当前实现假定为**无参数**方法。如果需要参数,可结合 `ShowInputDialog` 自行封装。 - -示例: +### 4. 完整示例 ```csharp using UnityEngine; +using MeowmentDebugTool; +using TMPro; -public static class DemoDebugActions +public class Test : MonoBehaviour { - // 在“自定义按钮”页中会生成一个名为“打印金币数量”的按钮 - [DebugButton("打印金币数量", 0.3f, 0.6f, 1f)] - private static void PrintCoinCount() + public TMP_FontAsset fontAsset; + + void Start() { - Debug.Log($"Coins: {PlayerData.Coin}"); - } - - // 不传 displayName 时,按钮文字默认使用方法名 - [DebugButton] - private static void KillAllEnemies() - { - EnemyManager.KillAll(); + #if MEOWMENT_DEBUG_TOOL + // 初始化调试工具 + UniversalDebugTool.Init(); + + // 设置字体 + if (fontAsset != null) + { + UniversalDebugTool.SetSDFFont(fontAsset); + } + #endif } } ``` -`DebugButtonAttribute` 构造函数签名: - -```csharp -public DebugButtonAttribute(string displayName = "", float r = 0.8f, float g = 0.8f, float b = 0.8f); -``` - -- `displayName`:按钮显示文字(可为空,为空则使用方法名)。 -- `r g b`:按钮背景色的 RGB 分量(0~1)。 - -写好方法后: - -1. 进入 Play 模式。 -2. 打开调试工具 → 切换到“自定义按钮”标签页。 -3. 即可看到刚才添加的调试按钮,点击执行对应逻辑。 - -如果你在运行时新增/修改了带有 `DebugButtonAttribute` 的方法(如通过热更等方式),可以调用: - -```csharp -UniversalDebugTool.Instance.ReloadCustomButtons(); -``` - -来重新扫描并刷新按钮列表。 - --- -## 输入对话框(可选) +## 🔧 给后端使用 - 添加自定义按钮 -调试工具内置了一个简单的输入对话框,可与自定义按钮组合使用: +### 1. 基础用法 + +使用 `[DebugButton]` 特性标记静态方法,调试工具会自动生成按钮: + +```csharp +using UnityEngine; + +public static class PlayerDebugFunctions +{ + [DebugButton("清空玩家数据")] + private static void ClearPlayerData() + { + PlayerPrefs.DeleteAll(); + Debug.Log("玩家数据已清空"); + } + + [DebugButton("添加1000金币")] + private static void AddCoins() + { + PlayerData.Coins += 1000; + Debug.Log($"当前金币:{PlayerData.Coins}"); + } +} +``` + +**运行效果:** +- 在"自定义按钮"页面会出现两个按钮 +- 点击按钮执行对应方法 + +### 2. 特性参数详解 + +```csharp +[DebugButton(string displayName = "", float r = 0.8f, float g = 0.8f, float b = 0.8f)] +``` + +#### 参数说明: +- **displayName**:按钮显示文字(不填则使用方法名) +- **r, g, b**:按钮背景颜色(RGB值,范围0-1) + +#### 示例:不同颜色按钮 + +```csharp +public static class GameDebugFunctions +{ + // 绿色按钮 - 安全操作 + [DebugButton("保存游戏", 0.2f, 0.6f, 0.2f)] + private static void SaveGame() + { + GameManager.Save(); + } + + // 红色按钮 - 危险操作 + [DebugButton("删除存档", 0.9f, 0.2f, 0.2f)] + private static void DeleteSave() + { + GameManager.DeleteSave(); + } + + // 蓝色按钮 - 功能测试 + [DebugButton("跳到第10关", 0.2f, 0.5f, 0.9f)] + private static void JumpToLevel10() + { + LevelManager.LoadLevel(10); + } + + // 默认颜色(灰色) + [DebugButton("打印游戏状态")] + private static void PrintGameState() + { + Debug.Log($"Level: {GameManager.CurrentLevel}"); + } +} +``` + +### 3. 使用输入对话框 + +需要用户输入参数时,使用 `ShowInputDialog`: ```csharp using TMPro; -public static class DemoInputDebug +public static class AdvancedDebugFunctions { [DebugButton("设置玩家等级")] private static void SetPlayerLevel() { UniversalDebugTool.ShowInputDialog( - "输入玩家等级", + "输入等级", onConfirmAction: text => { - if (int.TryParse(text, out var level)) + if (int.TryParse(text, out int level)) { PlayerData.Level = level; - Debug.Log($"设置玩家等级为: {level}"); + Debug.Log($"等级已设置为:{level}"); } else { - Debug.LogWarning($"无效等级: {text}"); + Debug.LogWarning("请输入有效数字"); } }, initialValue: "1", contentType: TMP_InputField.ContentType.IntegerNumber ); } -} -``` - -调用后会弹出一个输入框,用户点击确认时会把输入字符串传回回调函数中。 - ---- - -## 公共 API 一览 - -以下方法均定义在 `UniversalDebugTool` 中: - -**初始化和配置:** -- `static void Init()`:初始化调试工具,自动创建完整的UI系统(包括Canvas、主窗口、悬浮按钮、EventSystem等)。 -- `static void SetSDFFont(TMP_FontAsset fontAsset)`:为所有UI文本设置自定义SDF字体,包括预制件模板和后续创建的按钮。 - -**显示控制:** -- `static bool InstanceExists`:当前场景中是否存在实例。 -- `static UniversalDebugTool Instance`:单例实例。 -- `static void Show()`:显示调试工具 GameObject。 -- `static void Hide()`:隐藏调试工具 GameObject。 -- `static void Toggle()`:在"窗口显示"和"窗口关闭 + 显示悬浮按钮"之间切换。 - -**自定义按钮:** -- `void ReloadCustomButtons()`:重新扫描并生成自定义按钮。 - -**输入对话框:** -- `static void ShowInputDialog(string title, Action onConfirmAction, string initialValue = "", TMP_InputField.ContentType contentType = TMP_InputField.ContentType.Standard)`:显示输入对话框。 -- `static void CloseInputDialog()`:关闭输入对话框并清理回调。 - -**信息查看:** -- `void CopyDeviceInfoToClipboard()` / `void CopySystemInfoToClipboard()`:复制对应信息到系统剪贴板。 - -你也可以在自己项目的 UI / 快捷键逻辑中调用这些方法,例如: - -```csharp -// 示例:按 F1 切换调试面板 -void Update() -{ - if (Input.GetKeyDown(KeyCode.F1)) + + [DebugButton("设置玩家名称")] + private static void SetPlayerName() { - UniversalDebugTool.Toggle(); + UniversalDebugTool.ShowInputDialog( + "输入名称", + onConfirmAction: name => + { + PlayerData.Name = name; + Debug.Log($"名称已设置为:{name}"); + }, + initialValue: PlayerData.Name, + contentType: TMP_InputField.ContentType.Standard + ); } } ``` +### 4. 条件编译(推荐) + +为了确保正式版不包含调试代码: + +```csharp +public static class ItemDebugFunctions +{ + #if MEOWMENT_DEBUG_TOOL + [DebugButton("添加道具")] + private static void AddItem() + { + ItemManager.AddItem(1001, 10); + } + #endif +} +``` + +### 5. 高级技巧 + +#### 技巧1:分类管理 +按功能模块创建不同的类: + +```csharp +// 玩家相关 +public static class PlayerDebug { ... } + +// 关卡相关 +public static class LevelDebug { ... } + +// 道具相关 +public static class ItemDebug { ... } +``` + +#### 技巧2:快速测试流程 + +```csharp +[DebugButton("快速进入战斗")] +private static void QuickEnterBattle() +{ + // 1. 设置测试数据 + PlayerData.Level = 10; + PlayerData.Coins = 9999; + + // 2. 解锁功能 + FeatureManager.UnlockAll(); + + // 3. 跳转场景 + SceneManager.LoadScene("Battle"); +} +``` + +#### 技巧3:开发辅助 + +```csharp +[DebugButton("开启所有调试选项")] +private static void EnableAllDebug() +{ + GameConfig.ShowFPS = true; + GameConfig.GodMode = true; + GameConfig.UnlimitedEnergy = true; + Debug.Log("所有调试选项已开启"); +} +``` + +### 6. 实战示例 + +```csharp +using UnityEngine; +using UnityEngine.SceneManagement; +using TMPro; + +public static class SampleDebugFunctions +{ + #region 玩家数据 + [DebugButton("重置游戏", 0.9f, 0.3f, 0.3f)] + private static void ResetGame() + { + PlayerPrefs.DeleteAll(); + SceneManager.LoadScene(0); + Debug.Log("游戏已重置"); + } + + [DebugButton("满级满资源", 0.2f, 0.8f, 0.2f)] + private static void MaxEverything() + { + PlayerData.Level = 99; + PlayerData.Coins = 999999; + PlayerData.Gems = 99999; + Debug.Log("已设置为满级满资源"); + } + #endregion + + #region 关卡测试 + [DebugButton("跳关", 0.3f, 0.6f, 0.9f)] + private static void JumpToLevel() + { + UniversalDebugTool.ShowInputDialog( + "输入关卡号", + text => + { + if (int.TryParse(text, out int level)) + { + LevelManager.LoadLevel(level); + } + }, + "1", + TMP_InputField.ContentType.IntegerNumber + ); + } + + [DebugButton("解锁所有关卡", 0.2f, 0.8f, 0.8f)] + private static void UnlockAllLevels() + { + for (int i = 0; i < 100; i++) + { + LevelManager.UnlockLevel(i); + } + Debug.Log("已解锁所有关卡"); + } + #endregion + + #region 系统测试 + [DebugButton("清理未使用资源", 0.9f, 0.5f, 0.3f)] + private static void UnloadUnusedAssets() + { + Resources.UnloadUnusedAssets(); + Debug.Log("已清理未使用的资源"); + } + + [DebugButton("触发GC")] + private static void ForceGC() + { + System.GC.Collect(); + Debug.Log("已触发垃圾回收"); + } + #endregion +} +``` + --- -## 注意事项 +## ❓ 常见问题 -**依赖:** -- Unity 新 UI(`UnityEngine.UI`) -- TextMesh Pro(`TMPro`) -- 包会自动创建 EventSystem(如果场景中没有) +### Q1: 字体显示方块/乱码怎么办? +**A:** 调用 `UniversalDebugTool.SetSDFFont()` 设置支持中文的SDF字体。 -**使用建议:** -- 默认 UI 参考分辨率为 1080×2340,适合手机竖屏调试。可在"设置"页中修改窗口大小以适配不同设备。 -- 主窗口锚点在左上角,不受分辨率变化影响。 -- 悬浮按钮可在全屏范围内拖动,会自动吸附到边缘。 -- 工具栏中的"时间调整"逻辑仅是示例,需要在 `AdjustGameTime` 中实现实际的时间修改逻辑。 +### Q2: 自定义按钮不显示? +**A:** 检查: +- 方法是否为 `static` +- 是否添加了 `[DebugButton]` 特性 +- 是否在 `#if MEOWMENT_DEBUG_TOOL` 内 -**字体设置:** -- 所有 UI 文本默认使用 TMP 的默认字体。 -- 调用 `SetSDFFont()` 可以为所有文本(包括后续创建的自定义按钮)设置自定义字体。 +### Q3: 第二次点击"暂时隐藏"无效? +**A:** 等待上一次隐藏结束,或查看控制台是否有"已经在隐藏状态中"的警告。 -**条件编译:** -- 建议使用 `#if MEOWMENT_DEBUG_TOOL` 包裹调试工具相关代码。 -- 包安装时会自动定义 `MEOWMENT_DEBUG_TOOL` 宏,卸载时自动移除。 -- 这样可以确保移除包后不影响主工程编译。 +### Q4: 如何在正式版移除调试工具? +**A:** 删除Packages目录下的工具包文件夹即可,`#if MEOWMENT_DEBUG_TOOL` 包裹的代码会自动失效。 -**进一步定制:** -如需修改 UI 布局、颜色、标签页等,可以直接修改 `RuntimeUIGenerator.cs` 中的 UI 生成逻辑。 +### Q5: 控制台日志太多怎么办? +**A:** +- 点击"清空"按钮清除日志 +- 使用过滤器只显示Error/Warning +- 工具默认只保留最新100条日志 + +### Q6: 可以在多个场景使用吗? +**A:** 可以,工具使用DontDestroyOnLoad,场景切换不会销毁。 + +--- + +## 📝 更新日志 + +### [0.3.0] - 2025-12-22 +- ✨ 新增控制台模块,实时显示Unity日志 +- ✨ 新增暂时隐藏功能,方便截图 +- 🔨 重构代码架构,模块化设计 +- 🐛 修复多个UI布局问题 + +### [0.2.2] - 2025-12-19 +- 🔨 默认显示浮窗而非主窗口 +- 🐛 优化初始化逻辑 + +详见 [CHANGELOG.md](CHANGELOG.md) + +--- + +## 📦 依赖 + +- Unity 2021.3+ +- TextMesh Pro (com.unity.textmeshpro) +- Unity UI (com.unity.ugui) + +--- + +## 📧 联系方式 + +如有问题或建议,请联系开发团队。 + +--- + +**祝调试顺利!** 🐱 diff --git a/Packages/com.bywaystudios.meowmentdebugtool/REFACTORING_NOTES.md b/Packages/com.bywaystudios.meowmentdebugtool/REFACTORING_NOTES.md new file mode 100644 index 0000000..9a4747a --- /dev/null +++ b/Packages/com.bywaystudios.meowmentdebugtool/REFACTORING_NOTES.md @@ -0,0 +1,111 @@ +# MeowmentDebugTool 模块化重构说明 + +## 重构概述 + +已将 UniversalDebugTool 的4个主要功能模块分离成独立的脚本文件,使代码结构更清晰、更易维护。 + +## 新增文件 + +### 1. IDebugModule.cs +- **作用**: 调试模块的基础接口 +- **方法**: + - `Initialize()`: 初始化模块 + - `GetPage()`: 获取模块的UI页面GameObject + - `GetModuleName()`: 获取模块名称 + +### 2. ParametersModule.cs +- **作用**: 参数查看模块,显示设备和系统信息 +- **主要功能**: + - 显示设备信息(设备名称、型号、处理器等) + - 显示系统信息(Unity版本、平台、分辨率、图形设备等) + - 复制信息到剪贴板 + - 刷新信息 + +### 3. CustomButtonsModule.cs +- **作用**: 自定义按钮模块,通过反射加载标记为DebugButton的方法 +- **主要功能**: + - 自动扫描并加载所有带 `[DebugButton]` 特性的静态方法 + - 动态创建按钮UI + - 设置SDF字体 + - 支持自定义按钮回调 + - 点击按钮后自动关闭调试窗口 + +### 4. ToolbarModule.cs +- **作用**: 工具栏模块,提供时间调整等工具 +- **主要功能**: + - 时间调整滑块(支持秒和分钟显示) + - 增加/减少时间按钮 + - 可扩展的游戏时间调整接口 + +### 5. SettingsModule.cs +- **作用**: 设置模块,提供分辨率设置等功能 +- **主要功能**: + - 自定义窗口分辨率 + - 重置为默认分辨率 + - 实时显示当前窗口尺寸 + +## UniversalDebugTool.cs 的改动 + +### 主要变化 + +1. **移除了具体实现代码**: + - 参数查看功能 → ParametersModule + - 自定义按钮功能 → CustomButtonsModule + - 工具栏功能 → ToolbarModule + - 分辨率设置功能 → SettingsModule + +2. **新增模块管理**: + ```csharp + private ParametersModule parametersModule; + private CustomButtonsModule customButtonsModule; + private ToolbarModule toolbarModule; + private SettingsModule settingsModule; + private List allModules = new List(); + ``` + +3. **初始化流程**: + - `InitializeDebugTool()`: 创建所有模块实例并注册页面 + - `InitializeAllModules()`: 调用各模块的Initialize方法 + +4. **保留的公共API**: + - `CopyDeviceInfoToClipboard()` - 委托给 ParametersModule + - `CopySystemInfoToClipboard()` - 委托给 ParametersModule + - `RefreshAllInfo()` - 委托给 ParametersModule + - `SetCustomButtonCallback()` - 委托给 CustomButtonsModule + - `ReloadCustomButtons()` - 委托给 CustomButtonsModule + +## 优势 + +1. **职责分离**: 每个模块只负责一个功能领域 +2. **易于维护**: 修改某个功能时只需要编辑对应的模块文件 +3. **可扩展性**: 添加新模块只需实现 IDebugModule 接口 +4. **代码清晰**: UniversalDebugTool.cs 现在主要负责协调各模块,代码更简洁 + +## 使用方式 + +使用方式完全不变,所有现有的API和功能保持兼容: + +```csharp +// 初始化调试工具 +UniversalDebugTool.Init(); + +// 设置字体 +UniversalDebugTool.SetSDFFont(myFont); + +// 刷新信息 +UniversalDebugTool.Instance.RefreshAllInfo(); + +// 自定义按钮仍然使用相同的特性 +[DebugButton("测试按钮")] +public static void TestMethod() +{ + Debug.Log("测试"); +} +``` + +## 未来扩展 + +如果需要添加新的调试功能模块,只需: +1. 创建新的类并实现 `IDebugModule` 接口 +2. 在 `UniversalDebugTool.InitializeDebugTool()` 中实例化并添加到 `allModules` 列表 +3. 调用 `InitializeAllModules()` 会自动初始化新模块 diff --git a/Packages/com.bywaystudios.meowmentdebugtool/REFACTORING_NOTES.md.meta b/Packages/com.bywaystudios.meowmentdebugtool/REFACTORING_NOTES.md.meta new file mode 100644 index 0000000..0b6f1dd --- /dev/null +++ b/Packages/com.bywaystudios.meowmentdebugtool/REFACTORING_NOTES.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 7a75e76e7a8253b4a97f7c353af12a0f +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.bywaystudios.meowmentdebugtool/Runtime/ConsoleModule.cs b/Packages/com.bywaystudios.meowmentdebugtool/Runtime/ConsoleModule.cs new file mode 100644 index 0000000..2a6204d --- /dev/null +++ b/Packages/com.bywaystudios.meowmentdebugtool/Runtime/ConsoleModule.cs @@ -0,0 +1,579 @@ +using System; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; +using TMPro; + +namespace MeowmentDebugTool +{ + /// + /// 控制台模块 - 显示Unity日志(基于UGUI实现) + /// + public class ConsoleModule : IDebugModule + { + #region 字段 + private GameObject consolePage; + + // UI组件 + private ScrollRect logScrollRect; + private RectTransform logContent; + private ScrollRect detailScrollRect; + private TMP_Text detailText; + + // 按钮 + private Button clearButton; + private Toggle lockScrollToggle; + private Toggle infoFilterToggle; + private Toggle warningFilterToggle; + private Toggle errorFilterToggle; + private Toggle fatalFilterToggle; + + // 日志项预制件 + private GameObject logItemPrefab; + + // 日志数据 + private Queue logNodes = new Queue(); + private LogNode selectedNode = null; + + // 日志计数 + private int infoCount = 0; + private int warningCount = 0; + private int errorCount = 0; + private int fatalCount = 0; + + // 设置 + private bool lockScroll = true; + private int maxLine = 200; + private bool infoFilter = true; + private bool warningFilter = true; + private bool errorFilter = true; + private bool fatalFilter = true; + + // 颜色设置 + private Color32 infoColor = Color.white; + private Color32 warningColor = Color.yellow; + private Color32 errorColor = Color.red; + private Color32 fatalColor = new Color(0.7f, 0.2f, 0.2f); + + // UI对象池 + private List logItemPool = new List(); + private List activeLogItems = new List(); + + // 保存的字体 + private TMP_FontAsset savedFontAsset = null; + + // 延迟刷新标记 + private bool needRefresh = false; + private int lastRefreshFrame = -1; + #endregion + + #region 构造函数 + public ConsoleModule(GameObject page, ScrollRect logScroll, RectTransform logContentTransform, + ScrollRect detailScroll, TMP_Text detail, + Button clearBtn, Toggle lockToggle, Toggle infoToggle, Toggle warningToggle, + Toggle errorToggle, Toggle fatalToggle, GameObject logItemPrefabObj) + { + consolePage = page; + logScrollRect = logScroll; + logContent = logContentTransform; + detailScrollRect = detailScroll; + detailText = detail; + clearButton = clearBtn; + lockScrollToggle = lockToggle; + infoFilterToggle = infoToggle; + warningFilterToggle = warningToggle; + errorFilterToggle = errorToggle; + fatalFilterToggle = fatalToggle; + logItemPrefab = logItemPrefabObj; + } + #endregion + + #region IDebugModule 实现 + public void Initialize() + { + Debug.Log("[ConsoleModule] 初始化控制台模块..."); + + // 检查必要的组件 + if (consolePage == null) + Debug.LogError("[ConsoleModule] consolePage is null!"); + if (logScrollRect == null) + Debug.LogError("[ConsoleModule] logScrollRect is null!"); + if (logContent == null) + Debug.LogError("[ConsoleModule] logContent is null!"); + if (detailScrollRect == null) + Debug.LogError("[ConsoleModule] detailScrollRect is null!"); + if (detailText == null) + Debug.LogError("[ConsoleModule] detailText is null!"); + if (clearButton == null) + Debug.LogError("[ConsoleModule] clearButton is null!"); + if (logItemPrefab == null) + Debug.LogError("[ConsoleModule] logItemPrefab is null!"); + + // 注册Unity日志回调 + Application.logMessageReceived += OnLogMessageReceived; + + // 设置按钮事件 + if (clearButton != null) + clearButton.onClick.AddListener(ClearAllLogs); + + // 设置Toggle事件 + if (lockScrollToggle != null) + { + lockScrollToggle.isOn = lockScroll; + lockScrollToggle.onValueChanged.AddListener(OnLockScrollChanged); + } + + if (infoFilterToggle != null) + { + infoFilterToggle.isOn = infoFilter; + infoFilterToggle.onValueChanged.AddListener(OnInfoFilterChanged); + } + + if (warningFilterToggle != null) + { + warningFilterToggle.isOn = warningFilter; + warningFilterToggle.onValueChanged.AddListener(OnWarningFilterChanged); + } + + if (errorFilterToggle != null) + { + errorFilterToggle.isOn = errorFilter; + errorFilterToggle.onValueChanged.AddListener(OnErrorFilterChanged); + } + + if (fatalFilterToggle != null) + { + fatalFilterToggle.isOn = fatalFilter; + fatalFilterToggle.onValueChanged.AddListener(OnFatalFilterChanged); + } + + // 清空详情 + if (detailText != null) + detailText.text = "点击日志查看详细信息..."; + + Debug.Log("[ConsoleModule] 控制台模块初始化完成"); + } + + public GameObject GetPage() + { + return consolePage; + } + + public string GetModuleName() + { + return "控制台"; + } + #endregion + + #region 公共方法 + /// + /// 设置SDF字体 + /// + public void SetSDFFont(TMP_FontAsset fontAsset) + { + savedFontAsset = fontAsset; + + // 应用到详情文本 + if (detailText != null && fontAsset != null) + { + detailText.font = fontAsset; + } + + // 刷新已有日志项 + RefreshLogDisplay(); + } + + /// + /// 获取日志统计信息 + /// + public void GetLogCount(out int info, out int warning, out int error, out int fatal) + { + RefreshCount(); + info = infoCount; + warning = warningCount; + error = errorCount; + fatal = fatalCount; + } + + /// + /// 更新(每帧调用) + /// + public void Update() + { + // 如果页面未激活,跳过更新 + if (consolePage == null || !consolePage.activeSelf) + { + return; + } + + // 先处理延迟刷新 + if (needRefresh) + { + needRefresh = false; + lastRefreshFrame = Time.frameCount; + RefreshLogDisplay(); + } + + // 如果锁定滚动,自动滚动到底部 + // 只在刷新后的下一帧设置,避免rebuild loop + if (lockScroll && logScrollRect != null && Time.frameCount > lastRefreshFrame + 1) + { + logScrollRect.verticalNormalizedPosition = 0f; + } + + // 更新Toggle文本显示 + UpdateFilterToggleText(); + } + + /// + /// 销毁 + /// + public void Shutdown() + { + Application.logMessageReceived -= OnLogMessageReceived; + ClearAllLogs(); + } + #endregion + + #region 私有方法 + private void OnLogMessageReceived(string logMessage, string stackTrace, LogType logType) + { + // 将Assert转换为Error + if (logType == LogType.Assert) + { + logType = LogType.Error; + } + + // 创建日志节点 + LogNode logNode = LogNode.Create(logType, logMessage, stackTrace); + logNodes.Enqueue(logNode); + + // 限制最大行数 + while (logNodes.Count > maxLine) + { + logNodes.Dequeue(); + } + + // 标记需要刷新,而不是立即刷新(避免rebuild loop) + needRefresh = true; + } + + private void RefreshLogDisplay() + { + // 如果页面未激活,延迟刷新到下次页面激活时 + if (consolePage == null || !consolePage.activeSelf) + { + needRefresh = true; + return; + } + + if (logContent == null) + { + Debug.LogError("[ConsoleModule] logContent is null!"); + return; + } + + if (logItemPrefab == null) + { + Debug.LogError("[ConsoleModule] logItemPrefab is null!"); + return; + } + + // 回收所有激活的日志项 + foreach (var item in activeLogItems) + { + if (item != null) + { + item.SetActive(false); + if (!logItemPool.Contains(item)) + { + logItemPool.Add(item); + } + } + } + activeLogItems.Clear(); + + // 遍历日志节点并创建UI + int index = 0; + int displayCount = 0; + foreach (LogNode logNode in logNodes) + { + // 根据过滤器判断是否显示 + if (!ShouldShowLog(logNode.LogType)) + continue; + + // 从对象池获取或创建日志项 + GameObject logItem = GetLogItemFromPool(); + if (logItem == null) + { + Debug.LogError("[ConsoleModule] Failed to get log item from pool!"); + continue; + } + + logItem.transform.SetParent(logContent, false); + logItem.SetActive(true); + activeLogItems.Add(logItem); + + // 调试:确认对象状态 + if (!logItem.activeSelf) + { + Debug.LogWarning($"[ConsoleModule] LogItem {logItem.name} is not active after SetActive(true)!"); + } + + // 设置日志项内容 + SetupLogItem(logItem, logNode, index); + + index++; + displayCount++; + } + } + + private bool ShouldShowLog(LogType logType) + { + switch (logType) + { + case LogType.Log: + return infoFilter; + case LogType.Warning: + return warningFilter; + case LogType.Error: + return errorFilter; + case LogType.Exception: + return fatalFilter; + default: + return true; + } + } + + private GameObject GetLogItemFromPool() + { + if (logItemPool.Count > 0) + { + GameObject item = logItemPool[0]; + logItemPool.RemoveAt(0); + return item; + } + else + { + if (logItemPrefab == null) + { + Debug.LogError("[ConsoleModule] logItemPrefab is null!"); + return null; + } + + // 创建新的日志项 + GameObject newItem = UnityEngine.Object.Instantiate(logItemPrefab); + newItem.name = "LogItem_" + activeLogItems.Count; + return newItem; + } + } + + private void SetupLogItem(GameObject logItem, LogNode logNode, int index) + { + if (logItem == null) + { + Debug.LogError("[ConsoleModule] logItem is null in SetupLogItem!"); + return; + } + + // 获取日志项的Toggle和Text组件 + Toggle toggle = logItem.GetComponent(); + TMP_Text text = logItem.GetComponentInChildren(); + + if (toggle == null) + { + Debug.LogWarning($"[ConsoleModule] Toggle component not found on {logItem.name}!"); + } + + if (text == null) + { + Debug.LogWarning($"[ConsoleModule] TMP_Text component not found on {logItem.name}!"); + return; + } + + // 设置文本内容和颜色 + Color32 color = GetLogStringColor(logNode.LogType); + string logText = GetLogString(logNode); + + text.text = logText; + text.color = color; + + // 应用字体 + if (savedFontAsset != null) + { + text.font = savedFontAsset; + } + + if (toggle != null) + { + // 设置Toggle状态 + toggle.isOn = (selectedNode == logNode); + + // 设置Toggle事件 + toggle.onValueChanged.RemoveAllListeners(); + toggle.onValueChanged.AddListener((isOn) => + { + if (isOn) + { + OnLogItemSelected(logNode); + } + }); + } + } + + private void OnLogItemSelected(LogNode logNode) + { + selectedNode = logNode; + + // 显示详细信息 + if (detailText != null && logNode != null) + { + Color32 color = GetLogStringColor(logNode.LogType); + string colorHex = ColorUtility.ToHtmlStringRGBA(color); + + string detailInfo = $"{logNode.LogMessage}\n\n"; + detailInfo += $"时间: {logNode.LogTime.ToLocalTime():HH:mm:ss.fff}\n"; + detailInfo += $"帧数: {logNode.LogFrameCount}\n"; + detailInfo += $"类型: {logNode.LogType}\n\n"; + + if (!string.IsNullOrEmpty(logNode.StackTrace)) + { + detailInfo += $"堆栈跟踪:\n{logNode.StackTrace}"; + } + + detailText.text = detailInfo; + } + + // 重置详情滚动位置 + if (detailScrollRect != null) + { + detailScrollRect.verticalNormalizedPosition = 1f; + } + } + + private string GetLogString(LogNode logNode) + { + return $"[{logNode.LogTime.ToLocalTime():HH:mm:ss.fff}][{logNode.LogFrameCount}] {logNode.LogMessage}"; + } + + private Color32 GetLogStringColor(LogType logType) + { + switch (logType) + { + case LogType.Log: + return infoColor; + case LogType.Warning: + return warningColor; + case LogType.Error: + return errorColor; + case LogType.Exception: + return fatalColor; + default: + return Color.white; + } + } + + private void RefreshCount() + { + infoCount = 0; + warningCount = 0; + errorCount = 0; + fatalCount = 0; + + foreach (LogNode logNode in logNodes) + { + switch (logNode.LogType) + { + case LogType.Log: + infoCount++; + break; + case LogType.Warning: + warningCount++; + break; + case LogType.Error: + errorCount++; + break; + case LogType.Exception: + fatalCount++; + break; + } + } + } + + private void UpdateFilterToggleText() + { + RefreshCount(); + + if (infoFilterToggle != null) + { + var label = infoFilterToggle.GetComponentInChildren(); + if (label != null) + label.text = $"Info ({infoCount})"; + } + + if (warningFilterToggle != null) + { + var label = warningFilterToggle.GetComponentInChildren(); + if (label != null) + label.text = $"Warning ({warningCount})"; + } + + if (errorFilterToggle != null) + { + var label = errorFilterToggle.GetComponentInChildren(); + if (label != null) + label.text = $"Error ({errorCount})"; + } + + if (fatalFilterToggle != null) + { + var label = fatalFilterToggle.GetComponentInChildren(); + if (label != null) + label.text = $"Fatal ({fatalCount})"; + } + } + + private void ClearAllLogs() + { + logNodes.Clear(); + selectedNode = null; + + if (detailText != null) + detailText.text = "点击日志查看详细信息..."; + + RefreshLogDisplay(); + + Debug.Log("[ConsoleModule] 已清空所有日志"); + } + + private void OnLockScrollChanged(bool value) + { + lockScroll = value; + } + + private void OnInfoFilterChanged(bool value) + { + infoFilter = value; + needRefresh = true; + } + + private void OnWarningFilterChanged(bool value) + { + warningFilter = value; + needRefresh = true; + } + + private void OnErrorFilterChanged(bool value) + { + errorFilter = value; + needRefresh = true; + } + + private void OnFatalFilterChanged(bool value) + { + fatalFilter = value; + needRefresh = true; + } + #endregion + } +} diff --git a/Packages/com.bywaystudios.meowmentdebugtool/Runtime/ConsoleModule.cs.meta b/Packages/com.bywaystudios.meowmentdebugtool/Runtime/ConsoleModule.cs.meta new file mode 100644 index 0000000..d9de75b --- /dev/null +++ b/Packages/com.bywaystudios.meowmentdebugtool/Runtime/ConsoleModule.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1660ec87345f07e4992b23d662f497dc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.bywaystudios.meowmentdebugtool/Runtime/CustomButtonsModule.cs b/Packages/com.bywaystudios.meowmentdebugtool/Runtime/CustomButtonsModule.cs new file mode 100644 index 0000000..f7d3205 --- /dev/null +++ b/Packages/com.bywaystudios.meowmentdebugtool/Runtime/CustomButtonsModule.cs @@ -0,0 +1,180 @@ +using System; +using System.Reflection; +using UnityEngine; +using UnityEngine.UI; +using TMPro; + +namespace MeowmentDebugTool +{ + /// + /// 自定义按钮模块 - 通过反射加载标记为DebugButton的方法 + /// + public class CustomButtonsModule : IDebugModule + { + #region 字段 + private GameObject customButtonsPage; + private RectTransform buttonContainer; + private GameObject buttonPrefab; + private ScrollRect buttonsScrollRect; + + // 自定义按钮回调 + private Action customButtonCallback; + + // 保存的SDF字体资源 + private TMP_FontAsset savedFontAsset = null; + + // 关闭窗口回调 + private Action onCloseWindowCallback; + #endregion + + #region 构造函数 + public CustomButtonsModule(GameObject page, RectTransform container, GameObject prefab, + ScrollRect scrollRect, Action closeWindowCallback) + { + customButtonsPage = page; + buttonContainer = container; + buttonPrefab = prefab; + buttonsScrollRect = scrollRect; + onCloseWindowCallback = closeWindowCallback; + } + #endregion + + #region IDebugModule 实现 + public void Initialize() + { + Debug.Log("[CustomButtonsModule] 初始化自定义按钮模块..."); + LoadCustomButtons(); + } + + public GameObject GetPage() + { + return customButtonsPage; + } + + public string GetModuleName() + { + return "自定义按钮"; + } + #endregion + + #region 公共方法 + /// + /// 设置SDF字体 + /// + public void SetSDFFont(TMP_FontAsset fontAsset) + { + savedFontAsset = fontAsset; + } + + /// + /// 设置自定义按钮回调 + /// + public void SetCustomButtonCallback(Action callback) + { + customButtonCallback = callback; + } + + /// + /// 重新加载自定义按钮 + /// + public void ReloadCustomButtons() + { + LoadCustomButtons(); + } + #endregion + + #region 私有方法 + /// + /// 加载所有自定义按钮(使用反射) + /// + private void LoadCustomButtons() + { + if (buttonContainer == null || buttonPrefab == null) + { + Debug.LogWarning("[CustomButtonsModule] 按钮容器或按钮预制件未设置"); + return; + } + + // 清空现有按钮 + foreach (Transform child in buttonContainer) + { + UnityEngine.Object.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(); + if (attribute != null) + { + CreateCustomButton(method, attribute); + } + } + } + } + catch (Exception e) + { + // 某些程序集可能无法访问,跳过 + Debug.LogWarning($"[CustomButtonsModule] 无法访问程序集 {assembly.FullName}: {e.Message}"); + } + } + } + + private void CreateCustomButton(MethodInfo method, DebugButtonAttribute attribute) + { + GameObject buttonObj = UnityEngine.Object.Instantiate(buttonPrefab, buttonContainer); + Button button = buttonObj.GetComponent