3.0.17提交
This commit is contained in:
parent
bdb24b05d1
commit
61a51df887
@ -1,215 +0,0 @@
|
||||
# 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<TMP_FontAsset>("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正常显示日志
|
||||
- ✅ 过滤器正常工作
|
||||
- ✅ 滚动功能正常
|
||||
- ⚠️ 可能仍有字体警告(如果没有设置中文字体)
|
||||
|
||||
字体警告不影响功能,只是文本显示为方块。如果需要中文显示,请按照上述方案添加中文字体。
|
||||
@ -1,7 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3d6ac60c7ac1db84299fc8dde7508c38
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -5,6 +5,29 @@
|
||||
格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/),
|
||||
版本号遵循 [Semantic Versioning](https://semver.org/lang/zh-CN/)。
|
||||
|
||||
## [0.3.11] - 2025-12-30
|
||||
|
||||
### Added
|
||||
|
||||
- 添加按钮分组功能
|
||||
|
||||
## [0.3.9] - 2025-12-25
|
||||
|
||||
### Added
|
||||
- **数值模块运行时控制**
|
||||
- 新增 `UpdateDebugValue()` 公开接口,支持运行时动态修改数值特性的当前值
|
||||
- 支持完整方法名("ClassName.MethodName")或简单方法名("MethodName")查找
|
||||
- 自动限制值在特性定义的最小值和最大值范围内
|
||||
- 同步更新UI滑块和输入框显示
|
||||
|
||||
## [0.3.8] - 2025-12-25
|
||||
### Improved
|
||||
- **控制台模块性能优化**
|
||||
- 默认不显示Info和Warning类型日志(Error和Fatal保持默认显示)
|
||||
- 对于被过滤的日志类型不创建LogItem UI元素,大幅减少初始内存占用和UI开销
|
||||
- 仅在用户手动激活对应过滤器时才创建和显示相关日志UI
|
||||
- 显著提升启动性能,特别是在大量Info/Warning日志场景下
|
||||
|
||||
|
||||
|
||||
## [0.3.7] - 2025-12-24
|
||||
|
||||
@ -1,236 +0,0 @@
|
||||
# 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) - 模块化重构说明
|
||||
@ -1,7 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9bc4f10ea5e6a9d49b550c5aec275f1a
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -1,171 +0,0 @@
|
||||
# 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组件 |
|
||||
|
||||
## 未来扩展
|
||||
|
||||
可以考虑添加的功能:
|
||||
- [ ] 搜索/过滤日志内容
|
||||
- [ ] 导出日志到文件
|
||||
- [ ] 日志标签/分类
|
||||
- [ ] 日志统计图表
|
||||
- [ ] 复制日志到剪贴板
|
||||
- [ ] 日志时间范围过滤
|
||||
@ -1,7 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2e77813ce2d912b42b65134e2ce344f9
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -1,233 +0,0 @@
|
||||
# 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控制台了!
|
||||
|
||||
测试所有功能:
|
||||
- ✅ 清除日志
|
||||
- ✅ 锁定/解锁滚动
|
||||
- ✅ 过滤不同类型日志
|
||||
- ✅ 点击查看详情
|
||||
- ✅ 滚动浏览日志
|
||||
|
||||
享受调试吧! 🎉
|
||||
@ -1,7 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1531af79f3e63374182c727d39576258
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -1,186 +0,0 @@
|
||||
# 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()` 时自动创建。
|
||||
|
||||
享受调试吧! 🎉
|
||||
@ -1,7 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aa6bf6f6937d0ff4da7b444f776bf41e
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -47,14 +47,14 @@ void Start()
|
||||
```csharp
|
||||
public static class DebugFunctions
|
||||
{
|
||||
[DebugButton("清空数据")]
|
||||
[DebugButton("分组1","清空数据")]
|
||||
private static void ClearData()
|
||||
{
|
||||
PlayerPrefs.DeleteAll();
|
||||
}
|
||||
|
||||
// 设置按钮颜色 (r, g, b)
|
||||
[DebugButton("添加金币", 0.2f, 0.8f, 0.2f)]
|
||||
[DebugButton("分组1","添加金币", 0.2f, 0.8f, 0.2f)]
|
||||
private static void AddCoins()
|
||||
{
|
||||
PlayerData.Coins += 1000;
|
||||
|
||||
@ -1,111 +0,0 @@
|
||||
# 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<IDebugModule> allModules = new List<IDebugModule>();
|
||||
```
|
||||
|
||||
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()` 会自动初始化新模块
|
||||
@ -1,7 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7a75e76e7a8253b4a97f7c353af12a0f
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -28,11 +28,18 @@ namespace MeowmentDebugTool
|
||||
private Toggle errorFilterToggle;
|
||||
private Toggle fatalFilterToggle;
|
||||
|
||||
// 文字筛选
|
||||
private TMP_InputField textFilterInputField;
|
||||
private string textFilter = "";
|
||||
|
||||
// 日志项预制件
|
||||
private GameObject logItemPrefab;
|
||||
|
||||
// 日志数据
|
||||
private Queue<LogNode> logNodes = new Queue<LogNode>();
|
||||
// 日志数据 - 分离的缓存池
|
||||
private Queue<LogNode> infoLogNodes = new Queue<LogNode>();
|
||||
private Queue<LogNode> warningLogNodes = new Queue<LogNode>();
|
||||
private Queue<LogNode> errorLogNodes = new Queue<LogNode>();
|
||||
private Queue<LogNode> fatalLogNodes = new Queue<LogNode>();
|
||||
private LogNode selectedNode = null;
|
||||
|
||||
// 日志计数
|
||||
@ -43,9 +50,12 @@ namespace MeowmentDebugTool
|
||||
|
||||
// 设置
|
||||
private bool lockScroll = true;
|
||||
private int maxLine = 200;
|
||||
private bool infoFilter = true;
|
||||
private bool warningFilter = true;
|
||||
private int maxInfoLines = 50;
|
||||
private int maxWarningLines = 50;
|
||||
private int maxErrorLines = 50;
|
||||
private int maxFatalLines = 50;
|
||||
private bool infoFilter = false; // 默认不显示Info
|
||||
private bool warningFilter = false; // 默认不显示Warning
|
||||
private bool errorFilter = true;
|
||||
private bool fatalFilter = true;
|
||||
|
||||
@ -71,7 +81,7 @@ namespace MeowmentDebugTool
|
||||
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)
|
||||
Toggle errorToggle, Toggle fatalToggle, TMP_InputField textFilterInput, GameObject logItemPrefabObj)
|
||||
{
|
||||
consolePage = page;
|
||||
logScrollRect = logScroll;
|
||||
@ -84,6 +94,7 @@ namespace MeowmentDebugTool
|
||||
warningFilterToggle = warningToggle;
|
||||
errorFilterToggle = errorToggle;
|
||||
fatalFilterToggle = fatalToggle;
|
||||
textFilterInputField = textFilterInput;
|
||||
logItemPrefab = logItemPrefabObj;
|
||||
}
|
||||
#endregion
|
||||
@ -147,6 +158,13 @@ namespace MeowmentDebugTool
|
||||
fatalFilterToggle.onValueChanged.AddListener(OnFatalFilterChanged);
|
||||
}
|
||||
|
||||
// 设置文字筛选输入框事件
|
||||
if (textFilterInputField != null)
|
||||
{
|
||||
textFilterInputField.text = textFilter;
|
||||
textFilterInputField.onValueChanged.AddListener(OnTextFilterChanged);
|
||||
}
|
||||
|
||||
// 清空详情
|
||||
if (detailText != null)
|
||||
detailText.text = "点击日志查看详细信息...";
|
||||
@ -195,6 +213,29 @@ namespace MeowmentDebugTool
|
||||
fatal = fatalCount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置各类型日志的最大缓存数量
|
||||
/// </summary>
|
||||
public void SetMaxLogLines(int maxInfo, int maxWarning, int maxError, int maxFatal)
|
||||
{
|
||||
maxInfoLines = Mathf.Max(1, maxInfo);
|
||||
maxWarningLines = Mathf.Max(1, maxWarning);
|
||||
maxErrorLines = Mathf.Max(1, maxError);
|
||||
maxFatalLines = Mathf.Max(1, maxFatal);
|
||||
Debug.Log($"[ConsoleModule] 设置缓存池大小 - Info:{maxInfoLines}, Warning:{maxWarningLines}, Error:{maxErrorLines}, Fatal:{maxFatalLines}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取各类型日志的最大缓存数量
|
||||
/// </summary>
|
||||
public void GetMaxLogLines(out int maxInfo, out int maxWarning, out int maxError, out int maxFatal)
|
||||
{
|
||||
maxInfo = maxInfoLines;
|
||||
maxWarning = maxWarningLines;
|
||||
maxError = maxErrorLines;
|
||||
maxFatal = maxFatalLines;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新(每帧调用)
|
||||
/// </summary>
|
||||
@ -246,12 +287,40 @@ namespace MeowmentDebugTool
|
||||
|
||||
// 创建日志节点
|
||||
LogNode logNode = LogNode.Create(logType, logMessage, stackTrace);
|
||||
logNodes.Enqueue(logNode);
|
||||
|
||||
// 限制最大行数
|
||||
while (logNodes.Count > maxLine)
|
||||
// 根据类型添加到对应的缓存池
|
||||
Queue<LogNode> targetQueue = null;
|
||||
int maxLines = 0;
|
||||
|
||||
switch (logType)
|
||||
{
|
||||
logNodes.Dequeue();
|
||||
case LogType.Log:
|
||||
targetQueue = infoLogNodes;
|
||||
maxLines = maxInfoLines;
|
||||
break;
|
||||
case LogType.Warning:
|
||||
targetQueue = warningLogNodes;
|
||||
maxLines = maxWarningLines;
|
||||
break;
|
||||
case LogType.Error:
|
||||
targetQueue = errorLogNodes;
|
||||
maxLines = maxErrorLines;
|
||||
break;
|
||||
case LogType.Exception:
|
||||
targetQueue = fatalLogNodes;
|
||||
maxLines = maxFatalLines;
|
||||
break;
|
||||
}
|
||||
|
||||
if (targetQueue != null)
|
||||
{
|
||||
targetQueue.Enqueue(logNode);
|
||||
|
||||
// 限制该类型的最大行数
|
||||
while (targetQueue.Count > maxLines)
|
||||
{
|
||||
targetQueue.Dequeue();
|
||||
}
|
||||
}
|
||||
|
||||
// 标记需要刷新,而不是立即刷新(避免rebuild loop)
|
||||
@ -293,13 +362,22 @@ namespace MeowmentDebugTool
|
||||
}
|
||||
activeLogItems.Clear();
|
||||
|
||||
// 遍历日志节点并创建UI
|
||||
// 遍历日志节点并创建UI(性能优化:只为需要显示的日志创建LogItem)
|
||||
int index = 0;
|
||||
int displayCount = 0;
|
||||
foreach (LogNode logNode in logNodes)
|
||||
|
||||
// 合并所有缓存池的日志,按时间排序
|
||||
List<LogNode> allLogs = new List<LogNode>();
|
||||
allLogs.AddRange(infoLogNodes);
|
||||
allLogs.AddRange(warningLogNodes);
|
||||
allLogs.AddRange(errorLogNodes);
|
||||
allLogs.AddRange(fatalLogNodes);
|
||||
allLogs.Sort((a, b) => a.LogTime.CompareTo(b.LogTime));
|
||||
|
||||
foreach (LogNode logNode in allLogs)
|
||||
{
|
||||
// 根据过滤器判断是否显示
|
||||
if (!ShouldShowLog(logNode.LogType))
|
||||
// 根据过滤器判断是否显示,如果不显示则跳过创建LogItem
|
||||
if (!ShouldShowLog(logNode))
|
||||
continue;
|
||||
|
||||
// 从对象池获取或创建日志项
|
||||
@ -328,21 +406,41 @@ namespace MeowmentDebugTool
|
||||
}
|
||||
}
|
||||
|
||||
private bool ShouldShowLog(LogType logType)
|
||||
private bool ShouldShowLog(LogNode logNode)
|
||||
{
|
||||
switch (logType)
|
||||
// 首先检查类型筛选
|
||||
bool typeMatch = false;
|
||||
switch (logNode.LogType)
|
||||
{
|
||||
case LogType.Log:
|
||||
return infoFilter;
|
||||
typeMatch = infoFilter;
|
||||
break;
|
||||
case LogType.Warning:
|
||||
return warningFilter;
|
||||
typeMatch = warningFilter;
|
||||
break;
|
||||
case LogType.Error:
|
||||
return errorFilter;
|
||||
typeMatch = errorFilter;
|
||||
break;
|
||||
case LogType.Exception:
|
||||
return fatalFilter;
|
||||
typeMatch = fatalFilter;
|
||||
break;
|
||||
default:
|
||||
return true;
|
||||
typeMatch = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!typeMatch)
|
||||
return false;
|
||||
|
||||
// 然后检查文字筛选
|
||||
if (!string.IsNullOrEmpty(textFilter))
|
||||
{
|
||||
// 如果日志消息不包含筛选文字,则不显示
|
||||
if (!logNode.LogMessage.Contains(textFilter))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private GameObject GetLogItemFromPool()
|
||||
@ -475,29 +573,10 @@ namespace MeowmentDebugTool
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
infoCount = infoLogNodes.Count;
|
||||
warningCount = warningLogNodes.Count;
|
||||
errorCount = errorLogNodes.Count;
|
||||
fatalCount = fatalLogNodes.Count;
|
||||
}
|
||||
|
||||
private void UpdateFilterToggleText()
|
||||
@ -535,7 +614,10 @@ namespace MeowmentDebugTool
|
||||
|
||||
private void ClearAllLogs()
|
||||
{
|
||||
logNodes.Clear();
|
||||
infoLogNodes.Clear();
|
||||
warningLogNodes.Clear();
|
||||
errorLogNodes.Clear();
|
||||
fatalLogNodes.Clear();
|
||||
selectedNode = null;
|
||||
|
||||
if (detailText != null)
|
||||
@ -574,6 +656,12 @@ namespace MeowmentDebugTool
|
||||
fatalFilter = value;
|
||||
needRefresh = true;
|
||||
}
|
||||
|
||||
private void OnTextFilterChanged(string value)
|
||||
{
|
||||
textFilter = value;
|
||||
needRefresh = true;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
@ -7,7 +9,7 @@ using TMPro;
|
||||
namespace MeowmentDebugTool
|
||||
{
|
||||
/// <summary>
|
||||
/// 自定义按钮模块 - 通过反射加载标记为DebugButton的方法
|
||||
/// 自定义按钮模块 - 通过反射加载标记为DebugButton的方法,支持分组显示
|
||||
/// </summary>
|
||||
public class CustomButtonsModule : IDebugModule
|
||||
{
|
||||
@ -25,6 +27,20 @@ namespace MeowmentDebugTool
|
||||
|
||||
// 关闭窗口回调
|
||||
private Action onCloseWindowCallback;
|
||||
|
||||
// 分组相关
|
||||
private Dictionary<string, List<ButtonInfo>> buttonGroups = new Dictionary<string, List<ButtonInfo>>();
|
||||
#endregion
|
||||
|
||||
#region 内部类
|
||||
/// <summary>
|
||||
/// 按钮信息
|
||||
/// </summary>
|
||||
private class ButtonInfo
|
||||
{
|
||||
public MethodInfo Method;
|
||||
public DebugButtonAttribute Attribute;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 构造函数
|
||||
@ -43,6 +59,13 @@ namespace MeowmentDebugTool
|
||||
public void Initialize()
|
||||
{
|
||||
Debug.Log("[CustomButtonsModule] 初始化自定义按钮模块...");
|
||||
|
||||
if (buttonContainer == null || buttonPrefab == null)
|
||||
{
|
||||
Debug.LogError("[CustomButtonsModule] 必要组件缺失,初始化失败");
|
||||
return;
|
||||
}
|
||||
|
||||
LoadCustomButtons();
|
||||
}
|
||||
|
||||
@ -83,7 +106,7 @@ namespace MeowmentDebugTool
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 私有方法
|
||||
#region 私有方法 - 按钮加载
|
||||
/// <summary>
|
||||
/// 加载所有自定义按钮(使用反射)
|
||||
/// </summary>
|
||||
@ -95,11 +118,8 @@ namespace MeowmentDebugTool
|
||||
return;
|
||||
}
|
||||
|
||||
// 清空现有按钮
|
||||
foreach (Transform child in buttonContainer)
|
||||
{
|
||||
UnityEngine.Object.Destroy(child.gameObject);
|
||||
}
|
||||
// 清空现有数据
|
||||
buttonGroups.Clear();
|
||||
|
||||
// 查找所有标记为DebugButton的方法
|
||||
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
|
||||
@ -116,7 +136,20 @@ namespace MeowmentDebugTool
|
||||
var attribute = method.GetCustomAttribute<DebugButtonAttribute>();
|
||||
if (attribute != null)
|
||||
{
|
||||
CreateCustomButton(method, attribute);
|
||||
// 获取组名(如果为空则使用"默认")
|
||||
string groupName = string.IsNullOrEmpty(attribute.GroupName) ? "默认" : attribute.GroupName;
|
||||
|
||||
// 添加到对应的组
|
||||
if (!buttonGroups.ContainsKey(groupName))
|
||||
{
|
||||
buttonGroups[groupName] = new List<ButtonInfo>();
|
||||
}
|
||||
|
||||
buttonGroups[groupName].Add(new ButtonInfo
|
||||
{
|
||||
Method = method,
|
||||
Attribute = attribute
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -127,6 +160,252 @@ namespace MeowmentDebugTool
|
||||
Debug.LogWarning($"[CustomButtonsModule] 无法访问程序集 {assembly.FullName}: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Log($"[CustomButtonsModule] 共找到 {buttonGroups.Count} 个按钮组");
|
||||
foreach (var group in buttonGroups)
|
||||
{
|
||||
Debug.Log($" - {group.Key}: {group.Value.Count} 个按钮");
|
||||
}
|
||||
|
||||
// 显示所有组的按钮
|
||||
ShowAllGroupsWithTitles();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 显示所有组的按钮,每个组带标题
|
||||
/// </summary>
|
||||
private void ShowAllGroupsWithTitles()
|
||||
{
|
||||
// 清空现有内容
|
||||
foreach (Transform child in buttonContainer)
|
||||
{
|
||||
UnityEngine.Object.Destroy(child.gameObject);
|
||||
}
|
||||
|
||||
// 确保 buttonContainer 使用垂直布局而不是网格布局
|
||||
SetupButtonContainerLayout();
|
||||
|
||||
// 按组名排序("默认"组排在最前面)
|
||||
var sortedGroups = buttonGroups.OrderBy(kvp => kvp.Key == "默认" ? "" : kvp.Key);
|
||||
|
||||
foreach (var group in sortedGroups)
|
||||
{
|
||||
string groupName = group.Key;
|
||||
List<ButtonInfo> buttons = group.Value;
|
||||
|
||||
// 为每个组创建一个独立的容器
|
||||
GameObject groupContainer = CreateGroupContainer(groupName);
|
||||
|
||||
// 在组容器中创建标题
|
||||
CreateGroupTitle(groupName, groupContainer.transform);
|
||||
|
||||
// 在组容器中创建该组的所有按钮
|
||||
foreach (var buttonInfo in buttons)
|
||||
{
|
||||
CreateCustomButtonInGroup(buttonInfo.Method, buttonInfo.Attribute, groupContainer.transform);
|
||||
}
|
||||
}
|
||||
|
||||
// 重置滚动位置到顶部
|
||||
if (buttonsScrollRect != null)
|
||||
{
|
||||
Canvas.ForceUpdateCanvases();
|
||||
buttonsScrollRect.verticalNormalizedPosition = 1f;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置 buttonContainer 的布局
|
||||
/// </summary>
|
||||
private void SetupButtonContainerLayout()
|
||||
{
|
||||
if (buttonContainer == null)
|
||||
{
|
||||
Debug.LogError("[CustomButtonsModule] buttonContainer 为 null,无法设置布局");
|
||||
return;
|
||||
}
|
||||
|
||||
GameObject containerObject = buttonContainer.gameObject;
|
||||
|
||||
// 移除可能存在的 GridLayoutGroup
|
||||
GridLayoutGroup gridLayout = containerObject.GetComponent<GridLayoutGroup>();
|
||||
if (gridLayout != null)
|
||||
{
|
||||
UnityEngine.Object.DestroyImmediate(gridLayout);
|
||||
}
|
||||
|
||||
// 添加或获取 VerticalLayoutGroup
|
||||
VerticalLayoutGroup verticalLayout = containerObject.GetComponent<VerticalLayoutGroup>();
|
||||
if (verticalLayout == null)
|
||||
{
|
||||
verticalLayout = containerObject.AddComponent<VerticalLayoutGroup>();
|
||||
|
||||
if (verticalLayout == null)
|
||||
{
|
||||
Debug.LogError("[CustomButtonsModule] 错误:无法添加 VerticalLayoutGroup!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 配置垂直布局
|
||||
verticalLayout.childForceExpandWidth = true;
|
||||
verticalLayout.childForceExpandHeight = false;
|
||||
verticalLayout.childControlWidth = true;
|
||||
verticalLayout.childControlHeight = true;
|
||||
verticalLayout.spacing = 20;
|
||||
verticalLayout.padding = new RectOffset(10, 10, 10, 10);
|
||||
|
||||
// 确保有 ContentSizeFitter
|
||||
ContentSizeFitter sizeFitter = containerObject.GetComponent<ContentSizeFitter>();
|
||||
if (sizeFitter == null)
|
||||
{
|
||||
sizeFitter = containerObject.AddComponent<ContentSizeFitter>();
|
||||
}
|
||||
|
||||
sizeFitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||
sizeFitter.horizontalFit = ContentSizeFitter.FitMode.Unconstrained;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建组容器
|
||||
/// </summary>
|
||||
private GameObject CreateGroupContainer(string groupName)
|
||||
{
|
||||
GameObject groupContainer = new GameObject($"Group_{groupName}");
|
||||
groupContainer.transform.SetParent(buttonContainer, false);
|
||||
|
||||
// 添加RectTransform
|
||||
RectTransform rectTransform = groupContainer.AddComponent<RectTransform>();
|
||||
|
||||
// 添加VerticalLayoutGroup,让标题和按钮垂直排列
|
||||
VerticalLayoutGroup verticalLayout = groupContainer.AddComponent<VerticalLayoutGroup>();
|
||||
verticalLayout.childForceExpandWidth = true;
|
||||
verticalLayout.childForceExpandHeight = false;
|
||||
verticalLayout.childControlWidth = true;
|
||||
verticalLayout.childControlHeight = true;
|
||||
verticalLayout.spacing = 5;
|
||||
verticalLayout.padding = new RectOffset(0, 0, 10, 10);
|
||||
|
||||
// 添加ContentSizeFitter,让容器自动调整大小
|
||||
ContentSizeFitter sizeFitter = groupContainer.AddComponent<ContentSizeFitter>();
|
||||
sizeFitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||
sizeFitter.horizontalFit = ContentSizeFitter.FitMode.Unconstrained;
|
||||
|
||||
// 添加LayoutElement,确保占满宽度
|
||||
LayoutElement layoutElement = groupContainer.AddComponent<LayoutElement>();
|
||||
layoutElement.flexibleWidth = 1;
|
||||
|
||||
return groupContainer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建组标题
|
||||
/// </summary>
|
||||
private void CreateGroupTitle(string groupName, Transform parent)
|
||||
{
|
||||
// 创建标题GameObject
|
||||
GameObject titleObj = new GameObject($"Title_{groupName}");
|
||||
titleObj.transform.SetParent(parent, false);
|
||||
|
||||
// 添加RectTransform
|
||||
RectTransform rectTransform = titleObj.AddComponent<RectTransform>();
|
||||
|
||||
// 添加TextMeshProUGUI组件
|
||||
TMP_Text titleText = titleObj.AddComponent<TextMeshProUGUI>();
|
||||
titleText.text = groupName;
|
||||
titleText.fontSize = 32;
|
||||
titleText.fontStyle = FontStyles.Bold;
|
||||
titleText.alignment = TextAlignmentOptions.Left;
|
||||
titleText.color = new Color(1f, 1f, 1f, 0.9f);
|
||||
titleText.margin = new Vector4(10, 5, 0, 5);
|
||||
|
||||
// 应用保存的字体
|
||||
if (savedFontAsset != null)
|
||||
{
|
||||
titleText.font = savedFontAsset;
|
||||
}
|
||||
|
||||
// 添加LayoutElement
|
||||
LayoutElement layoutElement = titleObj.AddComponent<LayoutElement>();
|
||||
layoutElement.preferredHeight = 50;
|
||||
layoutElement.flexibleWidth = 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在组容器中创建按钮(需要创建一个按钮区域容器)
|
||||
/// </summary>
|
||||
private void CreateCustomButtonInGroup(MethodInfo method, DebugButtonAttribute attribute, Transform groupParent)
|
||||
{
|
||||
// 查找或创建按钮区域容器
|
||||
Transform buttonArea = groupParent.Find("ButtonArea");
|
||||
if (buttonArea == null)
|
||||
{
|
||||
GameObject buttonAreaObj = new GameObject("ButtonArea");
|
||||
buttonAreaObj.transform.SetParent(groupParent, false);
|
||||
|
||||
// 添加GridLayoutGroup,让按钮以网格形式排列
|
||||
GridLayoutGroup gridLayout = buttonAreaObj.AddComponent<GridLayoutGroup>();
|
||||
gridLayout.cellSize = new Vector2(200, 80); // 按钮大小,根据你的buttonPrefab调整
|
||||
gridLayout.spacing = new Vector2(10, 10);
|
||||
gridLayout.constraint = GridLayoutGroup.Constraint.Flexible;
|
||||
gridLayout.childAlignment = TextAnchor.UpperLeft;
|
||||
|
||||
// 添加ContentSizeFitter
|
||||
ContentSizeFitter sizeFitter = buttonAreaObj.AddComponent<ContentSizeFitter>();
|
||||
sizeFitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||
sizeFitter.horizontalFit = ContentSizeFitter.FitMode.Unconstrained;
|
||||
|
||||
// 添加LayoutElement
|
||||
LayoutElement layoutElement = buttonAreaObj.AddComponent<LayoutElement>();
|
||||
layoutElement.flexibleWidth = 1;
|
||||
|
||||
buttonArea = buttonAreaObj.transform;
|
||||
}
|
||||
|
||||
// 在按钮区域中创建按钮
|
||||
GameObject buttonObj = UnityEngine.Object.Instantiate(buttonPrefab, buttonArea);
|
||||
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($"[CustomButtonsModule] 执行调试方法: {method.Name}");
|
||||
|
||||
// 调用回调
|
||||
customButtonCallback?.Invoke(button, buttonText);
|
||||
customButtonCallback = null;
|
||||
|
||||
// 点击按钮后关闭主窗口
|
||||
onCloseWindowCallback?.Invoke();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"[CustomButtonsModule] 执行调试方法 {method.Name} 时出错: {e.Message}");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void CreateCustomButton(MethodInfo method, DebugButtonAttribute attribute)
|
||||
|
||||
@ -24,8 +24,22 @@ namespace MeowmentDebugTool
|
||||
// 保存每个数值的当前值
|
||||
private Dictionary<string, int> valueStates = new Dictionary<string, int>();
|
||||
|
||||
// 保存每个数值的滑动条和输入框引用(用于动态更新范围)
|
||||
private Dictionary<string, ValueControlData> valueControls = new Dictionary<string, ValueControlData>();
|
||||
|
||||
// 关闭窗口回调
|
||||
private Action onCloseWindowCallback;
|
||||
|
||||
/// <summary>
|
||||
/// 数值控件数据结构
|
||||
/// </summary>
|
||||
private class ValueControlData
|
||||
{
|
||||
public Slider slider;
|
||||
public TMP_InputField inputField;
|
||||
public MethodInfo method;
|
||||
public DebugValueAttribute attribute;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 构造函数
|
||||
@ -74,6 +88,138 @@ namespace MeowmentDebugTool
|
||||
{
|
||||
LoadCustomValues();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新指定方法的数值范围
|
||||
/// </summary>
|
||||
/// <param name="methodName">方法名称(类名.方法名 或 方法名)</param>
|
||||
/// <param name="minValue">新的最小值</param>
|
||||
/// <param name="maxValue">新的最大值</param>
|
||||
/// <returns>是否成功更新</returns>
|
||||
public bool UpdateValueRange(string methodName, int minValue, int maxValue)
|
||||
{
|
||||
if (minValue >= maxValue)
|
||||
{
|
||||
Debug.LogWarning($"[CustomValuesModule] 无效的范围: minValue({minValue}) >= maxValue({maxValue})");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 尝试精确匹配
|
||||
if (valueControls.ContainsKey(methodName))
|
||||
{
|
||||
return UpdateValueRangeInternal(methodName, minValue, maxValue);
|
||||
}
|
||||
|
||||
// 尝试模糊匹配(只用方法名)
|
||||
foreach (var kvp in valueControls)
|
||||
{
|
||||
string key = kvp.Key;
|
||||
string simpleMethodName = key.Substring(key.LastIndexOf('.') + 1);
|
||||
|
||||
if (simpleMethodName == methodName)
|
||||
{
|
||||
return UpdateValueRangeInternal(key, minValue, maxValue);
|
||||
}
|
||||
}
|
||||
|
||||
Debug.LogWarning($"[CustomValuesModule] 未找到方法: {methodName}");
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新指定方法的当前值
|
||||
/// </summary>
|
||||
/// <param name="methodName">方法名称(类名.方法名 或 方法名)</param>
|
||||
/// <param name="value">新的值</param>
|
||||
/// <returns>是否成功更新</returns>
|
||||
public bool UpdateValue(string methodName, int value)
|
||||
{
|
||||
// 尝试精确匹配
|
||||
if (valueControls.ContainsKey(methodName))
|
||||
{
|
||||
return UpdateValueInternal(methodName, value);
|
||||
}
|
||||
|
||||
// 尝试模糊匹配(只用方法名)
|
||||
foreach (var kvp in valueControls)
|
||||
{
|
||||
string key = kvp.Key;
|
||||
string simpleMethodName = key.Substring(key.LastIndexOf('.') + 1);
|
||||
|
||||
if (simpleMethodName == methodName)
|
||||
{
|
||||
return UpdateValueInternal(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
Debug.LogWarning($"[CustomValuesModule] 未找到方法: {methodName}");
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool UpdateValueRangeInternal(string methodKey, int minValue, int maxValue)
|
||||
{
|
||||
var data = valueControls[methodKey];
|
||||
|
||||
// 更新attribute的范围
|
||||
data.attribute.MinValue = minValue;
|
||||
data.attribute.MaxValue = maxValue;
|
||||
|
||||
// 更新slider的范围
|
||||
if (data.slider != null)
|
||||
{
|
||||
data.slider.minValue = minValue;
|
||||
data.slider.maxValue = maxValue;
|
||||
|
||||
// 确保当前值在新范围内
|
||||
int currentValue = valueStates[methodKey];
|
||||
int clampedValue = Mathf.Clamp(currentValue, minValue, maxValue);
|
||||
|
||||
if (currentValue != clampedValue)
|
||||
{
|
||||
valueStates[methodKey] = clampedValue;
|
||||
data.slider.value = clampedValue;
|
||||
|
||||
if (data.inputField != null)
|
||||
{
|
||||
data.inputField.text = clampedValue.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Log($"[CustomValuesModule] 已更新 {methodKey} 的范围: [{minValue}, {maxValue}]");
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool UpdateValueInternal(string methodKey, int value)
|
||||
{
|
||||
var data = valueControls[methodKey];
|
||||
|
||||
// 确保值在范围内
|
||||
int clampedValue = Mathf.Clamp(value, data.attribute.MinValue, data.attribute.MaxValue);
|
||||
|
||||
if (clampedValue != value)
|
||||
{
|
||||
Debug.LogWarning($"[CustomValuesModule] 值 {value} 超出范围 [{data.attribute.MinValue}, {data.attribute.MaxValue}],已限制为 {clampedValue}");
|
||||
}
|
||||
|
||||
// 更新保存的状态
|
||||
valueStates[methodKey] = clampedValue;
|
||||
|
||||
// 更新slider
|
||||
if (data.slider != null)
|
||||
{
|
||||
data.slider.value = clampedValue;
|
||||
}
|
||||
|
||||
// 更新输入框
|
||||
if (data.inputField != null)
|
||||
{
|
||||
data.inputField.text = clampedValue.ToString();
|
||||
}
|
||||
|
||||
Debug.Log($"[CustomValuesModule] 已更新 {methodKey} 的值: {clampedValue}");
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 私有方法
|
||||
@ -93,6 +239,9 @@ namespace MeowmentDebugTool
|
||||
{
|
||||
UnityEngine.Object.Destroy(child.gameObject);
|
||||
}
|
||||
|
||||
// 清空控件引用
|
||||
valueControls.Clear();
|
||||
|
||||
// 查找所有标记为DebugValue的方法
|
||||
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
|
||||
@ -167,6 +316,15 @@ namespace MeowmentDebugTool
|
||||
}
|
||||
|
||||
int currentValue = valueStates[methodKey];
|
||||
|
||||
// 保存控件引用
|
||||
valueControls[methodKey] = new ValueControlData
|
||||
{
|
||||
slider = slider,
|
||||
inputField = inputField,
|
||||
method = method,
|
||||
attribute = attribute
|
||||
};
|
||||
|
||||
// 设置滑动条
|
||||
if (slider != null)
|
||||
@ -192,7 +350,19 @@ namespace MeowmentDebugTool
|
||||
if (inputField != null)
|
||||
{
|
||||
inputField.text = currentValue.ToString();
|
||||
inputField.contentType = TMP_InputField.ContentType.IntegerNumber;
|
||||
// 根据最小值判断是否允许负数
|
||||
// 如果最小值小于0,使用Standard类型并限制只能输入数字和负号
|
||||
inputField.contentType = attribute.MinValue < 0
|
||||
? TMP_InputField.ContentType.Standard
|
||||
: TMP_InputField.ContentType.IntegerNumber;
|
||||
|
||||
// 如果允许负数,需要设置字符验证来只允许数字和负号
|
||||
if (attribute.MinValue < 0)
|
||||
{
|
||||
inputField.characterValidation = TMP_InputField.CharacterValidation.None;
|
||||
inputField.inputType = TMP_InputField.InputType.Standard;
|
||||
inputField.keyboardType = TouchScreenKeyboardType.NumbersAndPunctuation;
|
||||
}
|
||||
|
||||
// 应用保存的字体
|
||||
if (savedFontAsset != null)
|
||||
@ -203,6 +373,12 @@ namespace MeowmentDebugTool
|
||||
// 输入框值改变时更新滑动条和保存状态
|
||||
inputField.onValueChanged.AddListener((string text) =>
|
||||
{
|
||||
// 允许输入过程中暂时为空或只有负号
|
||||
if (string.IsNullOrEmpty(text) || text == "-")
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (int.TryParse(text, out int intValue))
|
||||
{
|
||||
// 限制范围
|
||||
|
||||
@ -8,9 +8,12 @@ namespace MeowmentDebugTool
|
||||
/// 可拖动的悬浮按钮
|
||||
/// 点击打开调试工具,可以在屏幕上自由拖动
|
||||
/// </summary>
|
||||
public class DraggableFloatingButton : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
|
||||
public class DraggableFloatingButton : MonoBehaviour, IPointerDownHandler, IBeginDragHandler, IDragHandler, IEndDragHandler, IPointerClickHandler
|
||||
{
|
||||
[Header("设置")]
|
||||
[Tooltip("是否启用拖动功能")]
|
||||
public bool enableDragging = false;
|
||||
|
||||
[Tooltip("是否限制在屏幕范围内")]
|
||||
public bool clampToScreen = true;
|
||||
|
||||
@ -22,7 +25,7 @@ namespace MeowmentDebugTool
|
||||
public bool snapToEdge = true;
|
||||
|
||||
[Tooltip("吸附动画时间")]
|
||||
public float snapDuration = 0.3f;
|
||||
public float snapDuration = 0.1f;
|
||||
|
||||
private RectTransform rectTransform;
|
||||
private Canvas canvas;
|
||||
@ -32,9 +35,9 @@ namespace MeowmentDebugTool
|
||||
private bool isSnapping = false;
|
||||
|
||||
// 用于区分点击和拖动
|
||||
private Vector2 dragStartPosition;
|
||||
private const float clickThreshold = 5f; // 小于这个距离算点击
|
||||
private bool wasDragging = false; // 标记刚才是否拖动过
|
||||
private bool hasDragged = false; // 标记本次操作是否进行了拖动
|
||||
private Vector2 dragStartPosition; // 拖动起始位置
|
||||
private const float dragThreshold = 5f; // 拖动阈值,移动超过这个距离才算拖动
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
@ -68,13 +71,27 @@ namespace MeowmentDebugTool
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 每次按下时重置拖动标记(在OnBeginDrag之前触发)
|
||||
/// </summary>
|
||||
public void OnPointerDown(PointerEventData eventData)
|
||||
{
|
||||
hasDragged = false; // 重置拖动标记
|
||||
Debug.Log("[DraggableFloatingButton] OnPointerDown - 重置拖动标记");
|
||||
}
|
||||
|
||||
public void OnBeginDrag(PointerEventData eventData)
|
||||
{
|
||||
if (!enableDragging)
|
||||
{
|
||||
// 如果禁用拖动,将事件传递给父级,让其他UI元素能够接收拖动事件
|
||||
PassEventToParent(eventData, ExecuteEvents.beginDragHandler);
|
||||
return;
|
||||
}
|
||||
|
||||
isDragging = true;
|
||||
isSnapping = false;
|
||||
|
||||
// 记录起始位置,用于判断是点击还是拖动
|
||||
dragStartPosition = eventData.position;
|
||||
dragStartPosition = eventData.position; // 记录起始位置
|
||||
|
||||
// 计算拖动偏移
|
||||
RectTransformUtility.ScreenPointToLocalPointInRectangle(
|
||||
@ -89,8 +106,22 @@ namespace MeowmentDebugTool
|
||||
|
||||
public void OnDrag(PointerEventData eventData)
|
||||
{
|
||||
if (!enableDragging)
|
||||
{
|
||||
// 如果禁用拖动,将事件传递给父级
|
||||
PassEventToParent(eventData, ExecuteEvents.dragHandler);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isDragging) return;
|
||||
|
||||
// 检查是否超过拖动阈值
|
||||
float dragDistance = Vector2.Distance(dragStartPosition, eventData.position);
|
||||
if (dragDistance > dragThreshold)
|
||||
{
|
||||
hasDragged = true; // 标记已经拖动
|
||||
}
|
||||
|
||||
// 转换屏幕坐标到Canvas局部坐标
|
||||
RectTransformUtility.ScreenPointToLocalPointInRectangle(
|
||||
canvas.transform as RectTransform,
|
||||
@ -113,18 +144,14 @@ namespace MeowmentDebugTool
|
||||
|
||||
public void OnEndDrag(PointerEventData eventData)
|
||||
{
|
||||
isDragging = false;
|
||||
|
||||
// 计算拖动距离
|
||||
float dragDistance = Vector2.Distance(dragStartPosition, eventData.position);
|
||||
|
||||
// 如果拖动距离超过阈值,说明是拖动而不是点击
|
||||
if (dragDistance > clickThreshold)
|
||||
if (!enableDragging)
|
||||
{
|
||||
wasDragging = true;
|
||||
// 在下一帧重置标记
|
||||
StartCoroutine(ResetDragFlag());
|
||||
// 如果禁用拖动,将事件传递给父级
|
||||
PassEventToParent(eventData, ExecuteEvents.endDragHandler);
|
||||
return;
|
||||
}
|
||||
|
||||
isDragging = false;
|
||||
|
||||
// 自动吸附到最近的边缘
|
||||
if (snapToEdge)
|
||||
@ -133,18 +160,31 @@ namespace MeowmentDebugTool
|
||||
}
|
||||
}
|
||||
|
||||
private System.Collections.IEnumerator ResetDragFlag()
|
||||
{
|
||||
yield return null; // 等待一帧
|
||||
wasDragging = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查是否刚拖动过(用于Button点击事件判断)
|
||||
/// 处理点击事件 - 只有在没有拖动时才打开窗口
|
||||
/// </summary>
|
||||
public bool GetWasDragging()
|
||||
public void OnPointerClick(PointerEventData eventData)
|
||||
{
|
||||
return wasDragging;
|
||||
Debug.Log($"[DraggableFloatingButton] OnPointerClick 被触发,hasDragged={hasDragged}");
|
||||
|
||||
// 如果进行了拖动,不执行点击操作
|
||||
if (hasDragged)
|
||||
{
|
||||
Debug.Log("[!] 检测到拖动,不打开窗口");
|
||||
return;
|
||||
}
|
||||
|
||||
// 纯点击操作,打开调试窗口
|
||||
Debug.Log("[OK] 点击浮窗,打开调试窗口");
|
||||
UniversalDebugTool debugTool = FindObjectOfType<UniversalDebugTool>();
|
||||
if (debugTool != null)
|
||||
{
|
||||
debugTool.OpenDebugWindow();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("[X] 未找到 UniversalDebugTool 实例!");
|
||||
}
|
||||
}
|
||||
|
||||
private Vector2 ClampToScreen(Vector2 position)
|
||||
@ -231,5 +271,40 @@ namespace MeowmentDebugTool
|
||||
{
|
||||
return rectTransform.anchoredPosition;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将事件传递给父级或下层的UI元素
|
||||
/// 当拖动功能被禁用时,确保其他UI元素仍能接收拖动事件
|
||||
/// </summary>
|
||||
private void PassEventToParent<T>(PointerEventData eventData, ExecuteEvents.EventFunction<T> eventFunction) where T : IEventSystemHandler
|
||||
{
|
||||
Transform parent = transform.parent;
|
||||
while (parent != null)
|
||||
{
|
||||
// 尝试执行父级对象上的事件
|
||||
if (ExecuteEvents.Execute(parent.gameObject, eventData, eventFunction))
|
||||
{
|
||||
// 如果父级处理了事件,停止传递
|
||||
return;
|
||||
}
|
||||
parent = parent.parent;
|
||||
}
|
||||
|
||||
// 如果父级都没有处理,尝试传递给射线检测到的下一个对象
|
||||
var raycastResults = new System.Collections.Generic.List<RaycastResult>();
|
||||
EventSystem.current.RaycastAll(eventData, raycastResults);
|
||||
|
||||
// 跳过当前对象,找到下一个可以处理事件的对象
|
||||
foreach (var result in raycastResults)
|
||||
{
|
||||
if (result.gameObject == gameObject)
|
||||
continue;
|
||||
|
||||
if (ExecuteEvents.Execute(result.gameObject, eventData, eventFunction))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -127,16 +127,19 @@ namespace MeowmentDebugTool
|
||||
GameObject floatBtn = new GameObject("FloatingButton");
|
||||
floatBtn.transform.SetParent(parent, false);
|
||||
RectTransform rect = floatBtn.AddComponent<RectTransform>();
|
||||
rect.anchorMin = new Vector2(0.5f, 0.5f);
|
||||
rect.anchorMax = new Vector2(0.5f, 0.5f);
|
||||
rect.pivot = new Vector2(0.5f, 0.5f);
|
||||
rect.sizeDelta = new Vector2(120, 120);
|
||||
rect.anchoredPosition = new Vector2(400, 0);
|
||||
// 使用左上角锚点,这样在任何屏幕宽高比下都能正确定位到左上角
|
||||
rect.anchorMin = new Vector2(0, 1);
|
||||
rect.anchorMax = new Vector2(0, 1);
|
||||
rect.pivot = new Vector2(0, 1);
|
||||
rect.sizeDelta = new Vector2(90, 90);
|
||||
// 距离左上角边缘10像素
|
||||
rect.anchoredPosition = new Vector2(10, -10);
|
||||
|
||||
Image img = floatBtn.AddComponent<Image>();
|
||||
img.color = new Color(0.2f, 0.6f, 1f, 0.8f);
|
||||
img.raycastTarget = true; // 确保可以接收点击事件
|
||||
|
||||
Button btn = floatBtn.AddComponent<Button>();
|
||||
// 不再添加Button组件,点击事件由DraggableFloatingButton的OnPointerClick处理
|
||||
|
||||
// 添加文本
|
||||
GameObject textObj = new GameObject("Text");
|
||||
@ -148,11 +151,11 @@ namespace MeowmentDebugTool
|
||||
|
||||
TextMeshProUGUI text = textObj.AddComponent<TextMeshProUGUI>();
|
||||
text.text = "调试";
|
||||
text.fontSize = 36;
|
||||
text.fontSize = 28;
|
||||
text.alignment = TextAlignmentOptions.Center;
|
||||
text.color = Color.white;
|
||||
|
||||
// 添加拖拽组件
|
||||
// 添加拖拽组件(包含点击处理)
|
||||
DraggableFloatingButton draggable = floatBtn.AddComponent<DraggableFloatingButton>();
|
||||
draggable.clampToScreen = true;
|
||||
draggable.snapToEdge = true;
|
||||
@ -222,6 +225,8 @@ namespace MeowmentDebugTool
|
||||
rect.sizeDelta = Vector2.zero;
|
||||
|
||||
ScrollRect scroll = page.AddComponent<ScrollRect>();
|
||||
scroll.horizontal = false; // 禁用横向滚动
|
||||
scroll.vertical = true; // 启用竖向滚动
|
||||
|
||||
GameObject viewport = new GameObject("Viewport");
|
||||
viewport.transform.SetParent(page.transform, false);
|
||||
@ -229,6 +234,7 @@ namespace MeowmentDebugTool
|
||||
vpRect.anchorMin = Vector2.zero;
|
||||
vpRect.anchorMax = Vector2.one;
|
||||
vpRect.sizeDelta = Vector2.zero;
|
||||
viewport.AddComponent<Image>().color = new Color(0, 0, 0, 0.1f);
|
||||
viewport.AddComponent<Mask>().showMaskGraphic = false;
|
||||
|
||||
GameObject buttonContainer = new GameObject("ButtonContainer");
|
||||
@ -237,6 +243,8 @@ namespace MeowmentDebugTool
|
||||
containerRect.anchorMin = new Vector2(0, 1);
|
||||
containerRect.anchorMax = new Vector2(1, 1);
|
||||
containerRect.pivot = new Vector2(0.5f, 1);
|
||||
containerRect.offsetMin = new Vector2(0, containerRect.offsetMin.y);
|
||||
containerRect.offsetMax = new Vector2(0, containerRect.offsetMax.y);
|
||||
|
||||
GridLayoutGroup grid = buttonContainer.AddComponent<GridLayoutGroup>();
|
||||
grid.cellSize = new Vector2(300, 100);
|
||||
@ -264,6 +272,8 @@ namespace MeowmentDebugTool
|
||||
rect.sizeDelta = Vector2.zero;
|
||||
|
||||
ScrollRect scroll = page.AddComponent<ScrollRect>();
|
||||
scroll.horizontal = false; // 禁用横向滚动
|
||||
scroll.vertical = true; // 启用竖向滚动
|
||||
|
||||
GameObject viewport = new GameObject("Viewport");
|
||||
viewport.transform.SetParent(page.transform, false);
|
||||
@ -271,6 +281,7 @@ namespace MeowmentDebugTool
|
||||
vpRect.anchorMin = Vector2.zero;
|
||||
vpRect.anchorMax = Vector2.one;
|
||||
vpRect.sizeDelta = Vector2.zero;
|
||||
viewport.AddComponent<Image>().color = new Color(0, 0, 0, 0.1f);
|
||||
viewport.AddComponent<Mask>().showMaskGraphic = false;
|
||||
|
||||
GameObject checkBoxContainer = new GameObject("CheckBoxContainer");
|
||||
@ -279,6 +290,8 @@ namespace MeowmentDebugTool
|
||||
containerRect.anchorMin = new Vector2(0, 1);
|
||||
containerRect.anchorMax = new Vector2(1, 1);
|
||||
containerRect.pivot = new Vector2(0.5f, 1);
|
||||
containerRect.offsetMin = new Vector2(0, containerRect.offsetMin.y); // left = 0
|
||||
containerRect.offsetMax = new Vector2(0, containerRect.offsetMax.y); // right = 0
|
||||
|
||||
// 使用垂直布局,一行一个复选框
|
||||
VerticalLayoutGroup vertLayout = checkBoxContainer.AddComponent<VerticalLayoutGroup>();
|
||||
@ -307,6 +320,8 @@ namespace MeowmentDebugTool
|
||||
rect.sizeDelta = Vector2.zero;
|
||||
|
||||
ScrollRect scroll = page.AddComponent<ScrollRect>();
|
||||
scroll.horizontal = false; // 禁用横向滚动
|
||||
scroll.vertical = true; // 启用竖向滚动
|
||||
|
||||
GameObject viewport = new GameObject("Viewport");
|
||||
viewport.transform.SetParent(page.transform, false);
|
||||
@ -314,6 +329,7 @@ namespace MeowmentDebugTool
|
||||
vpRect.anchorMin = Vector2.zero;
|
||||
vpRect.anchorMax = Vector2.one;
|
||||
vpRect.sizeDelta = Vector2.zero;
|
||||
viewport.AddComponent<Image>().color = new Color(0, 0, 0, 0.1f);
|
||||
viewport.AddComponent<Mask>().showMaskGraphic = false;
|
||||
|
||||
GameObject valueContainer = new GameObject("ValueContainer");
|
||||
@ -322,6 +338,8 @@ namespace MeowmentDebugTool
|
||||
containerRect.anchorMin = new Vector2(0, 1);
|
||||
containerRect.anchorMax = new Vector2(1, 1);
|
||||
containerRect.pivot = new Vector2(0.5f, 1);
|
||||
containerRect.offsetMin = new Vector2(0, containerRect.offsetMin.y); // left = 0
|
||||
containerRect.offsetMax = new Vector2(0, containerRect.offsetMax.y); // right = 0
|
||||
|
||||
// 使用垂直布局,一行一个数值项
|
||||
VerticalLayoutGroup vertLayout = valueContainer.AddComponent<VerticalLayoutGroup>();
|
||||
@ -488,6 +506,124 @@ namespace MeowmentDebugTool
|
||||
currentText.GetComponent<TextMeshProUGUI>().alignment = TextAlignmentOptions.Center;
|
||||
currentText.GetComponent<RectTransform>().sizeDelta = new Vector2(0, 60);
|
||||
|
||||
// Console Log缓存池设置面板
|
||||
GameObject bufferPanel = new GameObject("BufferPanel");
|
||||
bufferPanel.transform.SetParent(page.transform, false);
|
||||
RectTransform bufferPanelRect = bufferPanel.AddComponent<RectTransform>();
|
||||
bufferPanelRect.anchorMin = new Vector2(0, 0);
|
||||
bufferPanelRect.anchorMax = new Vector2(1, 0.5f);
|
||||
bufferPanelRect.offsetMin = new Vector2(20, 20);
|
||||
bufferPanelRect.offsetMax = new Vector2(-20, 0);
|
||||
|
||||
VerticalLayoutGroup bufferVLayout = bufferPanel.AddComponent<VerticalLayoutGroup>();
|
||||
bufferVLayout.spacing = 30;
|
||||
bufferVLayout.padding = new RectOffset(40, 40, 40, 40);
|
||||
bufferVLayout.childControlWidth = true;
|
||||
bufferVLayout.childControlHeight = false;
|
||||
bufferVLayout.childForceExpandWidth = true;
|
||||
|
||||
// 标题
|
||||
GameObject bufferTitle = CreateTextObject("BufferTitle", bufferPanel.transform, "Console Log缓存池设置");
|
||||
bufferTitle.GetComponent<TextMeshProUGUI>().fontSize = 36;
|
||||
bufferTitle.GetComponent<TextMeshProUGUI>().alignment = TextAlignmentOptions.Center;
|
||||
bufferTitle.GetComponent<RectTransform>().sizeDelta = new Vector2(0, 60);
|
||||
|
||||
// Info缓存池输入
|
||||
GameObject infoBufferContainer = new GameObject("InfoBufferContainer");
|
||||
infoBufferContainer.transform.SetParent(bufferPanel.transform, false);
|
||||
RectTransform infoBufferRect = infoBufferContainer.AddComponent<RectTransform>();
|
||||
infoBufferRect.sizeDelta = new Vector2(0, 100);
|
||||
|
||||
HorizontalLayoutGroup infoBufferHLayout = infoBufferContainer.AddComponent<HorizontalLayoutGroup>();
|
||||
infoBufferHLayout.spacing = 20;
|
||||
infoBufferHLayout.childControlWidth = true;
|
||||
infoBufferHLayout.childControlHeight = true;
|
||||
infoBufferHLayout.childForceExpandWidth = false;
|
||||
infoBufferHLayout.childForceExpandHeight = true;
|
||||
|
||||
GameObject infoLabel = CreateTextObject("InfoLabel", infoBufferContainer.transform, "Info:");
|
||||
infoLabel.GetComponent<TextMeshProUGUI>().fontSize = 28;
|
||||
infoLabel.GetComponent<RectTransform>().sizeDelta = new Vector2(200, 100);
|
||||
|
||||
GameObject infoBufferInput = CreateInputField("InfoBufferInputField", infoBufferContainer.transform, "50");
|
||||
infoBufferInput.GetComponent<RectTransform>().sizeDelta = new Vector2(300, 100);
|
||||
|
||||
// Warning缓存池输入
|
||||
GameObject warningBufferContainer = new GameObject("WarningBufferContainer");
|
||||
warningBufferContainer.transform.SetParent(bufferPanel.transform, false);
|
||||
RectTransform warningBufferRect = warningBufferContainer.AddComponent<RectTransform>();
|
||||
warningBufferRect.sizeDelta = new Vector2(0, 100);
|
||||
|
||||
HorizontalLayoutGroup warningBufferHLayout = warningBufferContainer.AddComponent<HorizontalLayoutGroup>();
|
||||
warningBufferHLayout.spacing = 20;
|
||||
warningBufferHLayout.childControlWidth = true;
|
||||
warningBufferHLayout.childControlHeight = true;
|
||||
warningBufferHLayout.childForceExpandWidth = false;
|
||||
warningBufferHLayout.childForceExpandHeight = true;
|
||||
|
||||
GameObject warningLabel = CreateTextObject("WarningLabel", warningBufferContainer.transform, "Warning:");
|
||||
warningLabel.GetComponent<TextMeshProUGUI>().fontSize = 28;
|
||||
warningLabel.GetComponent<RectTransform>().sizeDelta = new Vector2(200, 100);
|
||||
|
||||
GameObject warningBufferInput = CreateInputField("WarningBufferInputField", warningBufferContainer.transform, "50");
|
||||
warningBufferInput.GetComponent<RectTransform>().sizeDelta = new Vector2(300, 100);
|
||||
|
||||
// Error缓存池输入
|
||||
GameObject errorBufferContainer = new GameObject("ErrorBufferContainer");
|
||||
errorBufferContainer.transform.SetParent(bufferPanel.transform, false);
|
||||
RectTransform errorBufferRect = errorBufferContainer.AddComponent<RectTransform>();
|
||||
errorBufferRect.sizeDelta = new Vector2(0, 100);
|
||||
|
||||
HorizontalLayoutGroup errorBufferHLayout = errorBufferContainer.AddComponent<HorizontalLayoutGroup>();
|
||||
errorBufferHLayout.spacing = 20;
|
||||
errorBufferHLayout.childControlWidth = true;
|
||||
errorBufferHLayout.childControlHeight = true;
|
||||
errorBufferHLayout.childForceExpandWidth = false;
|
||||
errorBufferHLayout.childForceExpandHeight = true;
|
||||
|
||||
GameObject errorLabel = CreateTextObject("ErrorLabel", errorBufferContainer.transform, "Error:");
|
||||
errorLabel.GetComponent<TextMeshProUGUI>().fontSize = 28;
|
||||
errorLabel.GetComponent<RectTransform>().sizeDelta = new Vector2(200, 100);
|
||||
|
||||
GameObject errorBufferInput = CreateInputField("ErrorBufferInputField", errorBufferContainer.transform, "50");
|
||||
errorBufferInput.GetComponent<RectTransform>().sizeDelta = new Vector2(300, 100);
|
||||
|
||||
// Fatal缓存池输入
|
||||
GameObject fatalBufferContainer = new GameObject("FatalBufferContainer");
|
||||
fatalBufferContainer.transform.SetParent(bufferPanel.transform, false);
|
||||
RectTransform fatalBufferRect = fatalBufferContainer.AddComponent<RectTransform>();
|
||||
fatalBufferRect.sizeDelta = new Vector2(0, 100);
|
||||
|
||||
HorizontalLayoutGroup fatalBufferHLayout = fatalBufferContainer.AddComponent<HorizontalLayoutGroup>();
|
||||
fatalBufferHLayout.spacing = 20;
|
||||
fatalBufferHLayout.childControlWidth = true;
|
||||
fatalBufferHLayout.childControlHeight = true;
|
||||
fatalBufferHLayout.childForceExpandWidth = false;
|
||||
fatalBufferHLayout.childForceExpandHeight = true;
|
||||
|
||||
GameObject fatalLabel = CreateTextObject("FatalLabel", fatalBufferContainer.transform, "Fatal:");
|
||||
fatalLabel.GetComponent<TextMeshProUGUI>().fontSize = 28;
|
||||
fatalLabel.GetComponent<RectTransform>().sizeDelta = new Vector2(200, 100);
|
||||
|
||||
GameObject fatalBufferInput = CreateInputField("FatalBufferInputField", fatalBufferContainer.transform, "50");
|
||||
fatalBufferInput.GetComponent<RectTransform>().sizeDelta = new Vector2(300, 100);
|
||||
|
||||
// 缓存池按钮容器
|
||||
GameObject bufferBtnContainer = new GameObject("BufferButtonContainer");
|
||||
bufferBtnContainer.transform.SetParent(bufferPanel.transform, false);
|
||||
RectTransform bufferBtnRect = bufferBtnContainer.AddComponent<RectTransform>();
|
||||
bufferBtnRect.sizeDelta = new Vector2(0, 100);
|
||||
|
||||
HorizontalLayoutGroup bufferBtnLayout = bufferBtnContainer.AddComponent<HorizontalLayoutGroup>();
|
||||
bufferBtnLayout.spacing = 20;
|
||||
bufferBtnLayout.childControlWidth = true;
|
||||
bufferBtnLayout.childControlHeight = true;
|
||||
bufferBtnLayout.childForceExpandWidth = true;
|
||||
bufferBtnLayout.childForceExpandHeight = true;
|
||||
|
||||
GameObject applyBufferBtn = CreateButton("ApplyBufferButton", bufferBtnContainer.transform, "应用");
|
||||
GameObject resetBufferBtn = CreateButton("ResetBufferButton", bufferBtnContainer.transform, "重置");
|
||||
|
||||
return page;
|
||||
}
|
||||
|
||||
@ -563,13 +699,48 @@ namespace MeowmentDebugTool
|
||||
LayoutElement fatalLayout = fatalToggle.AddComponent<LayoutElement>();
|
||||
fatalLayout.preferredHeight = 35;
|
||||
|
||||
// 文字筛选面板
|
||||
GameObject textFilterPanel = new GameObject("TextFilterPanel");
|
||||
textFilterPanel.transform.SetParent(page.transform, false);
|
||||
RectTransform textFilterRect = textFilterPanel.AddComponent<RectTransform>();
|
||||
textFilterRect.sizeDelta = new Vector2(0, 120);
|
||||
LayoutElement textFilterLayout = textFilterPanel.AddComponent<LayoutElement>();
|
||||
textFilterLayout.preferredHeight = 120;
|
||||
textFilterLayout.flexibleHeight = 0;
|
||||
|
||||
HorizontalLayoutGroup textFilterHLayout = textFilterPanel.AddComponent<HorizontalLayoutGroup>();
|
||||
textFilterHLayout.spacing = 20;
|
||||
textFilterHLayout.padding = new RectOffset(10, 10, 10, 10);
|
||||
textFilterHLayout.childControlWidth = true;
|
||||
textFilterHLayout.childControlHeight = true;
|
||||
textFilterHLayout.childForceExpandWidth = true;
|
||||
textFilterHLayout.childForceExpandHeight = true;
|
||||
|
||||
// 文字筛选标签
|
||||
GameObject textFilterLabel = CreateTextObject("TextFilterLabel", textFilterPanel.transform, "文字筛选:");
|
||||
textFilterLabel.GetComponent<TextMeshProUGUI>().fontSize = 28;
|
||||
textFilterLabel.GetComponent<TextMeshProUGUI>().alignment = TextAlignmentOptions.Left;
|
||||
textFilterLabel.GetComponent<RectTransform>().sizeDelta = new Vector2(200, 0);
|
||||
LayoutElement labelLayout = textFilterLabel.AddComponent<LayoutElement>();
|
||||
labelLayout.preferredWidth = 200;
|
||||
labelLayout.flexibleWidth = 0;
|
||||
|
||||
// 文字筛选输入框
|
||||
GameObject textFilterInput = CreateInputField("ConsoleTextFilterInputField", textFilterPanel.transform, "");
|
||||
textFilterInput.GetComponent<RectTransform>().sizeDelta = new Vector2(0, 100);
|
||||
TMP_InputField inputField = textFilterInput.GetComponent<TMP_InputField>();
|
||||
if (inputField != null && inputField.placeholder is TextMeshProUGUI placeholder)
|
||||
{
|
||||
placeholder.text = "输入筛选文字...";
|
||||
}
|
||||
|
||||
// 日志区域 (60%)
|
||||
GameObject logArea = new GameObject("LogArea");
|
||||
logArea.transform.SetParent(page.transform, false);
|
||||
RectTransform logAreaRect = logArea.AddComponent<RectTransform>();
|
||||
logAreaRect.sizeDelta = new Vector2(0, 1300);
|
||||
logAreaRect.sizeDelta = new Vector2(0, 1180);
|
||||
LayoutElement logAreaLayout = logArea.AddComponent<LayoutElement>();
|
||||
logAreaLayout.preferredHeight = 1300;
|
||||
logAreaLayout.preferredHeight = 1180;
|
||||
logAreaLayout.flexibleHeight = 0;
|
||||
|
||||
// 日志ScrollView
|
||||
@ -658,9 +829,21 @@ namespace MeowmentDebugTool
|
||||
detailContentRect.anchorMin = new Vector2(0, 1);
|
||||
detailContentRect.anchorMax = new Vector2(1, 1);
|
||||
detailContentRect.pivot = new Vector2(0.5f, 1);
|
||||
detailContentRect.sizeDelta = new Vector2(-20, 0);
|
||||
detailContentRect.sizeDelta = new Vector2(0, 100);
|
||||
detailContentRect.anchoredPosition = Vector2.zero;
|
||||
|
||||
// 为 Content 添加 ContentSizeFitter 以自动调整高度
|
||||
ContentSizeFitter contentFitter = detailContent.AddComponent<ContentSizeFitter>();
|
||||
contentFitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||
|
||||
// 添加 VerticalLayoutGroup 用于布局
|
||||
VerticalLayoutGroup contentLayout = detailContent.AddComponent<VerticalLayoutGroup>();
|
||||
contentLayout.childControlHeight = false;
|
||||
contentLayout.childControlWidth = true;
|
||||
contentLayout.childForceExpandHeight = false;
|
||||
contentLayout.childForceExpandWidth = true;
|
||||
contentLayout.padding = new RectOffset(10, 10, 10, 10);
|
||||
|
||||
// 详情文本
|
||||
GameObject detailText = CreateTextObject("ConsoleDetailText", detailContent.transform, "点击日志查看详细信息...");
|
||||
TextMeshProUGUI detailTMP = detailText.GetComponent<TextMeshProUGUI>();
|
||||
@ -670,14 +853,21 @@ namespace MeowmentDebugTool
|
||||
detailTMP.overflowMode = TextOverflowModes.Overflow;
|
||||
detailTMP.richText = true;
|
||||
detailTMP.color = new Color(0.8f, 0.8f, 0.8f, 1f);
|
||||
|
||||
RectTransform detailTextRect = detailText.GetComponent<RectTransform>();
|
||||
detailTextRect.anchorMin = new Vector2(0, 1);
|
||||
detailTextRect.anchorMax = new Vector2(1, 1);
|
||||
detailTextRect.pivot = new Vector2(0.5f, 1);
|
||||
detailTextRect.sizeDelta = new Vector2(0, 0);
|
||||
|
||||
ContentSizeFitter detailFitter = detailText.AddComponent<ContentSizeFitter>();
|
||||
detailFitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||
// 为文本添加 ContentSizeFitter 使其根据内容自动调整大小
|
||||
ContentSizeFitter detailTextFitter = detailText.AddComponent<ContentSizeFitter>();
|
||||
detailTextFitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||
|
||||
// 添加 LayoutElement 使文本在 VerticalLayoutGroup 中正确工作
|
||||
LayoutElement detailTextLayout = detailText.AddComponent<LayoutElement>();
|
||||
detailTextLayout.flexibleHeight = 0;
|
||||
detailTextLayout.preferredHeight = -1;
|
||||
|
||||
detailScroll.content = detailContentRect;
|
||||
|
||||
@ -1189,6 +1379,13 @@ namespace MeowmentDebugTool
|
||||
labelText.alignment = TextAlignmentOptions.Left;
|
||||
labelText.color = Color.white;
|
||||
|
||||
// 设置默认字体
|
||||
TMP_FontAsset defaultFont = GetDefaultFont();
|
||||
if (defaultFont != null)
|
||||
{
|
||||
labelText.font = defaultFont;
|
||||
}
|
||||
|
||||
// 添加Toggle组件
|
||||
Toggle toggle = toggleObj.AddComponent<Toggle>();
|
||||
toggle.targetGraphic = checkboxBgImage;
|
||||
@ -1234,6 +1431,13 @@ namespace MeowmentDebugTool
|
||||
labelText.alignment = TextAlignmentOptions.Left;
|
||||
labelText.color = Color.white;
|
||||
|
||||
// 设置默认字体
|
||||
TMP_FontAsset defaultFont = GetDefaultFont();
|
||||
if (defaultFont != null)
|
||||
{
|
||||
labelText.font = defaultFont;
|
||||
}
|
||||
|
||||
// 创建滑动条(标题下方,占据左侧60%)
|
||||
GameObject sliderObj = new GameObject("Slider");
|
||||
sliderObj.transform.SetParent(valueObj.transform, false);
|
||||
@ -1445,6 +1649,27 @@ namespace MeowmentDebugTool
|
||||
type.GetField("currentResolutionText", flags)?.SetValue(tool, resPanel.Find("CurrentResolutionText")?.GetComponent<TextMeshProUGUI>());
|
||||
}
|
||||
|
||||
// 设置缓存池大小输入框
|
||||
Transform bufferPanel = settingsPage.transform.Find("BufferPanel");
|
||||
if (bufferPanel != null)
|
||||
{
|
||||
type.GetField("infoBufferInputField", flags)?.SetValue(tool,
|
||||
bufferPanel.Find("InfoBufferContainer/InfoBufferInputField")?.GetComponent<TMP_InputField>());
|
||||
type.GetField("warningBufferInputField", flags)?.SetValue(tool,
|
||||
bufferPanel.Find("WarningBufferContainer/WarningBufferInputField")?.GetComponent<TMP_InputField>());
|
||||
type.GetField("errorBufferInputField", flags)?.SetValue(tool,
|
||||
bufferPanel.Find("ErrorBufferContainer/ErrorBufferInputField")?.GetComponent<TMP_InputField>());
|
||||
type.GetField("fatalBufferInputField", flags)?.SetValue(tool,
|
||||
bufferPanel.Find("FatalBufferContainer/FatalBufferInputField")?.GetComponent<TMP_InputField>());
|
||||
|
||||
Transform bufferBtnContainer = bufferPanel.Find("BufferButtonContainer");
|
||||
if (bufferBtnContainer != null)
|
||||
{
|
||||
type.GetField("applyBufferButton", flags)?.SetValue(tool, bufferBtnContainer.Find("ApplyBufferButton")?.GetComponent<Button>());
|
||||
type.GetField("resetBufferButton", flags)?.SetValue(tool, bufferBtnContainer.Find("ResetBufferButton")?.GetComponent<Button>());
|
||||
}
|
||||
}
|
||||
|
||||
// 控制台页面
|
||||
type.GetField("consolePage", flags)?.SetValue(tool, consolePage);
|
||||
type.GetField("consoleLogScrollRect", flags)?.SetValue(tool, consolePage.transform.Find("LogArea/ConsoleLogScrollView")?.GetComponent<ScrollRect>());
|
||||
@ -1462,6 +1687,14 @@ namespace MeowmentDebugTool
|
||||
type.GetField("consoleFatalFilterToggle", flags)?.SetValue(tool, controlPanel.Find("ConsoleFatalFilterToggle")?.GetComponent<Toggle>());
|
||||
}
|
||||
|
||||
// 设置控制台文字筛选输入框
|
||||
Transform textFilterPanel = consolePage.transform.Find("TextFilterPanel");
|
||||
if (textFilterPanel != null)
|
||||
{
|
||||
type.GetField("consoleTextFilterInputField", flags)?.SetValue(tool,
|
||||
textFilterPanel.Find("ConsoleTextFilterInputField")?.GetComponent<TMP_InputField>());
|
||||
}
|
||||
|
||||
// 创建日志项预制件
|
||||
GameObject consoleLogItemPrefab = CreateConsoleLogItemPrefab();
|
||||
type.GetField("consoleLogItemPrefab", flags)?.SetValue(tool, consoleLogItemPrefab);
|
||||
|
||||
@ -17,6 +17,15 @@ namespace MeowmentDebugTool
|
||||
private Button resetResolutionButton;
|
||||
private TMP_Text currentResolutionText;
|
||||
|
||||
// Console Log缓存池设置
|
||||
private TMP_InputField infoBufferInputField;
|
||||
private TMP_InputField warningBufferInputField;
|
||||
private TMP_InputField errorBufferInputField;
|
||||
private TMP_InputField fatalBufferInputField;
|
||||
private Button applyBufferButton;
|
||||
private Button resetBufferButton;
|
||||
private ConsoleModule consoleModule;
|
||||
|
||||
// 主窗口引用
|
||||
private RectTransform mainWindow;
|
||||
private Canvas canvas;
|
||||
@ -24,11 +33,17 @@ namespace MeowmentDebugTool
|
||||
// 默认分辨率
|
||||
private Vector2 defaultResolution = new Vector2(1080, 2340);
|
||||
private Vector2 currentCustomResolution;
|
||||
|
||||
// 默认缓存池大小
|
||||
private const int DEFAULT_BUFFER_SIZE = 50;
|
||||
#endregion
|
||||
|
||||
#region 构造函数
|
||||
public SettingsModule(GameObject page, TMP_InputField widthInput, TMP_InputField heightInput,
|
||||
Button applyButton, Button resetButton, TMP_Text resolutionText,
|
||||
TMP_InputField infoBufferInput, TMP_InputField warningBufferInput,
|
||||
TMP_InputField errorBufferInput, TMP_InputField fatalBufferInput,
|
||||
Button applyBufferBtn, Button resetBufferBtn,
|
||||
RectTransform mainWin, Canvas canvasRef)
|
||||
{
|
||||
settingsPage = page;
|
||||
@ -37,6 +52,14 @@ namespace MeowmentDebugTool
|
||||
applyResolutionButton = applyButton;
|
||||
resetResolutionButton = resetButton;
|
||||
currentResolutionText = resolutionText;
|
||||
|
||||
infoBufferInputField = infoBufferInput;
|
||||
warningBufferInputField = warningBufferInput;
|
||||
errorBufferInputField = errorBufferInput;
|
||||
fatalBufferInputField = fatalBufferInput;
|
||||
applyBufferButton = applyBufferBtn;
|
||||
resetBufferButton = resetBufferBtn;
|
||||
|
||||
mainWindow = mainWin;
|
||||
canvas = canvasRef;
|
||||
|
||||
@ -55,8 +78,17 @@ namespace MeowmentDebugTool
|
||||
if (resetResolutionButton != null)
|
||||
resetResolutionButton.onClick.AddListener(ResetToDefaultResolution);
|
||||
|
||||
if (applyBufferButton != null)
|
||||
applyBufferButton.onClick.AddListener(ApplyBufferSize);
|
||||
|
||||
if (resetBufferButton != null)
|
||||
resetBufferButton.onClick.AddListener(ResetBufferSize);
|
||||
|
||||
// 应用默认分辨率
|
||||
ApplyResolution(defaultResolution);
|
||||
|
||||
// 设置默认缓存池大小
|
||||
ResetBufferSize();
|
||||
}
|
||||
|
||||
public GameObject GetPage()
|
||||
@ -68,6 +100,28 @@ namespace MeowmentDebugTool
|
||||
{
|
||||
return "设置";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置 Console Module 引用
|
||||
/// </summary>
|
||||
public void SetConsoleModule(ConsoleModule module)
|
||||
{
|
||||
consoleModule = module;
|
||||
|
||||
// 更新输入框显示当前值
|
||||
if (consoleModule != null)
|
||||
{
|
||||
consoleModule.GetMaxLogLines(out int maxInfo, out int maxWarning, out int maxError, out int maxFatal);
|
||||
if (infoBufferInputField != null)
|
||||
infoBufferInputField.text = maxInfo.ToString();
|
||||
if (warningBufferInputField != null)
|
||||
warningBufferInputField.text = maxWarning.ToString();
|
||||
if (errorBufferInputField != null)
|
||||
errorBufferInputField.text = maxError.ToString();
|
||||
if (fatalBufferInputField != null)
|
||||
fatalBufferInputField.text = maxFatal.ToString();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 私有方法
|
||||
@ -123,6 +177,50 @@ namespace MeowmentDebugTool
|
||||
// 不需要强制刷新Canvas,会导致rebuild loop
|
||||
// Canvas会自动在下一帧更新
|
||||
}
|
||||
|
||||
private void ApplyBufferSize()
|
||||
{
|
||||
if (consoleModule == null)
|
||||
{
|
||||
Debug.LogWarning("[SettingsModule] ConsoleModule未设置,无法应用缓存池大小");
|
||||
return;
|
||||
}
|
||||
|
||||
int maxInfo = DEFAULT_BUFFER_SIZE;
|
||||
int maxWarning = DEFAULT_BUFFER_SIZE;
|
||||
int maxError = DEFAULT_BUFFER_SIZE;
|
||||
int maxFatal = DEFAULT_BUFFER_SIZE;
|
||||
|
||||
if (infoBufferInputField != null && int.TryParse(infoBufferInputField.text, out int info) && info > 0)
|
||||
maxInfo = info;
|
||||
if (warningBufferInputField != null && int.TryParse(warningBufferInputField.text, out int warning) && warning > 0)
|
||||
maxWarning = warning;
|
||||
if (errorBufferInputField != null && int.TryParse(errorBufferInputField.text, out int error) && error > 0)
|
||||
maxError = error;
|
||||
if (fatalBufferInputField != null && int.TryParse(fatalBufferInputField.text, out int fatal) && fatal > 0)
|
||||
maxFatal = fatal;
|
||||
|
||||
consoleModule.SetMaxLogLines(maxInfo, maxWarning, maxError, maxFatal);
|
||||
Debug.Log($"[SettingsModule] 已应用缓存池大小 - Info:{maxInfo}, Warning:{maxWarning}, Error:{maxError}, Fatal:{maxFatal}");
|
||||
}
|
||||
|
||||
private void ResetBufferSize()
|
||||
{
|
||||
if (infoBufferInputField != null)
|
||||
infoBufferInputField.text = DEFAULT_BUFFER_SIZE.ToString();
|
||||
if (warningBufferInputField != null)
|
||||
warningBufferInputField.text = DEFAULT_BUFFER_SIZE.ToString();
|
||||
if (errorBufferInputField != null)
|
||||
errorBufferInputField.text = DEFAULT_BUFFER_SIZE.ToString();
|
||||
if (fatalBufferInputField != null)
|
||||
fatalBufferInputField.text = DEFAULT_BUFFER_SIZE.ToString();
|
||||
|
||||
if (consoleModule != null)
|
||||
{
|
||||
consoleModule.SetMaxLogLines(DEFAULT_BUFFER_SIZE, DEFAULT_BUFFER_SIZE, DEFAULT_BUFFER_SIZE, DEFAULT_BUFFER_SIZE);
|
||||
Debug.Log($"[SettingsModule] 已重置缓存池大小为默认值: {DEFAULT_BUFFER_SIZE}");
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@ -131,8 +131,8 @@ namespace MeowmentDebugTool
|
||||
|
||||
// 方案3: 临时演示 - 修改Time.timeScale来模拟时间变化
|
||||
// 实际项目中应该修改你自己的时间变量
|
||||
Debug.Log($"💡 提示: 请在AdjustGameTime方法中实现你的时间调整逻辑");
|
||||
Debug.Log($"💡 建议: 修改游戏内的时间变量,例如 GameTime.Current.AddSeconds({seconds})");
|
||||
Debug.Log($"[!] 提示: 请在AdjustGameTime方法中实现你的时间调整逻辑");
|
||||
Debug.Log($"[!] 建议: 修改游戏内的时间变量,例如 GameTime.Current.AddSeconds({seconds})");
|
||||
|
||||
// 示例:如果你有一个静态的游戏时间偏移量
|
||||
// GameTimeOffset += seconds;
|
||||
|
||||
@ -66,6 +66,12 @@ namespace MeowmentDebugTool
|
||||
[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;
|
||||
@ -79,6 +85,7 @@ namespace MeowmentDebugTool
|
||||
[SerializeField] private Toggle consoleWarningFilterToggle;
|
||||
[SerializeField] private Toggle consoleErrorFilterToggle;
|
||||
[SerializeField] private Toggle consoleFatalFilterToggle;
|
||||
[SerializeField] private TMP_InputField consoleTextFilterInputField;
|
||||
[SerializeField] private GameObject consoleLogItemPrefab;
|
||||
|
||||
// [Header("输入对话框")]
|
||||
@ -142,6 +149,13 @@ namespace MeowmentDebugTool
|
||||
#region Unity生命周期
|
||||
private void Awake()
|
||||
{
|
||||
// 如果未初始化,直接销毁GameObject,不执行任何操作
|
||||
if (!isInitialized)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
|
||||
if (instance == null)
|
||||
{
|
||||
instance = this;
|
||||
@ -156,23 +170,24 @@ namespace MeowmentDebugTool
|
||||
// 设置Canvas为最上层
|
||||
SetCanvasToTop();
|
||||
|
||||
// 未初始化前隐藏所有UI
|
||||
if (!isInitialized)
|
||||
{
|
||||
HideAllUI();
|
||||
}
|
||||
// 隐藏所有UI
|
||||
HideAllUI();
|
||||
|
||||
// 注意:如果isInitialized为true,不在Awake中调用InitializeDebugTool
|
||||
// 因为此时反射设置的字段可能还没生效,延迟到Start中调用
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
// 如果未初始化,不执行任何操作
|
||||
if (!isInitialized)
|
||||
return;
|
||||
|
||||
// 只有在已初始化但还没调用过InitializeDebugTool时才执行
|
||||
// 这避免了Awake和Init()的重复调用问题
|
||||
if (isInitialized && allModules.Count == 0)
|
||||
// 这是通过Init()创建的实例,需要在Start中完成初始化
|
||||
// 此时反射设置的字段已经生效
|
||||
if (allModules.Count == 0)
|
||||
{
|
||||
// 这是通过Init()创建的实例,需要在Start中完成初始化
|
||||
// 此时反射设置的字段已经生效
|
||||
InitializeDebugTool();
|
||||
InitializeAllModules();
|
||||
ShowMainWindow();
|
||||
@ -191,11 +206,11 @@ namespace MeowmentDebugTool
|
||||
OnFourFingerTap();
|
||||
}
|
||||
|
||||
// 检测四指点击(仅在非键盘测试模式下)
|
||||
if (!useKeyboardTestMode)
|
||||
{
|
||||
DetectFourFingerTap();
|
||||
}
|
||||
// 四指点击检测已禁用,如需使用请在主程序中通过公共API控制显示/隐藏
|
||||
// if (!useKeyboardTestMode)
|
||||
// {
|
||||
// DetectFourFingerTap();
|
||||
// }
|
||||
|
||||
// 更新控制台模块
|
||||
if (consoleModule != null)
|
||||
@ -206,6 +221,10 @@ namespace MeowmentDebugTool
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
// 如果未初始化,不执行任何操作
|
||||
if (!isInitialized)
|
||||
return;
|
||||
|
||||
if (instance == this)
|
||||
{
|
||||
instance = null;
|
||||
@ -257,7 +276,7 @@ namespace MeowmentDebugTool
|
||||
/// <param name="fontAsset">TMP字体资源</param>
|
||||
public static void SetSDFFont(TMP_FontAsset fontAsset)
|
||||
{
|
||||
if (!InstanceExists)
|
||||
if (!isInitialized || !InstanceExists)
|
||||
{
|
||||
Debug.LogWarning("[MeowmentDebugTool] 调试工具未初始化,无法设置字体");
|
||||
return;
|
||||
@ -355,7 +374,7 @@ namespace MeowmentDebugTool
|
||||
|
||||
private void InitializeDebugTool()
|
||||
{
|
||||
Debug.Log("🚀 初始化UniversalDebugTool...");
|
||||
Debug.Log("[MeowmentDebugTool] 初始化UniversalDebugTool...");
|
||||
|
||||
// 创建模块实例
|
||||
parametersModule = new ParametersModule(
|
||||
@ -373,6 +392,9 @@ namespace MeowmentDebugTool
|
||||
settingsModule = new SettingsModule(
|
||||
settingsPage, widthInputField, heightInputField,
|
||||
applyResolutionButton, resetResolutionButton, currentResolutionText,
|
||||
infoBufferInputField, warningBufferInputField,
|
||||
errorBufferInputField, fatalBufferInputField,
|
||||
applyBufferButton, resetBufferButton,
|
||||
mainWindow, canvas);
|
||||
|
||||
consoleModule = new ConsoleModule(
|
||||
@ -380,14 +402,15 @@ namespace MeowmentDebugTool
|
||||
consoleDetailScrollRect, consoleDetailText,
|
||||
consoleClearButton, consoleLockScrollToggle,
|
||||
consoleInfoFilterToggle, consoleWarningFilterToggle,
|
||||
consoleErrorFilterToggle, consoleFatalFilterToggle, consoleLogItemPrefab);
|
||||
consoleErrorFilterToggle, consoleFatalFilterToggle,
|
||||
consoleTextFilterInputField, consoleLogItemPrefab);
|
||||
|
||||
// 添加所有模块到列表
|
||||
// 添加所有模块到列表(顺序:控制台、按钮、开关、数值、参数、设置)
|
||||
allModules.Add(consoleModule);
|
||||
allModules.Add(parametersModule);
|
||||
allModules.Add(customButtonsModule);
|
||||
allModules.Add(customCheckBoxesModule);
|
||||
allModules.Add(customValuesModule);
|
||||
allModules.Add(parametersModule);
|
||||
allModules.Add(settingsModule);
|
||||
|
||||
// 注册所有页面
|
||||
@ -396,7 +419,7 @@ namespace MeowmentDebugTool
|
||||
RegisterPage(module.GetModuleName(), module.GetPage());
|
||||
}
|
||||
|
||||
Debug.Log($"📄 已注册{pages.Count}个页面");
|
||||
Debug.Log($"[MeowmentDebugTool] 已注册{pages.Count}个页面");
|
||||
|
||||
// 创建标签按钮
|
||||
CreateTabButtons();
|
||||
@ -405,24 +428,16 @@ namespace MeowmentDebugTool
|
||||
if (closeButton != null)
|
||||
closeButton.onClick.AddListener(CloseDebugWindow);
|
||||
|
||||
// 设置悬浮按钮点击事件
|
||||
if (floatingButton != null)
|
||||
{
|
||||
Button floatBtn = floatingButton.GetComponent<Button>();
|
||||
if (floatBtn != null)
|
||||
{
|
||||
floatBtn.onClick.AddListener(OnFloatingButtonClick);
|
||||
}
|
||||
}
|
||||
// 注意:浮窗点击事件现在由DraggableFloatingButton的OnPointerClick处理
|
||||
|
||||
// 默认显示第一个页面
|
||||
// 默认显示按钮页面
|
||||
if (pages.Count > 0)
|
||||
{
|
||||
ShowPage("参数");
|
||||
ShowPage("按钮");
|
||||
}
|
||||
|
||||
Debug.Log("[OK] UniversalDebugTool初始化完成!");
|
||||
Debug.Log("💡 提示: 点击窗口顶部的标签切换页面,或按F1-F4使用快捷键");
|
||||
Debug.Log("[MeowmentDebugTool] 提示: 点击窗口顶部的标签切换页面,或按F1-F4使用快捷键");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -435,6 +450,13 @@ namespace MeowmentDebugTool
|
||||
{
|
||||
module.Initialize();
|
||||
}
|
||||
|
||||
// 设置ConsoleModule引用到SettingsModule
|
||||
if (settingsModule != null && consoleModule != null)
|
||||
{
|
||||
settingsModule.SetConsoleModule(consoleModule);
|
||||
}
|
||||
|
||||
Debug.Log("[OK] 所有模块初始化完成!");
|
||||
}
|
||||
|
||||
@ -453,12 +475,12 @@ namespace MeowmentDebugTool
|
||||
{
|
||||
string errorDetails = $"tabButtonPrefab={(tabButtonPrefab == null ? "null" : "已设置")}, " +
|
||||
$"tabButtonContainer={(tabButtonContainer == null ? "null" : "已设置")}";
|
||||
Debug.LogWarning($"⚠️ [MeowmentDebugTool] 标签按钮预制件或容器未完全初始化:{errorDetails}");
|
||||
Debug.LogWarning("💡 这可能是Unity初始化顺序问题,如果标签正常显示则可以忽略此警告");
|
||||
Debug.LogWarning($"⚠ [MeowmentDebugTool] 标签按钮预制件或容器未完全初始化:{errorDetails}");
|
||||
Debug.LogWarning("[!] 这可能是Unity初始化顺序问题,如果标签正常显示则可以忽略此警告");
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.Log($"📋 开始创建{pages.Count}个标签按钮...");
|
||||
Debug.Log($"[MeowmentDebugTool] 开始创建{pages.Count}个标签按钮...");
|
||||
|
||||
foreach (var page in pages)
|
||||
{
|
||||
@ -494,6 +516,12 @@ namespace MeowmentDebugTool
|
||||
/// </summary>
|
||||
public void ShowPage(string pageName)
|
||||
{
|
||||
if (!isInitialized)
|
||||
{
|
||||
Debug.LogWarning("[MeowmentDebugTool] 调试工具未初始化,无法显示页面");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pages.ContainsKey(pageName))
|
||||
{
|
||||
Debug.LogWarning($"页面 '{pageName}' 不存在!");
|
||||
@ -533,6 +561,12 @@ namespace MeowmentDebugTool
|
||||
/// </summary>
|
||||
public void CopyDeviceInfoToClipboard()
|
||||
{
|
||||
if (!isInitialized)
|
||||
{
|
||||
Debug.LogWarning("[MeowmentDebugTool] 调试工具未初始化");
|
||||
return;
|
||||
}
|
||||
|
||||
parametersModule?.CopyDeviceInfoToClipboard();
|
||||
}
|
||||
|
||||
@ -541,6 +575,12 @@ namespace MeowmentDebugTool
|
||||
/// </summary>
|
||||
public void CopySystemInfoToClipboard()
|
||||
{
|
||||
if (!isInitialized)
|
||||
{
|
||||
Debug.LogWarning("[MeowmentDebugTool] 调试工具未初始化");
|
||||
return;
|
||||
}
|
||||
|
||||
parametersModule?.CopySystemInfoToClipboard();
|
||||
}
|
||||
|
||||
@ -549,6 +589,12 @@ namespace MeowmentDebugTool
|
||||
/// </summary>
|
||||
public void RefreshAllInfo()
|
||||
{
|
||||
if (!isInitialized)
|
||||
{
|
||||
Debug.LogWarning("[MeowmentDebugTool] 调试工具未初始化");
|
||||
return;
|
||||
}
|
||||
|
||||
parametersModule?.RefreshAllInfo();
|
||||
}
|
||||
#endregion
|
||||
@ -559,7 +605,13 @@ namespace MeowmentDebugTool
|
||||
/// </summary>
|
||||
public static void SetCustomButtonCallback(Action<Button, TMP_Text> callback)
|
||||
{
|
||||
if (InstanceExists && Instance.customButtonsModule != null)
|
||||
if (!isInitialized || !InstanceExists)
|
||||
{
|
||||
Debug.LogWarning("[MeowmentDebugTool] 调试工具未初始化,无法设置按钮回调");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Instance.customButtonsModule != null)
|
||||
{
|
||||
Instance.customButtonsModule.SetCustomButtonCallback(callback);
|
||||
}
|
||||
@ -570,9 +622,64 @@ namespace MeowmentDebugTool
|
||||
/// </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
|
||||
|
||||
|
||||
|
||||
@ -642,15 +749,43 @@ namespace MeowmentDebugTool
|
||||
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 (InstanceExists)
|
||||
if (!isInitialized || !InstanceExists)
|
||||
{
|
||||
Instance.gameObject.SetActive(true);
|
||||
Debug.LogWarning("[MeowmentDebugTool] 调试工具未初始化,无法显示");
|
||||
return;
|
||||
}
|
||||
|
||||
Instance.gameObject.SetActive(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -658,10 +793,13 @@ namespace MeowmentDebugTool
|
||||
/// </summary>
|
||||
public static void Hide()
|
||||
{
|
||||
if (InstanceExists)
|
||||
if (!isInitialized || !InstanceExists)
|
||||
{
|
||||
Instance.gameObject.SetActive(false);
|
||||
Debug.LogWarning("[MeowmentDebugTool] 调试工具未初始化,无法隐藏");
|
||||
return;
|
||||
}
|
||||
|
||||
Instance.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -669,16 +807,19 @@ namespace MeowmentDebugTool
|
||||
/// </summary>
|
||||
public static void Toggle()
|
||||
{
|
||||
if (InstanceExists)
|
||||
if (!isInitialized || !InstanceExists)
|
||||
{
|
||||
if (Instance.mainWindow != null && Instance.mainWindow.gameObject.activeSelf)
|
||||
{
|
||||
Instance.CloseDebugWindow();
|
||||
}
|
||||
else
|
||||
{
|
||||
Instance.OpenDebugWindow();
|
||||
}
|
||||
Debug.LogWarning("[MeowmentDebugTool] 调试工具未初始化,无法切换状态");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Instance.mainWindow != null && Instance.mainWindow.gameObject.activeSelf)
|
||||
{
|
||||
Instance.CloseDebugWindow();
|
||||
}
|
||||
else
|
||||
{
|
||||
Instance.OpenDebugWindow();
|
||||
}
|
||||
}
|
||||
|
||||
@ -696,23 +837,6 @@ namespace MeowmentDebugTool
|
||||
Debug.Log("[关闭] 调试窗口已关闭");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 悬浮按钮点击回调(检查是否为拖动)
|
||||
/// </summary>
|
||||
private void OnFloatingButtonClick()
|
||||
{
|
||||
// 检查是否刚拖动过
|
||||
if (draggableComponent != null && draggableComponent.GetWasDragging())
|
||||
{
|
||||
// 如果刚拖动过,不打开窗口
|
||||
Debug.Log("🚫 拖动操作,不打开窗口");
|
||||
return;
|
||||
}
|
||||
|
||||
// 否则打开窗口
|
||||
OpenDebugWindow();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 打开调试窗口,隐藏悬浮按钮
|
||||
/// </summary>
|
||||
@ -745,7 +869,7 @@ namespace MeowmentDebugTool
|
||||
/// <param name="seconds">隐藏的秒数,默认5秒</param>
|
||||
public static void HideTemporarily(float seconds = 5f)
|
||||
{
|
||||
if (!InstanceExists)
|
||||
if (!isInitialized || !InstanceExists)
|
||||
{
|
||||
Debug.LogWarning("[MeowmentDebugTool] 调试工具未初始化,无法隐藏");
|
||||
return;
|
||||
@ -801,7 +925,7 @@ namespace MeowmentDebugTool
|
||||
// 5. 验证恢复结果
|
||||
if (canvas.enabled)
|
||||
{
|
||||
Debug.Log("[MeowmentDebugTool] ✓ UI已成功恢复(浮窗状态)");
|
||||
Debug.Log("[MeowmentDebugTool] [OK] UI已成功恢复(浮窗状态)");
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -813,65 +937,66 @@ namespace 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
|
||||
}
|
||||
// #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] 检测到四指点击!");
|
||||
Debug.Log("[MeowmentDebugTool] 检测到F键触发(测试模式)!");
|
||||
|
||||
// 检查主窗口或浮窗是否有任意一个显示
|
||||
bool anyUIVisible = (mainWindow != null && mainWindow.gameObject.activeSelf) ||
|
||||
@ -898,10 +1023,10 @@ namespace MeowmentDebugTool
|
||||
Debug.Log("[MeowmentDebugTool] 已显示浮窗");
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
// #endregion
|
||||
}
|
||||
|
||||
#region 自定义特性
|
||||
#region 自定义特性
|
||||
/// <summary>
|
||||
/// 调试按钮特性 - 标记方法为调试按钮
|
||||
///
|
||||
@ -909,7 +1034,7 @@ namespace MeowmentDebugTool
|
||||
/// 1. 在主项目中使用此特性时,请用条件编译包裹:
|
||||
///
|
||||
/// #if MEOWMENT_DEBUG_TOOL
|
||||
/// [DebugButton("我的调试按钮")]
|
||||
/// [DebugButton("默认", "我的调试按钮")]
|
||||
/// public static void MyDebugMethod()
|
||||
/// {
|
||||
/// Debug.Log("调试方法被执行");
|
||||
@ -918,19 +1043,22 @@ namespace MeowmentDebugTool
|
||||
///
|
||||
/// 2. 这样当包被卸载时,代码不会报错,也不会影响主工程的正常运行
|
||||
/// 3. MEOWMENT_DEBUG_TOOL 宏会在包安装时自动定义,卸载时自动移除
|
||||
/// 4. 组名参数用于对按钮进行分组显示,未指定组名的按钮会归入"默认"组
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
||||
public class DebugButtonAttribute : Attribute
|
||||
[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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
GroupName = string.IsNullOrEmpty(groupName) ? "默认" : groupName;
|
||||
DisplayName = displayName;
|
||||
ButtonColor = new Color(r, g, b, 1f);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 调试复选框特性 - 标记方法为调试复选框开关
|
||||
@ -951,18 +1079,18 @@ namespace MeowmentDebugTool
|
||||
/// 3. 这样当包被卸载时,代码不会报错,也不会影响主工程的正常运行
|
||||
/// 4. MEOWMENT_DEBUG_TOOL 宏会在包安装时自动定义,卸载时自动移除
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
||||
public class DebugCheckBoxAttribute : Attribute
|
||||
[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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
DisplayName = displayName;
|
||||
CheckBoxColor = new Color(r, g, b, 1f);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 调试数值特性 - 标记方法为调试数值调整器
|
||||
@ -984,24 +1112,24 @@ namespace MeowmentDebugTool
|
||||
/// 4. 可以设置自定义颜色
|
||||
/// 5. MEOWMENT_DEBUG_TOOL 宏会在包安装时自动定义,卸载时自动移除
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
||||
public class DebugValueAttribute : Attribute
|
||||
[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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
DisplayName = displayName;
|
||||
MinValue = minValue;
|
||||
MaxValue = maxValue;
|
||||
// 如果defaultValue为-1(未设置),则使用minValue作为默认值
|
||||
DefaultValue = defaultValue == -1 ? minValue : defaultValue;
|
||||
ValueColor = new Color(r, g, b, 1f);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "com.bywaystudios.meowmentdebugtool",
|
||||
"displayName": "MeowmentDebugTool",
|
||||
"version": "0.3.7",
|
||||
"version": "0.3.17",
|
||||
"description": "\u8c03\u8bd5\u5de5\u5177\uff0c\u96c6\u6210\u4e86\u5ba2\u6237\u7aef\u6d4b\u8bd5\u65f6\u7684\u5e38\u7528\u529f\u80fd\uff0c\u652f\u6301\u6269\u5c55\u81ea\u5b9a\u4e49\u6309\u94ae\uff0c\u65b9\u4fbf\u5f00\u53d1\u8005\u8c03\u8bd5\u548c\u6d4b\u8bd5\u3002",
|
||||
"samples": [
|
||||
{
|
||||
|
||||
156
功能实现总结.md
Normal file
156
功能实现总结.md
Normal file
@ -0,0 +1,156 @@
|
||||
# 功能实现完成 - 总结报告
|
||||
|
||||
## 实现的功能
|
||||
|
||||
### ✅ 1. Debugger显示状态查询
|
||||
|
||||
新增方法:`UniversalDebugTool.IsDebuggerVisible()`
|
||||
|
||||
**用途**:
|
||||
- 返回bool值,表示Debugger是否正在显示
|
||||
- 用于判断主窗口或悬浮按钮是否可见
|
||||
|
||||
**代码位置**:
|
||||
- [UniversalDebugTool.cs](Packages/com.bywaystudios.meowmentdebugtool/Runtime/UniversalDebugTool.cs#L719-L732)
|
||||
|
||||
**使用示例**:
|
||||
```csharp
|
||||
bool isVisible = UniversalDebugTool.IsDebuggerVisible();
|
||||
Debug.Log($"Debugger显示状态: {isVisible}");
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### ✅ 2. 按钮分组功能
|
||||
|
||||
**改动内容**:
|
||||
|
||||
1. **特性修改** - `DebugButtonAttribute`
|
||||
- 新增 `GroupName` 属性
|
||||
- 构造函数第一个参数改为组名
|
||||
- 默认组名为"默认"
|
||||
|
||||
2. **模块重构** - `CustomButtonsModule`
|
||||
- 添加分组数据结构
|
||||
- 自动创建分组切换UI容器
|
||||
- 实现按组加载和显示按钮
|
||||
- 添加组切换视觉反馈
|
||||
|
||||
**使用方式**:
|
||||
```csharp
|
||||
#if MEOWMENT_DEBUG_TOOL
|
||||
[MeowmentDebugTool.UniversalDebugTool.DebugButton("玩家", "加金币")]
|
||||
public static void AddGold()
|
||||
{
|
||||
Debug.Log("加金币");
|
||||
}
|
||||
#endif
|
||||
```
|
||||
|
||||
**功能特点**:
|
||||
- ✨ 自动分组:根据特性中的组名自动分类
|
||||
- 🎨 UI优化:顶部显示组切换按钮
|
||||
- 🔄 智能排序:"默认"组排在最前
|
||||
- 🎯 视觉反馈:当前组显示为绿色
|
||||
|
||||
---
|
||||
|
||||
## 文件修改清单
|
||||
|
||||
### 修改的文件
|
||||
1. ✏️ [UniversalDebugTool.cs](Packages/com.bywaystudios.meowmentdebugtool/Runtime/UniversalDebugTool.cs)
|
||||
- 添加 `IsDebuggerVisible()` 方法
|
||||
- 修改 `DebugButtonAttribute` 构造函数
|
||||
|
||||
2. ✏️ [CustomButtonsModule.cs](Packages/com.bywaystudios.meowmentdebugtool/Runtime/CustomButtonsModule.cs)
|
||||
- 完全重构以支持分组功能
|
||||
- 添加分组数据结构和UI逻辑
|
||||
|
||||
3. ✏️ [Test.cs](Assets/Scenes/Scripts/Test.cs)
|
||||
- 添加使用示例代码
|
||||
- 展示如何使用新的分组功能
|
||||
|
||||
### 新增的文件
|
||||
1. 📄 [新功能说明.md](新功能说明.md)
|
||||
- 详细的功能使用文档
|
||||
- 包含示例代码和注意事项
|
||||
|
||||
2. 📄 [功能实现总结.md](功能实现总结.md)
|
||||
- 本文件,快速查看实现内容
|
||||
|
||||
---
|
||||
|
||||
## 重要提示
|
||||
|
||||
### ⚠️ 破坏性更新
|
||||
|
||||
`DebugButton` 特性的参数顺序已改变:
|
||||
|
||||
**旧代码** ❌:
|
||||
```csharp
|
||||
[DebugButton("按钮名")]
|
||||
```
|
||||
|
||||
**新代码** ✅:
|
||||
```csharp
|
||||
[MeowmentDebugTool.UniversalDebugTool.DebugButton("默认", "按钮名")]
|
||||
```
|
||||
|
||||
### 🔧 迁移指南
|
||||
|
||||
如果有旧代码,需要:
|
||||
1. 在所有 `DebugButton` 特性前添加组名参数
|
||||
2. 使用完整的命名空间路径 `MeowmentDebugTool.UniversalDebugTool.DebugButton`
|
||||
3. 建议使用 `#if MEOWMENT_DEBUG_TOOL` 条件编译
|
||||
|
||||
---
|
||||
|
||||
## 测试建议
|
||||
|
||||
1. **基础功能测试**:
|
||||
- 调用 `IsDebuggerVisible()` 测试返回值
|
||||
- 尝试 Show/Hide 后检查状态
|
||||
|
||||
2. **分组功能测试**:
|
||||
- 创建多个不同组的按钮
|
||||
- 测试组切换是否正常
|
||||
- 验证按钮是否正确归类
|
||||
|
||||
3. **兼容性测试**:
|
||||
- 测试没有组名的按钮(应归入"默认"组)
|
||||
- 测试只有一个组的情况
|
||||
- 测试大量按钮的性能
|
||||
|
||||
---
|
||||
|
||||
## 已知问题
|
||||
|
||||
无
|
||||
|
||||
---
|
||||
|
||||
## 后续优化建议
|
||||
|
||||
1. **可配置性**:
|
||||
- 允许自定义分组按钮颜色
|
||||
- 允许自定义分组容器高度
|
||||
|
||||
2. **功能增强**:
|
||||
- 支持搜索/过滤按钮
|
||||
- 支持收藏常用按钮
|
||||
- 支持按钮排序
|
||||
|
||||
3. **UI改进**:
|
||||
- 更好的分组按钮布局(可滚动)
|
||||
- 添加分组图标支持
|
||||
|
||||
---
|
||||
|
||||
## 联系方式
|
||||
|
||||
如有问题或建议,请联系开发者。
|
||||
|
||||
---
|
||||
|
||||
**完成日期**:2025年12月30日
|
||||
**版本**:v1.1.0
|
||||
148
拖动功能问题修复说明.md
Normal file
148
拖动功能问题修复说明.md
Normal file
@ -0,0 +1,148 @@
|
||||
# 拖动功能问题修复说明
|
||||
|
||||
## 问题描述
|
||||
禁用调试工具的拖动功能后(`enableDragging = false`),发现其他UI上的棋子也无法拖动了。
|
||||
|
||||
## 问题原因
|
||||
|
||||
### Unity事件系统机制
|
||||
在Unity的事件系统中,当一个GameObject实现了拖动接口(`IBeginDragHandler`, `IDragHandler`, `IEndDragHandler`)时:
|
||||
|
||||
1. **事件会被该对象"拦截"**:即使在方法内部直接 `return`,Unity也认为该对象已经处理了事件
|
||||
2. **事件不会继续传递**:被拦截的事件不会传递给下层或其他UI元素
|
||||
3. **影响范围广泛**:这会导致场景中所有在该对象下方的可拖动UI元素都无法接收拖动事件
|
||||
|
||||
### 之前的实现问题
|
||||
```csharp
|
||||
public void OnBeginDrag(PointerEventData eventData)
|
||||
{
|
||||
if (!enableDragging) return; // ❌ 直接返回,但事件已被拦截
|
||||
// ... 正常的拖动逻辑
|
||||
}
|
||||
```
|
||||
|
||||
这种实现方式会导致:
|
||||
- 调试工具的悬浮按钮拦截了所有拖动事件
|
||||
- 其他UI元素(如棋子)无法接收到拖动事件
|
||||
- 即使 `enableDragging = false`,问题依然存在
|
||||
|
||||
## 解决方案
|
||||
|
||||
### 核心思路
|
||||
当 `enableDragging = false` 时,不应该简单地返回,而是应该将事件**主动传递**给其他可以处理该事件的UI元素。
|
||||
|
||||
### 实现细节
|
||||
|
||||
#### 1. 修改拖动事件处理方法
|
||||
在 `OnBeginDrag`, `OnDrag`, `OnEndDrag` 中添加事件传递逻辑:
|
||||
|
||||
```csharp
|
||||
public void OnBeginDrag(PointerEventData eventData)
|
||||
{
|
||||
if (!enableDragging)
|
||||
{
|
||||
// ✅ 将事件传递给父级或下层UI元素
|
||||
PassEventToParent(eventData, ExecuteEvents.beginDragHandler);
|
||||
return;
|
||||
}
|
||||
// ... 正常的拖动逻辑
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. 实现事件传递方法
|
||||
添加 `PassEventToParent` 方法,按以下顺序尝试传递事件:
|
||||
|
||||
```csharp
|
||||
private void PassEventToParent<T>(PointerEventData eventData, ExecuteEvents.EventFunction<T> eventFunction)
|
||||
where T : IEventSystemHandler
|
||||
{
|
||||
// 步骤1: 向上传递给父级对象
|
||||
Transform parent = transform.parent;
|
||||
while (parent != null)
|
||||
{
|
||||
if (ExecuteEvents.Execute(parent.gameObject, eventData, eventFunction))
|
||||
{
|
||||
return; // 父级处理了,停止传递
|
||||
}
|
||||
parent = parent.parent;
|
||||
}
|
||||
|
||||
// 步骤2: 如果父级都没处理,传递给射线检测到的下一个对象
|
||||
var raycastResults = new List<RaycastResult>();
|
||||
EventSystem.current.RaycastAll(eventData, raycastResults);
|
||||
|
||||
foreach (var result in raycastResults)
|
||||
{
|
||||
if (result.gameObject == gameObject)
|
||||
continue; // 跳过自己
|
||||
|
||||
if (ExecuteEvents.Execute(result.gameObject, eventData, eventFunction))
|
||||
{
|
||||
return; // 下层对象处理了,停止传递
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 工作原理
|
||||
|
||||
1. **事件拦截**:悬浮按钮首先接收到拖动事件
|
||||
2. **条件判断**:检查 `enableDragging` 是否为 `false`
|
||||
3. **向上传递**:尝试将事件传递给父级对象树
|
||||
4. **射线检测**:如果父级未处理,通过射线检测找到下层UI元素
|
||||
5. **事件执行**:让找到的对象执行相应的拖动事件处理
|
||||
|
||||
## 修改文件
|
||||
- `Packages/com.bywaystudios.meowmentdebugtool/Runtime/DraggableFloatingButton.cs`
|
||||
- 修改 `OnBeginDrag` 方法
|
||||
- 修改 `OnDrag` 方法
|
||||
- 修改 `OnEndDrag` 方法
|
||||
- 新增 `PassEventToParent` 方法
|
||||
|
||||
## 测试建议
|
||||
|
||||
### 测试场景1:禁用拖动模式
|
||||
1. 设置 `enableDragging = false`
|
||||
2. 点击悬浮按钮 → 应该正常打开调试窗口
|
||||
3. 尝试拖动其他UI上的棋子 → **应该能够正常拖动**
|
||||
|
||||
### 测试场景2:启用拖动模式
|
||||
1. 设置 `enableDragging = true`
|
||||
2. 拖动悬浮按钮 → 应该能够正常拖动
|
||||
3. 点击悬浮按钮(不拖动)→ 应该打开调试窗口
|
||||
|
||||
### 测试场景3:多层UI叠加
|
||||
1. 创建多个可拖动的UI元素,部分重叠
|
||||
2. 确保悬浮按钮在最上层
|
||||
3. 禁用悬浮按钮拖动后,测试下层UI元素能否正常拖动
|
||||
|
||||
## 技术要点
|
||||
|
||||
### Unity事件传递机制
|
||||
- `ExecuteEvents.Execute()`:手动触发事件处理
|
||||
- `EventSystem.RaycastAll()`:获取所有被射线击中的UI元素
|
||||
- 事件处理器接口:`IEventSystemHandler`
|
||||
|
||||
### 泛型约束
|
||||
使用泛型方法支持不同类型的事件处理:
|
||||
```csharp
|
||||
private void PassEventToParent<T>(...) where T : IEventSystemHandler
|
||||
```
|
||||
|
||||
### 事件类型
|
||||
- `ExecuteEvents.beginDragHandler` - 开始拖动
|
||||
- `ExecuteEvents.dragHandler` - 拖动中
|
||||
- `ExecuteEvents.endDragHandler` - 结束拖动
|
||||
|
||||
## 可能的副作用
|
||||
|
||||
### 性能考虑
|
||||
- `RaycastAll` 会执行完整的射线检测,可能有轻微性能开销
|
||||
- 仅在 `enableDragging = false` 时触发,实际影响很小
|
||||
|
||||
### 兼容性
|
||||
- 需要确保场景中有 `EventSystem`
|
||||
- 依赖 Unity 的 EventSystem 模块
|
||||
|
||||
## 总结
|
||||
通过主动传递事件而不是简单地忽略事件,解决了禁用拖动后影响其他UI元素的问题。这是一个典型的UI事件系统问题,需要理解Unity的事件传递机制才能正确处理。
|
||||
233
新功能说明.md
Normal file
233
新功能说明.md
Normal file
@ -0,0 +1,233 @@
|
||||
# MeowmentDebugTool 新功能说明
|
||||
|
||||
## 更新日期
|
||||
2025年12月30日
|
||||
|
||||
## 新增功能
|
||||
|
||||
### 1. 获取Debugger显示状态
|
||||
|
||||
新增了一个公共静态方法 `IsDebuggerVisible()`,用于获取Debugger的当前显示状态。
|
||||
|
||||
#### 使用方法
|
||||
|
||||
```csharp
|
||||
// 获取Debugger是否正在显示
|
||||
bool isVisible = UniversalDebugTool.IsDebuggerVisible();
|
||||
|
||||
if (isVisible)
|
||||
{
|
||||
Debug.Log("Debugger正在显示");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("Debugger已隐藏或未初始化");
|
||||
}
|
||||
```
|
||||
|
||||
#### 方法说明
|
||||
|
||||
- **方法签名**: `public static bool IsDebuggerVisible()`
|
||||
- **返回值**:
|
||||
- `true` - Debugger正在显示(主窗口或悬浮按钮可见)
|
||||
- `false` - Debugger完全隐藏或未初始化
|
||||
- **使用场景**:
|
||||
- 判断是否需要显示/隐藏Debugger
|
||||
- 根据Debugger状态执行不同的逻辑
|
||||
- 在截图或录屏前检查Debugger状态
|
||||
|
||||
---
|
||||
|
||||
### 2. 按钮分组功能
|
||||
|
||||
为调试按钮添加了分组功能,可以将按钮按功能分类显示,提高大量按钮时的使用体验。
|
||||
|
||||
#### 使用方法
|
||||
|
||||
在使用 `DebugButton` 特性时,在第一个参数指定组名:
|
||||
|
||||
```csharp
|
||||
using MeowmentDebugTool;
|
||||
|
||||
#if MEOWMENT_DEBUG_TOOL
|
||||
// 默认组
|
||||
[DebugButton("默认", "我的按钮")]
|
||||
public static void MyButton()
|
||||
{
|
||||
Debug.Log("按钮被点击");
|
||||
}
|
||||
|
||||
// 玩家相关按钮
|
||||
[DebugButton("玩家", "加金币")]
|
||||
public static void AddGold()
|
||||
{
|
||||
Debug.Log("给玩家加了1000金币");
|
||||
}
|
||||
|
||||
[DebugButton("玩家", "升级")]
|
||||
public static void LevelUp()
|
||||
{
|
||||
Debug.Log("玩家升了1级");
|
||||
}
|
||||
|
||||
// 关卡相关按钮
|
||||
[DebugButton("关卡", "跳过当前关卡")]
|
||||
public static void SkipLevel()
|
||||
{
|
||||
Debug.Log("跳过当前关卡");
|
||||
}
|
||||
|
||||
[DebugButton("关卡", "解锁所有关卡", 0.8f, 0.6f, 0.2f)]
|
||||
public static void UnlockAllLevels()
|
||||
{
|
||||
Debug.Log("解锁所有关卡");
|
||||
}
|
||||
#endif
|
||||
```
|
||||
|
||||
**注意**:确保在文件顶部添加 `using MeowmentDebugTool;` 以便可以直接使用 `DebugButton` 特性。
|
||||
|
||||
#### 特性参数说明
|
||||
|
||||
`DebugButton` 特性的新参数顺序:
|
||||
|
||||
```csharp
|
||||
DebugButtonAttribute(
|
||||
string groupName = "默认", // 组名(新增)
|
||||
string displayName = "", // 显示名称
|
||||
float r = 0.8f, // 按钮颜色 R
|
||||
float g = 0.8f, // 按钮颜色 G
|
||||
float b = 0.8f // 按钮颜色 B
|
||||
)
|
||||
```
|
||||
|
||||
#### 功能特点
|
||||
|
||||
1. **自动分组**: 系统会自动收集所有带有 `DebugButton` 特性的方法,并按组名分类
|
||||
2. **组标题显示**: 每个组会显示一个标题行,标题格式为:`━━━━━━ 组名 ━━━━━━`
|
||||
3. **默认组**: 如果不指定组名或组名为空,按钮会被归入"默认"组
|
||||
4. **组排序**: "默认"组会排在最前面,其他组按字母顺序排列
|
||||
5. **一次显示所有**: 所有组的按钮会在一个滚动视图中垂直显示,无需切换
|
||||
|
||||
#### 界面布局
|
||||
|
||||
```
|
||||
┌─────────────────────────────────┐
|
||||
│ ━━━━━━ 默认 ━━━━━━ │ ← 组标题
|
||||
│ [测试按钮1] │
|
||||
│ [测试按钮2] │ ← 该组的按钮
|
||||
│ │
|
||||
│ ━━━━━━ 玩家 ━━━━━━ │ ← 组标题
|
||||
│ [加金币] │
|
||||
│ [升级] │
|
||||
│ [满血复活] │ ← 该组的按钮
|
||||
│ │
|
||||
│ ━━━━━━ 关卡 ━━━━━━ │ ← 组标题
|
||||
│ [跳过当前关卡] │
|
||||
│ [重置关卡进度] │
|
||||
│ [解锁所有关卡] │ ← 该组的按钮
|
||||
│ ... │
|
||||
└─────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### 注意事项
|
||||
|
||||
1. **兼容性**: 这是一个破坏性更新,旧代码需要调整参数顺序
|
||||
|
||||
**旧代码**:
|
||||
```csharp
|
||||
[DebugButton("按钮名称")]
|
||||
```
|
||||
|
||||
**新代码** (需要添加组名):
|
||||
```csharp
|
||||
[DebugButton("默认", "按钮名称")]
|
||||
```
|
||||
|
||||
2. **组名建议**:
|
||||
- 使用简短明确的组名(如:玩家、关卡、道具、系统等)
|
||||
- 相关功能的按钮放在同一组
|
||||
- 常用功能建议放在"默认"组
|
||||
|
||||
3. **性能**: 分组功能不会影响性能,因为:
|
||||
- 只在初始化和手动重载时扫描方法
|
||||
- 所有按钮一次性创建,无需动态切换
|
||||
|
||||
---
|
||||
|
||||
## 示例代码
|
||||
|
||||
完整的使用示例请参考 `Assets/Scenes/Scripts/Test.cs` 文件。
|
||||
|
||||
## 向后兼容性说明
|
||||
|
||||
⚠️ **重要**: `DebugButton` 特性的参数顺序已更改,旧代码需要手动调整。
|
||||
|
||||
如果你之前这样使用:
|
||||
```csharp
|
||||
[DebugButton("我的按钮", 0.5f, 0.5f, 0.5f)]
|
||||
```
|
||||
|
||||
现在需要改为:
|
||||
```csharp
|
||||
[DebugButton("默认", "我的按钮", 0.5f, 0.5f, 0.5f)]
|
||||
```
|
||||
|
||||
或者如果不需要自定义颜色:
|
||||
```csharp
|
||||
[DebugButton("默认", "我的按钮")]
|
||||
```
|
||||
|
||||
**注意**:请确保在文件顶部添加 `using MeowmentDebugTool;`
|
||||
|
||||
---
|
||||
|
||||
## 技术细节
|
||||
|
||||
### CustomButtonsModule 的改动
|
||||
|
||||
1. 添加了分组数据结构 `Dictionary<string, List<ButtonInfo>>`
|
||||
2. 添加了分组UI容器的自动创建逻辑
|
||||
3. 修改了按钮加载流程,先分组再显示
|
||||
4. 添加了组切换功能和视觉反馈
|
||||
|
||||
### UniversalDebugTool 的改动
|
||||
|
||||
1. 添加了 `IsDebuggerVisible()` 方法
|
||||
2. 修改了 `DebugButtonAttribute` 的构造函数,添加了 `groupName` 参数
|
||||
3. 更新了文档注释
|
||||
|
||||
---
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q: 如何知道当前有哪些组?
|
||||
A: 在初始化时,控制台会输出所有找到的组及其按钮数量:
|
||||
```
|
||||
[CustomButtonsModule] 共找到 3 个按钮组
|
||||
- 默认: 2 个按钮
|
||||
- 玩家: 3 个按钮
|
||||
- 关卡: 3 个按钮
|
||||
```
|
||||
|
||||
### Q: 能否动态添加组?
|
||||
A: 可以,调用 `ReloadCustomButtons()` 方法即可重新扫描所有按钮并更新分组。
|
||||
|
||||
### Q: 分组按钮的样式可以自定义吗?
|
||||
A: 可以,分组按钮使用与普通按钮相同的预制件,可以通过修改预制件来自定义样式。
|
||||
|
||||
### Q: IsDebuggerVisible() 什么情况下返回 false?
|
||||
A: 以下情况会返回 false:
|
||||
- Debugger 未初始化(未调用 `Init()`)
|
||||
- 主窗口和悬浮按钮都被隐藏
|
||||
- Debugger GameObject 被销毁
|
||||
|
||||
---
|
||||
|
||||
## 更新日志
|
||||
|
||||
### v1.1.0 (2025-12-30)
|
||||
- ✨ 新增 `IsDebuggerVisible()` 方法用于获取显示状态
|
||||
- ✨ 新增按钮分组功能
|
||||
- 🔧 修改 `DebugButtonAttribute` 构造函数参数顺序(破坏性更新)
|
||||
- 📝 更新文档和示例代码
|
||||
Loading…
Reference in New Issue
Block a user