更新meowmentdebugtool 增加开关,数值功能,去掉工具栏功能
This commit is contained in:
parent
62f990f702
commit
bdb24b05d1
@ -5,6 +5,73 @@
|
||||
格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/),
|
||||
版本号遵循 [Semantic Versioning](https://semver.org/lang/zh-CN/)。
|
||||
|
||||
|
||||
|
||||
## [0.3.7] - 2025-12-24
|
||||
|
||||
### Added
|
||||
- **数值特性增强**
|
||||
- `[DebugValue]` 特性新增 `defaultValue` 参数,用于设置初始默认值
|
||||
- 参数顺序:`displayName`, `minValue`, `maxValue`, `defaultValue`, `r`, `g`, `b`
|
||||
- 若未指定 `defaultValue`(或传入-1),则自动使用 `minValue` 作为默认值
|
||||
|
||||
### Changed
|
||||
- **模块命名优化**
|
||||
- "自定义数值"页面重命名为"数值",简化用户界面
|
||||
- 统一更新相关日志消息和UI文本
|
||||
|
||||
### Improved
|
||||
- **用户体验提升**
|
||||
- 数值页面的确认按钮点击后自动关闭主窗口(与自定义按钮保持一致)
|
||||
- 优化操作流程,调整数值后自动返回游戏界面
|
||||
|
||||
|
||||
|
||||
## [0.3.6] - 2025-12-24
|
||||
|
||||
### Added
|
||||
- **自定义复选框页面 (CheckBoxes)**
|
||||
- 新增 `[DebugCheckBox]` 特性,用于创建开关功能
|
||||
- 支持自定义显示名称和复选框颜色(RGB参数)
|
||||
- 自动通过反射扫描并加载所有带该特性的静态方法
|
||||
- 方法必须接收一个 `bool` 参数作为开关状态
|
||||
- 支持状态保存,切换页面后保持开关状态
|
||||
- 使用 ScrollView 支持大量复选框滚动显示
|
||||
- 单行布局,每个复选框占据一行
|
||||
|
||||
- **自定义数值页面 (Values)**
|
||||
- 新增 `[DebugValue]` 特性,用于创建整数值调整功能
|
||||
- 支持自定义显示名称、最小值、最大值、颜色(RGB参数)
|
||||
- 复合UI组件:标题 + 滑块 + 输入框 + 确认按钮
|
||||
- 滑块和输入框双向绑定,实时同步数值
|
||||
- 数值自动限制在指定范围内(最小值-最大值)
|
||||
- 方法必须接收一个 `int` 参数
|
||||
- 支持状态保存,切换页面后保持当前值
|
||||
- 使用 ScrollView 支持大量数值项滚动显示
|
||||
- 两行布局:标题行 + 控制行(滑块55% + 输入框17% + 按钮26%)
|
||||
|
||||
### Changed
|
||||
- 移除工具栏页面及相关功能(时间调整功能已不再需要)
|
||||
- 优化UI布局尺寸
|
||||
- 复选框项宽度:800px,高度:80px
|
||||
- 数值调整项宽度:900px,高度:140px
|
||||
- 改进标题可见性,采用两行布局设计
|
||||
|
||||
### Fixed
|
||||
- 修复运行时UI生成时的 NullReferenceException 问题
|
||||
- 修复 TMP_InputField 的 textComponent 必须为子对象的问题
|
||||
- 修复非激活父对象下创建UI组件的问题
|
||||
|
||||
|
||||
## [0.3.5] - 2025-12-22
|
||||
|
||||
- 增加四指隐藏Debugger显示效果
|
||||
- 性能优化,update代码优化
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [0.3.0] - 2025-12-22
|
||||
|
||||
### Added
|
||||
|
||||
@ -5,15 +5,16 @@ namespace MeowmentDebugTool.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// 自动添加 MEOWMENT_DEBUG_TOOL 宏定义
|
||||
/// 注意:已禁用自动添加功能,如需启用请取消注释 [InitializeOnLoad] 特性
|
||||
/// </summary>
|
||||
[InitializeOnLoad]
|
||||
// [InitializeOnLoad] // 已注释,不再自动添加宏定义
|
||||
public static class DefineSymbolsManager
|
||||
{
|
||||
private const string DEFINE_SYMBOL = "MEOWMENT_DEBUG_TOOL";
|
||||
|
||||
static DefineSymbolsManager()
|
||||
{
|
||||
AddDefineSymbol();
|
||||
// AddDefineSymbol(); // 已注释,不再自动添加
|
||||
}
|
||||
|
||||
private static void AddDefineSymbol()
|
||||
|
||||
@ -1,547 +1,85 @@
|
||||
# MeowMent Debug Tool(喵刻调试工具)
|
||||
# MeowMent Debug Tool
|
||||
|
||||
一个强大的 Unity 运行时调试工具,提供多标签页调试面板、控制台日志、可拖拽悬浮按钮、系统信息查看以及自定义调试按钮等功能。
|
||||
Unity 运行时调试工具。
|
||||
|
||||
**版本:** 0.3.0
|
||||
**适用平台:** 全平台(Windows、Android、iOS等)
|
||||
**Unity版本:** 2021.3 及以上
|
||||
|
||||
---
|
||||
|
||||
## 📋 目录
|
||||
## 目录
|
||||
|
||||
- [功能特性](#功能特性)
|
||||
- [给策划使用 - 基础功能](#给策划使用---基础功能)
|
||||
- [给客户端使用 - 初始化配置](#给客户端使用---初始化配置)
|
||||
- [给后端使用 - 添加自定义按钮](#给后端使用---添加自定义按钮)
|
||||
- [常见问题](#常见问题)
|
||||
- [给策划](#给策划)
|
||||
- [给客户端](#给客户端)
|
||||
- [给服务端](#给服务端)
|
||||
|
||||
---
|
||||
## 给策划
|
||||
|
||||
## 🎯 功能特性
|
||||
点击屏幕左侧蓝色浮窗打开调试窗口。
|
||||
|
||||
### 核心功能模块
|
||||
- **参数查看**:实时查看设备信息、系统信息,支持一键复制
|
||||
- **自定义按钮**:通过特性自动生成调试按钮,无需手动添加
|
||||
- **工具栏**:时间调整工具、截图隐藏功能
|
||||
- **控制台**:实时捕获Unity日志,支持过滤、搜索、查看堆栈
|
||||
- **设置**:运行时修改分辨率
|
||||
**参数查看** - 查看设备信息、系统信息,可复制
|
||||
**自定义按钮** - 执行调试功能(清空数据、添加道具等)
|
||||
**开关** - 功能开关控制(开启/关闭某些功能)
|
||||
**数值** - 数值调整(通过滑块和输入框调整整数值)
|
||||
**控制台** - 查看日志、过滤、堆栈信息
|
||||
**设置** - 调整分辨率
|
||||
|
||||
### UI特性
|
||||
- 可拖拽悬浮按钮,自动吸附边缘
|
||||
- 多标签页设计,清晰分类
|
||||
- Canvas层级30000,始终在最上层
|
||||
- 支持自定义TextMeshPro字体
|
||||
四指点击屏幕是隐藏和现实Debugger工具
|
||||
|
||||
---
|
||||
## 给客户端
|
||||
|
||||
## 👥 给策划使用 - 基础功能
|
||||
|
||||
### 1. 如何打开调试工具
|
||||
|
||||
游戏运行后,屏幕左侧会显示一个**蓝色圆形浮窗按钮**:
|
||||
|
||||
- **点击浮窗** → 打开调试主窗口
|
||||
- **拖动浮窗** → 改变位置(会自动吸附到屏幕边缘)
|
||||
- **点击左上角X** → 关闭主窗口,回到浮窗状态
|
||||
|
||||
### 2. 功能页面说明
|
||||
|
||||
调试窗口包含5个标签页:
|
||||
|
||||
#### 📊 参数查看
|
||||
- **设备信息**:设备型号、系统版本、处理器、内存等
|
||||
- **系统信息**:Unity版本、图形API、显卡信息等
|
||||
- **复制按钮**:点击可复制信息到剪贴板,方便粘贴到bug报告
|
||||
|
||||
#### 🎮 自定义按钮
|
||||
- 显示后端程序员添加的调试功能按钮
|
||||
- 点击按钮即可执行对应功能(如清理存档、添加道具等)
|
||||
- 不同按钮有不同颜色,方便区分
|
||||
|
||||
**常见按钮示例:**
|
||||
- "清空玩家数据" - 重置游戏进度
|
||||
- "添加金币" - 增加游戏货币
|
||||
- "解锁所有关卡" - 开启所有内容
|
||||
- "重置今日任务" - 刷新每日任务
|
||||
|
||||
#### 🛠️ 工具栏
|
||||
- **时间调整**:模拟时间流逝(用于测试时间相关功能)
|
||||
- **暂时隐藏(5秒)**:截图前点击此按钮,调试工具会隐藏5秒,方便截取干净的游戏画面
|
||||
|
||||
**使用场景:**
|
||||
1. 需要截图游戏画面时
|
||||
2. 点击"暂时隐藏(5秒)"
|
||||
3. 调试工具消失,快速截图
|
||||
4. 5秒后自动恢复,继续调试
|
||||
|
||||
#### 📝 控制台
|
||||
实时显示游戏日志,帮助定位问题:
|
||||
|
||||
- **Info(白色)**:普通信息日志
|
||||
- **Warning(黄色)**:警告信息
|
||||
- **Error(红色)**:错误信息
|
||||
- **Fatal(深红)**:严重错误/异常
|
||||
|
||||
**功能按钮:**
|
||||
- **清空**:清除所有日志
|
||||
- **锁定滚动**:勾选后自动滚动到最新日志
|
||||
- **过滤器**:点击Info/Warning/Error切换显示对应类型日志
|
||||
|
||||
**查看详情:**
|
||||
- 点击任意日志条目 → 下方显示完整堆栈信息
|
||||
- 方便向程序员反馈详细错误
|
||||
|
||||
#### ⚙️ 设置
|
||||
- **分辨率调整**:修改游戏窗口大小(仅编辑器/Windows有效)
|
||||
- **当前分辨率显示**:显示当前屏幕分辨率
|
||||
|
||||
### 3. 日常测试工作流
|
||||
|
||||
**测试新功能:**
|
||||
1. 打开调试工具
|
||||
2. 切换到"自定义按钮"页面
|
||||
3. 点击相关测试按钮(如"开启活动")
|
||||
4. 观察游戏效果
|
||||
5. 如有问题,切换到"控制台"查看错误
|
||||
|
||||
**截图报bug:**
|
||||
1. 复现问题
|
||||
2. 点击"工具栏" → "暂时隐藏(5秒)"
|
||||
3. 截取游戏画面
|
||||
4. 切换回"控制台"截取错误日志
|
||||
5. 一起提交给程序
|
||||
|
||||
**收集设备信息:**
|
||||
1. 切换到"参数查看"
|
||||
2. 点击"复制设备信息"或"复制系统信息"
|
||||
3. 粘贴到bug报告中
|
||||
|
||||
---
|
||||
|
||||
## 💻 给客户端使用 - 初始化配置
|
||||
|
||||
### 1. 基础初始化
|
||||
|
||||
在游戏启动时(通常在启动场景的初始化脚本中)调用:
|
||||
在游戏启动时初始化:
|
||||
|
||||
```csharp
|
||||
using UnityEngine;
|
||||
using MeowmentDebugTool;
|
||||
using TMPro;
|
||||
|
||||
public class GameInitializer : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private TMP_FontAsset customFont; // 拖入自定义字体
|
||||
|
||||
void Start()
|
||||
{
|
||||
// 初始化调试工具
|
||||
UniversalDebugTool.Init();
|
||||
|
||||
// 设置自定义字体(可选)
|
||||
if (customFont != null)
|
||||
{
|
||||
UniversalDebugTool.SetSDFFont(customFont);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. API说明
|
||||
|
||||
#### `UniversalDebugTool.Init()`
|
||||
初始化调试工具,自动创建:
|
||||
- Canvas(排序顺序30000)
|
||||
- 主调试窗口
|
||||
- 悬浮按钮
|
||||
- EventSystem(如果没有)
|
||||
|
||||
**注意:**
|
||||
- 只需调用一次
|
||||
- 工具会自动DontDestroyOnLoad,场景切换不销毁
|
||||
- 未调用Init()前不显示任何UI
|
||||
|
||||
#### `UniversalDebugTool.SetSDFFont(TMP_FontAsset fontAsset)`
|
||||
设置所有UI文本的字体:
|
||||
- 包括已创建的UI和后续创建的按钮
|
||||
- 支持中文、特殊字符
|
||||
- 建议使用支持中文的SDF字体
|
||||
|
||||
**字体推荐:**
|
||||
- 思源黑体 SDF
|
||||
- 阿里巴巴普惠体 SDF
|
||||
- 或项目现有的中文字体
|
||||
|
||||
### 3. 条件编译(推荐)
|
||||
|
||||
为了确保打包时不包含调试代码:
|
||||
|
||||
```csharp
|
||||
void Start()
|
||||
{
|
||||
#if MEOWMENT_DEBUG_TOOL
|
||||
UniversalDebugTool.Init();
|
||||
UniversalDebugTool.SetSDFFont(customFont);
|
||||
UniversalDebugTool.SetSDFFont(customFont); // 可选,设置中文字体
|
||||
#endif
|
||||
}
|
||||
```
|
||||
|
||||
**说明:**
|
||||
- `MEOWMENT_DEBUG_TOOL` 宏在包安装时自动定义
|
||||
- 移除包时自动移除,不影响项目编译
|
||||
- 正式包不会包含调试工具代码
|
||||
## 给服务端
|
||||
|
||||
### 4. 完整示例
|
||||
用 `[DebugButton]` 特性标记静态方法自动生成按钮:
|
||||
|
||||
```csharp
|
||||
using UnityEngine;
|
||||
using MeowmentDebugTool;
|
||||
using TMPro;
|
||||
|
||||
public class Test : MonoBehaviour
|
||||
public static class DebugFunctions
|
||||
{
|
||||
public TMP_FontAsset fontAsset;
|
||||
|
||||
void Start()
|
||||
{
|
||||
#if MEOWMENT_DEBUG_TOOL
|
||||
// 初始化调试工具
|
||||
UniversalDebugTool.Init();
|
||||
|
||||
// 设置字体
|
||||
if (fontAsset != null)
|
||||
{
|
||||
UniversalDebugTool.SetSDFFont(fontAsset);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 给后端使用 - 添加自定义按钮
|
||||
|
||||
### 1. 基础用法
|
||||
|
||||
使用 `[DebugButton]` 特性标记静态方法,调试工具会自动生成按钮:
|
||||
|
||||
```csharp
|
||||
using UnityEngine;
|
||||
|
||||
public static class PlayerDebugFunctions
|
||||
{
|
||||
[DebugButton("清空玩家数据")]
|
||||
private static void ClearPlayerData()
|
||||
[DebugButton("清空数据")]
|
||||
private static void ClearData()
|
||||
{
|
||||
PlayerPrefs.DeleteAll();
|
||||
Debug.Log("玩家数据已清空");
|
||||
}
|
||||
|
||||
[DebugButton("添加1000金币")]
|
||||
// 设置按钮颜色 (r, g, b)
|
||||
[DebugButton("添加金币", 0.2f, 0.8f, 0.2f)]
|
||||
private static void AddCoins()
|
||||
{
|
||||
PlayerData.Coins += 1000;
|
||||
Debug.Log($"当前金币:{PlayerData.Coins}");
|
||||
}
|
||||
|
||||
// 复选框开关,方法必须接收bool参数
|
||||
[DebugCheckBox("无敌模式", 1f, 0.5f, 0f)]
|
||||
private static void ToggleGodMode(bool isOn)
|
||||
{
|
||||
Player.IsInvincible = isOn;
|
||||
}
|
||||
|
||||
// 数值调整,方法必须接收int参数
|
||||
// 参数:显示名称, 最小值, 最大值, 默认值, r, g, b
|
||||
[DebugValue("玩家等级", 1, 100, 50, 0f, 0.8f, 1f)]
|
||||
private static void SetPlayerLevel(int level)
|
||||
{
|
||||
Player.Level = level;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**运行效果:**
|
||||
- 在"自定义按钮"页面会出现两个按钮
|
||||
- 点击按钮执行对应方法
|
||||
## 常见问题
|
||||
|
||||
### 2. 特性参数详解
|
||||
**字体显示方块?** 调用 `SetSDFFont()` 设置中文字体
|
||||
**按钮不显示?** 检查方法是否为 `static` 且有 `[DebugButton]` 特性
|
||||
**如何移除?** 删除 Packages 下的工具包文件夹
|
||||
|
||||
```csharp
|
||||
[DebugButton(string displayName = "", float r = 0.8f, float g = 0.8f, float b = 0.8f)]
|
||||
```
|
||||
|
||||
#### 参数说明:
|
||||
- **displayName**:按钮显示文字(不填则使用方法名)
|
||||
- **r, g, b**:按钮背景颜色(RGB值,范围0-1)
|
||||
|
||||
#### 示例:不同颜色按钮
|
||||
|
||||
```csharp
|
||||
public static class GameDebugFunctions
|
||||
{
|
||||
// 绿色按钮 - 安全操作
|
||||
[DebugButton("保存游戏", 0.2f, 0.6f, 0.2f)]
|
||||
private static void SaveGame()
|
||||
{
|
||||
GameManager.Save();
|
||||
}
|
||||
|
||||
// 红色按钮 - 危险操作
|
||||
[DebugButton("删除存档", 0.9f, 0.2f, 0.2f)]
|
||||
private static void DeleteSave()
|
||||
{
|
||||
GameManager.DeleteSave();
|
||||
}
|
||||
|
||||
// 蓝色按钮 - 功能测试
|
||||
[DebugButton("跳到第10关", 0.2f, 0.5f, 0.9f)]
|
||||
private static void JumpToLevel10()
|
||||
{
|
||||
LevelManager.LoadLevel(10);
|
||||
}
|
||||
|
||||
// 默认颜色(灰色)
|
||||
[DebugButton("打印游戏状态")]
|
||||
private static void PrintGameState()
|
||||
{
|
||||
Debug.Log($"Level: {GameManager.CurrentLevel}");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 使用输入对话框
|
||||
|
||||
需要用户输入参数时,使用 `ShowInputDialog`:
|
||||
|
||||
```csharp
|
||||
using TMPro;
|
||||
|
||||
public static class AdvancedDebugFunctions
|
||||
{
|
||||
[DebugButton("设置玩家等级")]
|
||||
private static void SetPlayerLevel()
|
||||
{
|
||||
UniversalDebugTool.ShowInputDialog(
|
||||
"输入等级",
|
||||
onConfirmAction: text =>
|
||||
{
|
||||
if (int.TryParse(text, out int level))
|
||||
{
|
||||
PlayerData.Level = level;
|
||||
Debug.Log($"等级已设置为:{level}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("请输入有效数字");
|
||||
}
|
||||
},
|
||||
initialValue: "1",
|
||||
contentType: TMP_InputField.ContentType.IntegerNumber
|
||||
);
|
||||
}
|
||||
|
||||
[DebugButton("设置玩家名称")]
|
||||
private static void SetPlayerName()
|
||||
{
|
||||
UniversalDebugTool.ShowInputDialog(
|
||||
"输入名称",
|
||||
onConfirmAction: name =>
|
||||
{
|
||||
PlayerData.Name = name;
|
||||
Debug.Log($"名称已设置为:{name}");
|
||||
},
|
||||
initialValue: PlayerData.Name,
|
||||
contentType: TMP_InputField.ContentType.Standard
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 条件编译(推荐)
|
||||
|
||||
为了确保正式版不包含调试代码:
|
||||
|
||||
```csharp
|
||||
public static class ItemDebugFunctions
|
||||
{
|
||||
#if MEOWMENT_DEBUG_TOOL
|
||||
[DebugButton("添加道具")]
|
||||
private static void AddItem()
|
||||
{
|
||||
ItemManager.AddItem(1001, 10);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 高级技巧
|
||||
|
||||
#### 技巧1:分类管理
|
||||
按功能模块创建不同的类:
|
||||
|
||||
```csharp
|
||||
// 玩家相关
|
||||
public static class PlayerDebug { ... }
|
||||
|
||||
// 关卡相关
|
||||
public static class LevelDebug { ... }
|
||||
|
||||
// 道具相关
|
||||
public static class ItemDebug { ... }
|
||||
```
|
||||
|
||||
#### 技巧2:快速测试流程
|
||||
|
||||
```csharp
|
||||
[DebugButton("快速进入战斗")]
|
||||
private static void QuickEnterBattle()
|
||||
{
|
||||
// 1. 设置测试数据
|
||||
PlayerData.Level = 10;
|
||||
PlayerData.Coins = 9999;
|
||||
|
||||
// 2. 解锁功能
|
||||
FeatureManager.UnlockAll();
|
||||
|
||||
// 3. 跳转场景
|
||||
SceneManager.LoadScene("Battle");
|
||||
}
|
||||
```
|
||||
|
||||
#### 技巧3:开发辅助
|
||||
|
||||
```csharp
|
||||
[DebugButton("开启所有调试选项")]
|
||||
private static void EnableAllDebug()
|
||||
{
|
||||
GameConfig.ShowFPS = true;
|
||||
GameConfig.GodMode = true;
|
||||
GameConfig.UnlimitedEnergy = true;
|
||||
Debug.Log("所有调试选项已开启");
|
||||
}
|
||||
```
|
||||
|
||||
### 6. 实战示例
|
||||
|
||||
```csharp
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using TMPro;
|
||||
|
||||
public static class SampleDebugFunctions
|
||||
{
|
||||
#region 玩家数据
|
||||
[DebugButton("重置游戏", 0.9f, 0.3f, 0.3f)]
|
||||
private static void ResetGame()
|
||||
{
|
||||
PlayerPrefs.DeleteAll();
|
||||
SceneManager.LoadScene(0);
|
||||
Debug.Log("游戏已重置");
|
||||
}
|
||||
|
||||
[DebugButton("满级满资源", 0.2f, 0.8f, 0.2f)]
|
||||
private static void MaxEverything()
|
||||
{
|
||||
PlayerData.Level = 99;
|
||||
PlayerData.Coins = 999999;
|
||||
PlayerData.Gems = 99999;
|
||||
Debug.Log("已设置为满级满资源");
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 关卡测试
|
||||
[DebugButton("跳关", 0.3f, 0.6f, 0.9f)]
|
||||
private static void JumpToLevel()
|
||||
{
|
||||
UniversalDebugTool.ShowInputDialog(
|
||||
"输入关卡号",
|
||||
text =>
|
||||
{
|
||||
if (int.TryParse(text, out int level))
|
||||
{
|
||||
LevelManager.LoadLevel(level);
|
||||
}
|
||||
},
|
||||
"1",
|
||||
TMP_InputField.ContentType.IntegerNumber
|
||||
);
|
||||
}
|
||||
|
||||
[DebugButton("解锁所有关卡", 0.2f, 0.8f, 0.8f)]
|
||||
private static void UnlockAllLevels()
|
||||
{
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
LevelManager.UnlockLevel(i);
|
||||
}
|
||||
Debug.Log("已解锁所有关卡");
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 系统测试
|
||||
[DebugButton("清理未使用资源", 0.9f, 0.5f, 0.3f)]
|
||||
private static void UnloadUnusedAssets()
|
||||
{
|
||||
Resources.UnloadUnusedAssets();
|
||||
Debug.Log("已清理未使用的资源");
|
||||
}
|
||||
|
||||
[DebugButton("触发GC")]
|
||||
private static void ForceGC()
|
||||
{
|
||||
System.GC.Collect();
|
||||
Debug.Log("已触发垃圾回收");
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ❓ 常见问题
|
||||
|
||||
### Q1: 字体显示方块/乱码怎么办?
|
||||
**A:** 调用 `UniversalDebugTool.SetSDFFont()` 设置支持中文的SDF字体。
|
||||
|
||||
### Q2: 自定义按钮不显示?
|
||||
**A:** 检查:
|
||||
- 方法是否为 `static`
|
||||
- 是否添加了 `[DebugButton]` 特性
|
||||
- 是否在 `#if MEOWMENT_DEBUG_TOOL` 内
|
||||
|
||||
### Q3: 第二次点击"暂时隐藏"无效?
|
||||
**A:** 等待上一次隐藏结束,或查看控制台是否有"已经在隐藏状态中"的警告。
|
||||
|
||||
### Q4: 如何在正式版移除调试工具?
|
||||
**A:** 删除Packages目录下的工具包文件夹即可,`#if MEOWMENT_DEBUG_TOOL` 包裹的代码会自动失效。
|
||||
|
||||
### Q5: 控制台日志太多怎么办?
|
||||
**A:**
|
||||
- 点击"清空"按钮清除日志
|
||||
- 使用过滤器只显示Error/Warning
|
||||
- 工具默认只保留最新100条日志
|
||||
|
||||
### Q6: 可以在多个场景使用吗?
|
||||
**A:** 可以,工具使用DontDestroyOnLoad,场景切换不会销毁。
|
||||
|
||||
---
|
||||
|
||||
## 📝 更新日志
|
||||
|
||||
### [0.3.0] - 2025-12-22
|
||||
- ✨ 新增控制台模块,实时显示Unity日志
|
||||
- ✨ 新增暂时隐藏功能,方便截图
|
||||
- 🔨 重构代码架构,模块化设计
|
||||
- 🐛 修复多个UI布局问题
|
||||
|
||||
### [0.2.2] - 2025-12-19
|
||||
- 🔨 默认显示浮窗而非主窗口
|
||||
- 🐛 优化初始化逻辑
|
||||
|
||||
详见 [CHANGELOG.md](CHANGELOG.md)
|
||||
|
||||
---
|
||||
|
||||
## 📦 依赖
|
||||
|
||||
- Unity 2021.3+
|
||||
- TextMesh Pro (com.unity.textmeshpro)
|
||||
- Unity UI (com.unity.ugui)
|
||||
|
||||
---
|
||||
|
||||
## 📧 联系方式
|
||||
|
||||
如有问题或建议,请联系开发团队。
|
||||
|
||||
---
|
||||
|
||||
**祝调试顺利!** 🐱
|
||||
|
||||
@ -53,7 +53,7 @@ namespace MeowmentDebugTool
|
||||
|
||||
public string GetModuleName()
|
||||
{
|
||||
return "自定义按钮";
|
||||
return "按钮";
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
@ -0,0 +1,187 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using TMPro;
|
||||
|
||||
namespace MeowmentDebugTool
|
||||
{
|
||||
/// <summary>
|
||||
/// 自定义复选框模块 - 通过反射加载标记为DebugCheckBox的方法
|
||||
/// </summary>
|
||||
public class CustomCheckBoxesModule : IDebugModule
|
||||
{
|
||||
#region 字段
|
||||
private GameObject customCheckBoxesPage;
|
||||
private RectTransform checkBoxContainer;
|
||||
private GameObject checkBoxPrefab;
|
||||
private ScrollRect checkBoxesScrollRect;
|
||||
|
||||
// 保存的SDF字体资源
|
||||
private TMP_FontAsset savedFontAsset = null;
|
||||
|
||||
// 保存每个复选框的状态
|
||||
private Dictionary<string, bool> checkBoxStates = new Dictionary<string, bool>();
|
||||
#endregion
|
||||
|
||||
#region 构造函数
|
||||
public CustomCheckBoxesModule(GameObject page, RectTransform container, GameObject prefab,
|
||||
ScrollRect scrollRect)
|
||||
{
|
||||
customCheckBoxesPage = page;
|
||||
checkBoxContainer = container;
|
||||
checkBoxPrefab = prefab;
|
||||
checkBoxesScrollRect = scrollRect;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IDebugModule 实现
|
||||
public void Initialize()
|
||||
{
|
||||
Debug.Log("[CustomCheckBoxesModule] 初始化自定义复选框模块...");
|
||||
LoadCustomCheckBoxes();
|
||||
}
|
||||
|
||||
public GameObject GetPage()
|
||||
{
|
||||
return customCheckBoxesPage;
|
||||
}
|
||||
|
||||
public string GetModuleName()
|
||||
{
|
||||
return "开关";
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 公共方法
|
||||
/// <summary>
|
||||
/// 设置SDF字体
|
||||
/// </summary>
|
||||
public void SetSDFFont(TMP_FontAsset fontAsset)
|
||||
{
|
||||
savedFontAsset = fontAsset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重新加载自定义复选框
|
||||
/// </summary>
|
||||
public void ReloadCustomCheckBoxes()
|
||||
{
|
||||
LoadCustomCheckBoxes();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 私有方法
|
||||
/// <summary>
|
||||
/// 加载所有自定义复选框(使用反射)
|
||||
/// </summary>
|
||||
private void LoadCustomCheckBoxes()
|
||||
{
|
||||
if (checkBoxContainer == null || checkBoxPrefab == null)
|
||||
{
|
||||
Debug.LogWarning("[CustomCheckBoxesModule] 复选框容器或复选框预制件未设置");
|
||||
return;
|
||||
}
|
||||
|
||||
// 清空现有复选框
|
||||
foreach (Transform child in checkBoxContainer)
|
||||
{
|
||||
UnityEngine.Object.Destroy(child.gameObject);
|
||||
}
|
||||
|
||||
// 查找所有标记为DebugCheckBox的方法
|
||||
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
|
||||
foreach (var assembly in assemblies)
|
||||
{
|
||||
try
|
||||
{
|
||||
var types = assembly.GetTypes();
|
||||
foreach (var type in types)
|
||||
{
|
||||
var methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
foreach (var method in methods)
|
||||
{
|
||||
var attribute = method.GetCustomAttribute<DebugCheckBoxAttribute>();
|
||||
if (attribute != null)
|
||||
{
|
||||
CreateCustomCheckBox(method, attribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// 某些程序集可能无法访问,跳过
|
||||
Debug.LogWarning($"[CustomCheckBoxesModule] 无法访问程序集 {assembly.FullName}: {e.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateCustomCheckBox(MethodInfo method, DebugCheckBoxAttribute attribute)
|
||||
{
|
||||
// 验证方法签名:必须接受一个 bool 参数
|
||||
var parameters = method.GetParameters();
|
||||
if (parameters.Length != 1 || parameters[0].ParameterType != typeof(bool))
|
||||
{
|
||||
Debug.LogWarning($"[CustomCheckBoxesModule] 方法 {method.Name} 必须接受一个 bool 参数");
|
||||
return;
|
||||
}
|
||||
|
||||
GameObject checkBoxObj = UnityEngine.Object.Instantiate(checkBoxPrefab, checkBoxContainer);
|
||||
Toggle toggle = checkBoxObj.GetComponent<Toggle>();
|
||||
TMP_Text labelText = checkBoxObj.GetComponentInChildren<TMP_Text>();
|
||||
Image backgroundImage = checkBoxObj.GetComponent<Image>();
|
||||
|
||||
// 设置复选框文本
|
||||
if (labelText != null)
|
||||
{
|
||||
labelText.text = string.IsNullOrEmpty(attribute.DisplayName) ? method.Name : attribute.DisplayName;
|
||||
|
||||
// 应用保存的字体
|
||||
if (savedFontAsset != null)
|
||||
{
|
||||
labelText.font = savedFontAsset;
|
||||
}
|
||||
}
|
||||
|
||||
// 设置背景颜色
|
||||
if (backgroundImage != null && attribute.CheckBoxColor != default(Color))
|
||||
{
|
||||
backgroundImage.color = attribute.CheckBoxColor;
|
||||
}
|
||||
|
||||
// 获取或初始化状态(默认为 false)
|
||||
string methodKey = $"{method.DeclaringType?.FullName}.{method.Name}";
|
||||
if (!checkBoxStates.ContainsKey(methodKey))
|
||||
{
|
||||
checkBoxStates[methodKey] = false;
|
||||
}
|
||||
|
||||
// 设置初始状态
|
||||
if (toggle != null)
|
||||
{
|
||||
toggle.isOn = checkBoxStates[methodKey];
|
||||
}
|
||||
|
||||
// 设置复选框状态改变事件
|
||||
toggle.onValueChanged.AddListener((bool isOn) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// 保存状态
|
||||
checkBoxStates[methodKey] = isOn;
|
||||
|
||||
// 调用方法,传递状态
|
||||
method.Invoke(null, new object[] { isOn });
|
||||
Debug.Log($"[CustomCheckBoxesModule] 执行调试方法: {method.Name}, 状态: {isOn}");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"[CustomCheckBoxesModule] 执行调试方法 {method.Name} 时出错: {e.Message}");
|
||||
}
|
||||
});
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e8d7e036c6d4b2d4083addffa8c87eb8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -0,0 +1,274 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using TMPro;
|
||||
|
||||
namespace MeowmentDebugTool
|
||||
{
|
||||
/// <summary>
|
||||
/// 自定义数值模块 - 通过反射加载标记为DebugValue的方法
|
||||
/// </summary>
|
||||
public class CustomValuesModule : IDebugModule
|
||||
{
|
||||
#region 字段
|
||||
private GameObject customValuesPage;
|
||||
private RectTransform valueContainer;
|
||||
private GameObject valuePrefab;
|
||||
private ScrollRect valuesScrollRect;
|
||||
|
||||
// 保存的SDF字体资源
|
||||
private TMP_FontAsset savedFontAsset = null;
|
||||
|
||||
// 保存每个数值的当前值
|
||||
private Dictionary<string, int> valueStates = new Dictionary<string, int>();
|
||||
|
||||
// 关闭窗口回调
|
||||
private Action onCloseWindowCallback;
|
||||
#endregion
|
||||
|
||||
#region 构造函数
|
||||
public CustomValuesModule(GameObject page, RectTransform container, GameObject prefab,
|
||||
ScrollRect scrollRect, Action closeWindowCallback)
|
||||
{
|
||||
customValuesPage = page;
|
||||
valueContainer = container;
|
||||
valuePrefab = prefab;
|
||||
valuesScrollRect = scrollRect;
|
||||
onCloseWindowCallback = closeWindowCallback;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IDebugModule 实现
|
||||
public void Initialize()
|
||||
{
|
||||
Debug.Log("[CustomValuesModule] 初始化自定义数值模块...");
|
||||
LoadCustomValues();
|
||||
}
|
||||
|
||||
public GameObject GetPage()
|
||||
{
|
||||
return customValuesPage;
|
||||
}
|
||||
|
||||
public string GetModuleName()
|
||||
{
|
||||
return "数值";
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 公共方法
|
||||
/// <summary>
|
||||
/// 设置SDF字体
|
||||
/// </summary>
|
||||
public void SetSDFFont(TMP_FontAsset fontAsset)
|
||||
{
|
||||
savedFontAsset = fontAsset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重新加载自定义数值
|
||||
/// </summary>
|
||||
public void ReloadCustomValues()
|
||||
{
|
||||
LoadCustomValues();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 私有方法
|
||||
/// <summary>
|
||||
/// 加载所有自定义数值(使用反射)
|
||||
/// </summary>
|
||||
private void LoadCustomValues()
|
||||
{
|
||||
if (valueContainer == null || valuePrefab == null)
|
||||
{
|
||||
Debug.LogWarning("[CustomValuesModule] 数值容器或数值预制件未设置");
|
||||
return;
|
||||
}
|
||||
|
||||
// 清空现有数值
|
||||
foreach (Transform child in valueContainer)
|
||||
{
|
||||
UnityEngine.Object.Destroy(child.gameObject);
|
||||
}
|
||||
|
||||
// 查找所有标记为DebugValue的方法
|
||||
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
|
||||
foreach (var assembly in assemblies)
|
||||
{
|
||||
try
|
||||
{
|
||||
var types = assembly.GetTypes();
|
||||
foreach (var type in types)
|
||||
{
|
||||
var methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
foreach (var method in methods)
|
||||
{
|
||||
var attribute = method.GetCustomAttribute<DebugValueAttribute>();
|
||||
if (attribute != null)
|
||||
{
|
||||
CreateCustomValue(method, attribute);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// 某些程序集可能无法访问,跳过
|
||||
Debug.LogWarning($"[CustomValuesModule] 无法访问程序集 {assembly.FullName}: {e.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateCustomValue(MethodInfo method, DebugValueAttribute attribute)
|
||||
{
|
||||
// 验证方法签名:必须接受一个 int 参数
|
||||
var parameters = method.GetParameters();
|
||||
if (parameters.Length != 1 || parameters[0].ParameterType != typeof(int))
|
||||
{
|
||||
Debug.LogWarning($"[CustomValuesModule] 方法 {method.Name} 必须接受一个 int 参数");
|
||||
return;
|
||||
}
|
||||
|
||||
GameObject valueObj = UnityEngine.Object.Instantiate(valuePrefab, valueContainer);
|
||||
|
||||
// 获取组件
|
||||
TMP_Text labelText = valueObj.transform.Find("Label")?.GetComponent<TMP_Text>();
|
||||
Slider slider = valueObj.transform.Find("Slider")?.GetComponent<Slider>();
|
||||
TMP_InputField inputField = valueObj.transform.Find("InputField")?.GetComponent<TMP_InputField>();
|
||||
Button confirmButton = valueObj.transform.Find("ConfirmButton")?.GetComponent<Button>();
|
||||
Image backgroundImage = valueObj.GetComponent<Image>();
|
||||
|
||||
// 设置标签文本
|
||||
if (labelText != null)
|
||||
{
|
||||
labelText.text = string.IsNullOrEmpty(attribute.DisplayName) ? method.Name : attribute.DisplayName;
|
||||
|
||||
// 应用保存的字体
|
||||
if (savedFontAsset != null)
|
||||
{
|
||||
labelText.font = savedFontAsset;
|
||||
}
|
||||
}
|
||||
|
||||
// 设置背景颜色
|
||||
if (backgroundImage != null && attribute.ValueColor != default(Color))
|
||||
{
|
||||
backgroundImage.color = attribute.ValueColor;
|
||||
}
|
||||
|
||||
// 获取或初始化状态(使用默认值)
|
||||
string methodKey = $"{method.DeclaringType?.FullName}.{method.Name}";
|
||||
if (!valueStates.ContainsKey(methodKey))
|
||||
{
|
||||
valueStates[methodKey] = attribute.DefaultValue;
|
||||
}
|
||||
|
||||
int currentValue = valueStates[methodKey];
|
||||
|
||||
// 设置滑动条
|
||||
if (slider != null)
|
||||
{
|
||||
slider.minValue = attribute.MinValue;
|
||||
slider.maxValue = attribute.MaxValue;
|
||||
slider.wholeNumbers = true;
|
||||
slider.value = currentValue;
|
||||
|
||||
// 滑动条值改变时更新输入框和保存状态
|
||||
slider.onValueChanged.AddListener((float value) =>
|
||||
{
|
||||
int intValue = Mathf.RoundToInt(value);
|
||||
if (inputField != null)
|
||||
{
|
||||
inputField.text = intValue.ToString();
|
||||
}
|
||||
valueStates[methodKey] = intValue;
|
||||
});
|
||||
}
|
||||
|
||||
// 设置输入框
|
||||
if (inputField != null)
|
||||
{
|
||||
inputField.text = currentValue.ToString();
|
||||
inputField.contentType = TMP_InputField.ContentType.IntegerNumber;
|
||||
|
||||
// 应用保存的字体
|
||||
if (savedFontAsset != null)
|
||||
{
|
||||
inputField.textComponent.font = savedFontAsset;
|
||||
}
|
||||
|
||||
// 输入框值改变时更新滑动条和保存状态
|
||||
inputField.onValueChanged.AddListener((string text) =>
|
||||
{
|
||||
if (int.TryParse(text, out int intValue))
|
||||
{
|
||||
// 限制范围
|
||||
intValue = Mathf.Clamp(intValue, attribute.MinValue, attribute.MaxValue);
|
||||
|
||||
if (slider != null)
|
||||
{
|
||||
slider.value = intValue;
|
||||
}
|
||||
valueStates[methodKey] = intValue;
|
||||
}
|
||||
});
|
||||
|
||||
// 输入框失去焦点时确保值在范围内
|
||||
inputField.onEndEdit.AddListener((string text) =>
|
||||
{
|
||||
if (int.TryParse(text, out int intValue))
|
||||
{
|
||||
intValue = Mathf.Clamp(intValue, attribute.MinValue, attribute.MaxValue);
|
||||
inputField.text = intValue.ToString();
|
||||
valueStates[methodKey] = intValue;
|
||||
|
||||
if (slider != null)
|
||||
{
|
||||
slider.value = intValue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果输入无效,恢复为当前保存的值
|
||||
inputField.text = valueStates[methodKey].ToString();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 设置确认按钮
|
||||
if (confirmButton != null)
|
||||
{
|
||||
// 应用保存的字体到按钮文本
|
||||
if (savedFontAsset != null)
|
||||
{
|
||||
TMP_Text btnText = confirmButton.GetComponentInChildren<TMP_Text>();
|
||||
if (btnText != null)
|
||||
{
|
||||
btnText.font = savedFontAsset;
|
||||
}
|
||||
}
|
||||
|
||||
confirmButton.onClick.AddListener(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
int finalValue = valueStates[methodKey];
|
||||
|
||||
// 调用方法,传递数值
|
||||
method.Invoke(null, new object[] { finalValue });
|
||||
Debug.Log($"[CustomValuesModule] 执行调试方法: {method.Name}, 数值: {finalValue}");
|
||||
// 点击确认按钮后关闭主窗口
|
||||
onCloseWindowCallback?.Invoke(); }
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"[CustomValuesModule] 执行调试方法 {method.Name} 时出错: {e.Message}");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6e9a59724c256d243a97df1857fdc89e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -51,7 +51,8 @@ namespace MeowmentDebugTool
|
||||
|
||||
GameObject parametersPage = CreateParametersPage(contentContainer.transform);
|
||||
GameObject customButtonsPage = CreateCustomButtonsPage(contentContainer.transform);
|
||||
GameObject toolbarPage = CreateToolbarPage(contentContainer.transform);
|
||||
GameObject customCheckBoxesPage = CreateCustomCheckBoxesPage(contentContainer.transform);
|
||||
GameObject customValuesPage = CreateCustomValuesPage(contentContainer.transform);
|
||||
GameObject settingsPage = CreateSettingsPage(contentContainer.transform);
|
||||
GameObject consolePage = CreateConsolePage(contentContainer.transform);
|
||||
|
||||
@ -60,7 +61,7 @@ namespace MeowmentDebugTool
|
||||
|
||||
// 设置引用
|
||||
SetupReferences(tool, canvas, mainWindow, floatingButton,
|
||||
parametersPage, customButtonsPage, toolbarPage, settingsPage, consolePage, inputDialog);
|
||||
parametersPage, customButtonsPage, customCheckBoxesPage, customValuesPage, settingsPage, consolePage, inputDialog);
|
||||
|
||||
return tool;
|
||||
}
|
||||
@ -253,6 +254,92 @@ namespace MeowmentDebugTool
|
||||
return page;
|
||||
}
|
||||
|
||||
private static GameObject CreateCustomCheckBoxesPage(Transform parent)
|
||||
{
|
||||
GameObject page = new GameObject("CustomCheckBoxesPage");
|
||||
page.transform.SetParent(parent, false);
|
||||
RectTransform rect = page.AddComponent<RectTransform>();
|
||||
rect.anchorMin = Vector2.zero;
|
||||
rect.anchorMax = Vector2.one;
|
||||
rect.sizeDelta = Vector2.zero;
|
||||
|
||||
ScrollRect scroll = page.AddComponent<ScrollRect>();
|
||||
|
||||
GameObject viewport = new GameObject("Viewport");
|
||||
viewport.transform.SetParent(page.transform, false);
|
||||
RectTransform vpRect = viewport.AddComponent<RectTransform>();
|
||||
vpRect.anchorMin = Vector2.zero;
|
||||
vpRect.anchorMax = Vector2.one;
|
||||
vpRect.sizeDelta = Vector2.zero;
|
||||
viewport.AddComponent<Mask>().showMaskGraphic = false;
|
||||
|
||||
GameObject checkBoxContainer = new GameObject("CheckBoxContainer");
|
||||
checkBoxContainer.transform.SetParent(viewport.transform, false);
|
||||
RectTransform containerRect = checkBoxContainer.AddComponent<RectTransform>();
|
||||
containerRect.anchorMin = new Vector2(0, 1);
|
||||
containerRect.anchorMax = new Vector2(1, 1);
|
||||
containerRect.pivot = new Vector2(0.5f, 1);
|
||||
|
||||
// 使用垂直布局,一行一个复选框
|
||||
VerticalLayoutGroup vertLayout = checkBoxContainer.AddComponent<VerticalLayoutGroup>();
|
||||
vertLayout.spacing = 15;
|
||||
vertLayout.padding = new RectOffset(20, 20, 20, 20);
|
||||
vertLayout.childControlWidth = true;
|
||||
vertLayout.childControlHeight = false;
|
||||
vertLayout.childForceExpandWidth = true;
|
||||
|
||||
ContentSizeFitter fitter = checkBoxContainer.AddComponent<ContentSizeFitter>();
|
||||
fitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||
|
||||
scroll.viewport = vpRect;
|
||||
scroll.content = containerRect;
|
||||
|
||||
return page;
|
||||
}
|
||||
|
||||
private static GameObject CreateCustomValuesPage(Transform parent)
|
||||
{
|
||||
GameObject page = new GameObject("CustomValuesPage");
|
||||
page.transform.SetParent(parent, false);
|
||||
RectTransform rect = page.AddComponent<RectTransform>();
|
||||
rect.anchorMin = Vector2.zero;
|
||||
rect.anchorMax = Vector2.one;
|
||||
rect.sizeDelta = Vector2.zero;
|
||||
|
||||
ScrollRect scroll = page.AddComponent<ScrollRect>();
|
||||
|
||||
GameObject viewport = new GameObject("Viewport");
|
||||
viewport.transform.SetParent(page.transform, false);
|
||||
RectTransform vpRect = viewport.AddComponent<RectTransform>();
|
||||
vpRect.anchorMin = Vector2.zero;
|
||||
vpRect.anchorMax = Vector2.one;
|
||||
vpRect.sizeDelta = Vector2.zero;
|
||||
viewport.AddComponent<Mask>().showMaskGraphic = false;
|
||||
|
||||
GameObject valueContainer = new GameObject("ValueContainer");
|
||||
valueContainer.transform.SetParent(viewport.transform, false);
|
||||
RectTransform containerRect = valueContainer.AddComponent<RectTransform>();
|
||||
containerRect.anchorMin = new Vector2(0, 1);
|
||||
containerRect.anchorMax = new Vector2(1, 1);
|
||||
containerRect.pivot = new Vector2(0.5f, 1);
|
||||
|
||||
// 使用垂直布局,一行一个数值项
|
||||
VerticalLayoutGroup vertLayout = valueContainer.AddComponent<VerticalLayoutGroup>();
|
||||
vertLayout.spacing = 20;
|
||||
vertLayout.padding = new RectOffset(20, 20, 20, 20);
|
||||
vertLayout.childControlWidth = true;
|
||||
vertLayout.childControlHeight = false;
|
||||
vertLayout.childForceExpandWidth = true;
|
||||
|
||||
ContentSizeFitter fitter = valueContainer.AddComponent<ContentSizeFitter>();
|
||||
fitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||
|
||||
scroll.viewport = vpRect;
|
||||
scroll.content = containerRect;
|
||||
|
||||
return page;
|
||||
}
|
||||
|
||||
private static GameObject CreateToolbarPage(Transform parent)
|
||||
{
|
||||
GameObject page = new GameObject("ToolbarPage");
|
||||
@ -1042,6 +1129,230 @@ namespace MeowmentDebugTool
|
||||
return btn;
|
||||
}
|
||||
|
||||
private static GameObject CreateCustomCheckBoxPrefab()
|
||||
{
|
||||
// 使用同一个临时父对象
|
||||
GameObject tempParent = GameObject.Find("_TempPrefabHolder");
|
||||
if (tempParent == null)
|
||||
{
|
||||
tempParent = new GameObject("_TempPrefabHolder");
|
||||
tempParent.SetActive(false);
|
||||
Object.DontDestroyOnLoad(tempParent);
|
||||
}
|
||||
|
||||
GameObject toggleObj = new GameObject("CustomCheckBox", typeof(RectTransform));
|
||||
toggleObj.transform.SetParent(tempParent.transform, false);
|
||||
RectTransform rect = toggleObj.GetComponent<RectTransform>();
|
||||
rect.sizeDelta = new Vector2(800, 80);
|
||||
|
||||
// 添加背景图片
|
||||
Image bg = toggleObj.AddComponent<Image>();
|
||||
bg.color = new Color(0.15f, 0.15f, 0.15f, 1f);
|
||||
|
||||
// 创建复选框背景(左侧)
|
||||
GameObject checkboxBg = new GameObject("Background");
|
||||
checkboxBg.transform.SetParent(toggleObj.transform, false);
|
||||
RectTransform checkboxBgRect = checkboxBg.AddComponent<RectTransform>();
|
||||
checkboxBgRect.anchorMin = new Vector2(0, 0.5f);
|
||||
checkboxBgRect.anchorMax = new Vector2(0, 0.5f);
|
||||
checkboxBgRect.pivot = new Vector2(0, 0.5f);
|
||||
checkboxBgRect.anchoredPosition = new Vector2(20, 0);
|
||||
checkboxBgRect.sizeDelta = new Vector2(60, 60);
|
||||
|
||||
Image checkboxBgImage = checkboxBg.AddComponent<Image>();
|
||||
checkboxBgImage.color = new Color(0.8f, 0.8f, 0.8f, 1f);
|
||||
|
||||
// 创建勾选标记
|
||||
GameObject checkmark = new GameObject("Checkmark");
|
||||
checkmark.transform.SetParent(checkboxBg.transform, false);
|
||||
RectTransform checkmarkRect = checkmark.AddComponent<RectTransform>();
|
||||
checkmarkRect.anchorMin = Vector2.zero;
|
||||
checkmarkRect.anchorMax = Vector2.one;
|
||||
checkmarkRect.sizeDelta = Vector2.zero;
|
||||
|
||||
Image checkmarkImage = checkmark.AddComponent<Image>();
|
||||
checkmarkImage.color = new Color(0.2f, 0.8f, 0.2f, 1f);
|
||||
|
||||
// 创建文本标签(右侧)
|
||||
GameObject label = new GameObject("Label");
|
||||
label.transform.SetParent(toggleObj.transform, false);
|
||||
RectTransform labelRect = label.AddComponent<RectTransform>();
|
||||
labelRect.anchorMin = new Vector2(0, 0);
|
||||
labelRect.anchorMax = new Vector2(1, 1);
|
||||
labelRect.pivot = new Vector2(0, 0.5f);
|
||||
labelRect.offsetMin = new Vector2(100, 0);
|
||||
labelRect.offsetMax = new Vector2(-20, 0);
|
||||
|
||||
TextMeshProUGUI labelText = label.AddComponent<TextMeshProUGUI>();
|
||||
labelText.text = "复选框";
|
||||
labelText.fontSize = 32;
|
||||
labelText.alignment = TextAlignmentOptions.Left;
|
||||
labelText.color = Color.white;
|
||||
|
||||
// 添加Toggle组件
|
||||
Toggle toggle = toggleObj.AddComponent<Toggle>();
|
||||
toggle.targetGraphic = checkboxBgImage;
|
||||
toggle.graphic = checkmarkImage;
|
||||
toggle.isOn = false;
|
||||
|
||||
return toggleObj;
|
||||
}
|
||||
|
||||
private static GameObject CreateCustomValuePrefab()
|
||||
{
|
||||
// 使用同一个临时父对象
|
||||
GameObject tempParent = GameObject.Find("_TempPrefabHolder");
|
||||
if (tempParent == null)
|
||||
{
|
||||
tempParent = new GameObject("_TempPrefabHolder");
|
||||
tempParent.SetActive(false);
|
||||
Object.DontDestroyOnLoad(tempParent);
|
||||
}
|
||||
|
||||
GameObject valueObj = new GameObject("CustomValue", typeof(RectTransform));
|
||||
valueObj.transform.SetParent(tempParent.transform, false);
|
||||
RectTransform rect = valueObj.GetComponent<RectTransform>();
|
||||
rect.sizeDelta = new Vector2(900, 140);
|
||||
|
||||
// 添加背景图片
|
||||
Image bg = valueObj.AddComponent<Image>();
|
||||
bg.color = new Color(0.15f, 0.15f, 0.15f, 1f);
|
||||
|
||||
// 创建标签文本(顶部独占一行)
|
||||
GameObject label = new GameObject("Label");
|
||||
label.transform.SetParent(valueObj.transform, false);
|
||||
RectTransform labelRect = label.AddComponent<RectTransform>();
|
||||
labelRect.anchorMin = new Vector2(0, 1);
|
||||
labelRect.anchorMax = new Vector2(1, 1);
|
||||
labelRect.pivot = new Vector2(0, 1);
|
||||
labelRect.anchoredPosition = new Vector2(20, -10);
|
||||
labelRect.sizeDelta = new Vector2(-40, 35);
|
||||
|
||||
TextMeshProUGUI labelText = label.AddComponent<TextMeshProUGUI>();
|
||||
labelText.text = "数值名称";
|
||||
labelText.fontSize = 28;
|
||||
labelText.alignment = TextAlignmentOptions.Left;
|
||||
labelText.color = Color.white;
|
||||
|
||||
// 创建滑动条(标题下方,占据左侧60%)
|
||||
GameObject sliderObj = new GameObject("Slider");
|
||||
sliderObj.transform.SetParent(valueObj.transform, false);
|
||||
RectTransform sliderRect = sliderObj.AddComponent<RectTransform>();
|
||||
sliderRect.anchorMin = new Vector2(0, 0);
|
||||
sliderRect.anchorMax = new Vector2(0.55f, 0);
|
||||
sliderRect.pivot = new Vector2(0, 0);
|
||||
sliderRect.anchoredPosition = new Vector2(20, 15);
|
||||
sliderRect.sizeDelta = new Vector2(-20, 50);
|
||||
|
||||
Slider slider = sliderObj.AddComponent<Slider>();
|
||||
|
||||
// 滑动条背景
|
||||
GameObject sliderBg = new GameObject("Background");
|
||||
sliderBg.transform.SetParent(sliderObj.transform, false);
|
||||
RectTransform sliderBgRect = sliderBg.AddComponent<RectTransform>();
|
||||
sliderBgRect.anchorMin = Vector2.zero;
|
||||
sliderBgRect.anchorMax = Vector2.one;
|
||||
sliderBgRect.sizeDelta = Vector2.zero;
|
||||
Image sliderBgImage = sliderBg.AddComponent<Image>();
|
||||
sliderBgImage.color = new Color(0.3f, 0.3f, 0.3f, 1f);
|
||||
|
||||
// 滑动条填充区域
|
||||
GameObject fillArea = new GameObject("Fill Area");
|
||||
fillArea.transform.SetParent(sliderObj.transform, false);
|
||||
RectTransform fillAreaRect = fillArea.AddComponent<RectTransform>();
|
||||
fillAreaRect.anchorMin = Vector2.zero;
|
||||
fillAreaRect.anchorMax = Vector2.one;
|
||||
fillAreaRect.sizeDelta = new Vector2(-10, -10);
|
||||
|
||||
GameObject fill = new GameObject("Fill");
|
||||
fill.transform.SetParent(fillArea.transform, false);
|
||||
RectTransform fillRect = fill.AddComponent<RectTransform>();
|
||||
fillRect.anchorMin = Vector2.zero;
|
||||
fillRect.anchorMax = Vector2.one;
|
||||
fillRect.sizeDelta = Vector2.zero;
|
||||
Image fillImage = fill.AddComponent<Image>();
|
||||
fillImage.color = new Color(0.2f, 0.6f, 1f, 1f);
|
||||
|
||||
// 滑动条手柄区域
|
||||
GameObject handleArea = new GameObject("Handle Slide Area");
|
||||
handleArea.transform.SetParent(sliderObj.transform, false);
|
||||
RectTransform handleAreaRect = handleArea.AddComponent<RectTransform>();
|
||||
handleAreaRect.anchorMin = Vector2.zero;
|
||||
handleAreaRect.anchorMax = Vector2.one;
|
||||
handleAreaRect.sizeDelta = new Vector2(-10, 0);
|
||||
|
||||
GameObject handle = new GameObject("Handle");
|
||||
handle.transform.SetParent(handleArea.transform, false);
|
||||
RectTransform handleRect = handle.AddComponent<RectTransform>();
|
||||
handleRect.sizeDelta = new Vector2(30, 30);
|
||||
Image handleImage = handle.AddComponent<Image>();
|
||||
handleImage.color = Color.white;
|
||||
|
||||
slider.fillRect = fillRect;
|
||||
slider.handleRect = handleRect;
|
||||
slider.targetGraphic = handleImage;
|
||||
|
||||
// 创建输入框(滑动条右侧)
|
||||
GameObject inputFieldObj = new GameObject("InputField");
|
||||
inputFieldObj.transform.SetParent(valueObj.transform, false);
|
||||
RectTransform inputRect = inputFieldObj.AddComponent<RectTransform>();
|
||||
inputRect.anchorMin = new Vector2(0.56f, 0);
|
||||
inputRect.anchorMax = new Vector2(0.73f, 0);
|
||||
inputRect.pivot = new Vector2(0, 0);
|
||||
inputRect.anchoredPosition = new Vector2(5, 15);
|
||||
inputRect.sizeDelta = new Vector2(0, 50);
|
||||
|
||||
Image inputBg = inputFieldObj.AddComponent<Image>();
|
||||
inputBg.color = new Color(0.25f, 0.25f, 0.25f, 1f);
|
||||
|
||||
// 创建输入框的文本子对象
|
||||
GameObject inputTextObj = new GameObject("Text");
|
||||
inputTextObj.transform.SetParent(inputFieldObj.transform, false);
|
||||
RectTransform inputTextRect = inputTextObj.AddComponent<RectTransform>();
|
||||
inputTextRect.anchorMin = Vector2.zero;
|
||||
inputTextRect.anchorMax = Vector2.one;
|
||||
inputTextRect.sizeDelta = Vector2.zero;
|
||||
|
||||
TextMeshProUGUI inputTextComponent = inputTextObj.AddComponent<TextMeshProUGUI>();
|
||||
inputTextComponent.fontSize = 28;
|
||||
inputTextComponent.alignment = TextAlignmentOptions.Center;
|
||||
inputTextComponent.color = Color.white;
|
||||
|
||||
TMP_InputField inputField = inputFieldObj.AddComponent<TMP_InputField>();
|
||||
inputField.textComponent = inputTextComponent;
|
||||
|
||||
// 创建确认按钮(输入框右侧)- 手动创建而不是使用CreateButton
|
||||
GameObject confirmBtn = new GameObject("ConfirmButton", typeof(RectTransform));
|
||||
confirmBtn.transform.SetParent(valueObj.transform, false);
|
||||
RectTransform confirmRect = confirmBtn.GetComponent<RectTransform>();
|
||||
confirmRect.anchorMin = new Vector2(0.74f, 0);
|
||||
confirmRect.anchorMax = new Vector2(1, 0);
|
||||
confirmRect.pivot = new Vector2(0, 0);
|
||||
confirmRect.anchoredPosition = new Vector2(5, 15);
|
||||
confirmRect.sizeDelta = new Vector2(-20, 50);
|
||||
|
||||
Image confirmBtnImage = confirmBtn.AddComponent<Image>();
|
||||
confirmBtnImage.color = new Color(0.2f, 0.8f, 0.2f, 1f);
|
||||
|
||||
Button confirmButton = confirmBtn.AddComponent<Button>();
|
||||
confirmButton.targetGraphic = confirmBtnImage;
|
||||
|
||||
GameObject confirmBtnText = new GameObject("Text");
|
||||
confirmBtnText.transform.SetParent(confirmBtn.transform, false);
|
||||
RectTransform confirmBtnTextRect = confirmBtnText.AddComponent<RectTransform>();
|
||||
confirmBtnTextRect.anchorMin = Vector2.zero;
|
||||
confirmBtnTextRect.anchorMax = Vector2.one;
|
||||
confirmBtnTextRect.sizeDelta = Vector2.zero;
|
||||
|
||||
TextMeshProUGUI confirmText = confirmBtnText.AddComponent<TextMeshProUGUI>();
|
||||
confirmText.text = "确认";
|
||||
confirmText.fontSize = 28;
|
||||
confirmText.alignment = TextAlignmentOptions.Center;
|
||||
confirmText.color = Color.white;
|
||||
|
||||
return valueObj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 确保场景中有EventSystem,如果没有则创建一个
|
||||
/// </summary>
|
||||
@ -1064,7 +1375,7 @@ namespace MeowmentDebugTool
|
||||
|
||||
private static void SetupReferences(UniversalDebugTool tool, Canvas canvas, GameObject mainWindow,
|
||||
GameObject floatingButton, GameObject parametersPage, GameObject customButtonsPage,
|
||||
GameObject toolbarPage, GameObject settingsPage, GameObject consolePage, GameObject inputDialog)
|
||||
GameObject customCheckBoxesPage, GameObject customValuesPage, GameObject settingsPage, GameObject consolePage, GameObject inputDialog)
|
||||
{
|
||||
// 使用反射设置私有字段
|
||||
var type = typeof(UniversalDebugTool);
|
||||
@ -1092,27 +1403,26 @@ namespace MeowmentDebugTool
|
||||
type.GetField("buttonContainer", flags)?.SetValue(tool, customButtonsPage.transform.Find("Viewport/ButtonContainer").GetComponent<RectTransform>());
|
||||
type.GetField("buttonsScrollRect", flags)?.SetValue(tool, customButtonsPage.GetComponent<ScrollRect>());
|
||||
|
||||
// 创建按钮预制件
|
||||
// 自定义复选框页面
|
||||
type.GetField("customCheckBoxesPage", flags)?.SetValue(tool, customCheckBoxesPage);
|
||||
type.GetField("checkBoxContainer", flags)?.SetValue(tool, customCheckBoxesPage.transform.Find("Viewport/CheckBoxContainer").GetComponent<RectTransform>());
|
||||
type.GetField("checkBoxesScrollRect", flags)?.SetValue(tool, customCheckBoxesPage.GetComponent<ScrollRect>());
|
||||
|
||||
// 自定义数值页面
|
||||
type.GetField("customValuesPage", flags)?.SetValue(tool, customValuesPage);
|
||||
type.GetField("valueContainer", flags)?.SetValue(tool, customValuesPage.transform.Find("Viewport/ValueContainer").GetComponent<RectTransform>());
|
||||
type.GetField("valuesScrollRect", flags)?.SetValue(tool, customValuesPage.GetComponent<ScrollRect>());
|
||||
|
||||
// 创建按钮、复选框和数值预制件
|
||||
GameObject buttonPrefab = CreateCustomButtonPrefab();
|
||||
GameObject checkBoxPrefab = CreateCustomCheckBoxPrefab();
|
||||
GameObject valuePrefab = CreateCustomValuePrefab();
|
||||
GameObject tabButtonPrefab = CreateTabButtonPrefab();
|
||||
type.GetField("buttonPrefab", flags)?.SetValue(tool, buttonPrefab);
|
||||
type.GetField("checkBoxPrefab", flags)?.SetValue(tool, checkBoxPrefab);
|
||||
type.GetField("valuePrefab", flags)?.SetValue(tool, valuePrefab);
|
||||
type.GetField("tabButtonPrefab", flags)?.SetValue(tool, tabButtonPrefab);
|
||||
|
||||
// 工具栏页面
|
||||
type.GetField("toolbarPage", flags)?.SetValue(tool, toolbarPage);
|
||||
Transform toolbarPanel = toolbarPage.transform.Find("TimeAdjustPanel");
|
||||
if (toolbarPanel != null)
|
||||
{
|
||||
type.GetField("timeAdjustSlider", flags)?.SetValue(tool, toolbarPanel.Find("TimeAdjustSlider")?.GetComponent<Slider>());
|
||||
type.GetField("timeAdjustValueText", flags)?.SetValue(tool, toolbarPanel.Find("TimeAdjustValueText")?.GetComponent<TextMeshProUGUI>());
|
||||
Transform btnContainer = toolbarPanel.Find("ButtonContainer");
|
||||
if (btnContainer != null)
|
||||
{
|
||||
type.GetField("timeIncreaseButton", flags)?.SetValue(tool, btnContainer.Find("TimeIncreaseButton")?.GetComponent<Button>());
|
||||
type.GetField("timeDecreaseButton", flags)?.SetValue(tool, btnContainer.Find("TimeDecreaseButton")?.GetComponent<Button>());
|
||||
}
|
||||
}
|
||||
|
||||
// 设置页面
|
||||
type.GetField("settingsPage", flags)?.SetValue(tool, settingsPage);
|
||||
Transform resPanel = settingsPage.transform.Find("ResolutionPanel");
|
||||
|
||||
@ -47,12 +47,17 @@ namespace MeowmentDebugTool
|
||||
[SerializeField] private GameObject buttonPrefab;
|
||||
[SerializeField] private ScrollRect buttonsScrollRect;
|
||||
|
||||
[Header("工具栏页面")]
|
||||
[SerializeField] private GameObject toolbarPage;
|
||||
[SerializeField] private Slider timeAdjustSlider;
|
||||
[SerializeField] private TMP_Text timeAdjustValueText;
|
||||
[SerializeField] private Button timeIncreaseButton;
|
||||
[SerializeField] private Button timeDecreaseButton;
|
||||
[Header("自定义复选框页面")]
|
||||
[SerializeField] private GameObject customCheckBoxesPage;
|
||||
[SerializeField] private RectTransform checkBoxContainer;
|
||||
[SerializeField] private GameObject checkBoxPrefab;
|
||||
[SerializeField] private ScrollRect checkBoxesScrollRect;
|
||||
|
||||
[Header("数值页面")]
|
||||
[SerializeField] private GameObject customValuesPage;
|
||||
[SerializeField] private RectTransform valueContainer;
|
||||
[SerializeField] private GameObject valuePrefab;
|
||||
[SerializeField] private ScrollRect valuesScrollRect;
|
||||
|
||||
[Header("设置页面")]
|
||||
[SerializeField] private GameObject settingsPage;
|
||||
@ -100,13 +105,22 @@ namespace MeowmentDebugTool
|
||||
// 模块实例
|
||||
private ParametersModule parametersModule;
|
||||
private CustomButtonsModule customButtonsModule;
|
||||
private ToolbarModule toolbarModule;
|
||||
private CustomCheckBoxesModule customCheckBoxesModule;
|
||||
private CustomValuesModule customValuesModule;
|
||||
private SettingsModule settingsModule;
|
||||
private ConsoleModule consoleModule;
|
||||
private List<IDebugModule> allModules = new List<IDebugModule>();
|
||||
|
||||
// 暂时隐藏状态标志
|
||||
private bool isHiding = false;
|
||||
|
||||
// 四指点击检测
|
||||
private bool wasFourFingerTouch = false;
|
||||
private const float fourFingerTapTime = 0.3f; // 四指同时按下的时间阈值
|
||||
private float fourFingerTouchStartTime = 0f;
|
||||
|
||||
// 键盘测试模式(用于测试,可以用F键代替四指点击)
|
||||
private static bool useKeyboardTestMode = false; // 默认开启键盘测试模式 f
|
||||
#endregion
|
||||
|
||||
#region 单例
|
||||
@ -167,6 +181,22 @@ namespace MeowmentDebugTool
|
||||
|
||||
private void Update()
|
||||
{
|
||||
// 只有初始化后才执行任何逻辑
|
||||
if (!isInitialized)
|
||||
return;
|
||||
|
||||
// 键盘测试模式:按F键触发
|
||||
if (useKeyboardTestMode && Input.GetKeyDown(KeyCode.F))
|
||||
{
|
||||
OnFourFingerTap();
|
||||
}
|
||||
|
||||
// 检测四指点击(仅在非键盘测试模式下)
|
||||
if (!useKeyboardTestMode)
|
||||
{
|
||||
DetectFourFingerTap();
|
||||
}
|
||||
|
||||
// 更新控制台模块
|
||||
if (consoleModule != null)
|
||||
{
|
||||
@ -276,6 +306,22 @@ namespace MeowmentDebugTool
|
||||
Instance.customButtonsModule.ReloadCustomButtons();
|
||||
}
|
||||
|
||||
// 4.5. 通过模块重新加载自定义复选框
|
||||
if (Instance.customCheckBoxesModule != null)
|
||||
{
|
||||
Debug.Log("[MeowmentDebugTool] 重新加载自定义复选框以应用新字体...");
|
||||
Instance.customCheckBoxesModule.SetSDFFont(fontAsset);
|
||||
Instance.customCheckBoxesModule.ReloadCustomCheckBoxes();
|
||||
}
|
||||
|
||||
// 4.6. 通过模块重新加载数值
|
||||
if (Instance.customValuesModule != null)
|
||||
{
|
||||
Debug.Log("[MeowmentDebugTool] 重新加载数值以应用新字体...");
|
||||
Instance.customValuesModule.SetSDFFont(fontAsset);
|
||||
Instance.customValuesModule.ReloadCustomValues();
|
||||
}
|
||||
|
||||
// 5. 为控制台模块应用字体
|
||||
if (Instance.consoleModule != null)
|
||||
{
|
||||
@ -318,9 +364,11 @@ namespace MeowmentDebugTool
|
||||
customButtonsModule = new CustomButtonsModule(
|
||||
customButtonsPage, buttonContainer, buttonPrefab, buttonsScrollRect, CloseDebugWindow);
|
||||
|
||||
toolbarModule = new ToolbarModule(
|
||||
toolbarPage, timeAdjustSlider, timeAdjustValueText,
|
||||
timeIncreaseButton, timeDecreaseButton);
|
||||
customCheckBoxesModule = new CustomCheckBoxesModule(
|
||||
customCheckBoxesPage, checkBoxContainer, checkBoxPrefab, checkBoxesScrollRect);
|
||||
|
||||
customValuesModule = new CustomValuesModule(
|
||||
customValuesPage, valueContainer, valuePrefab, valuesScrollRect, CloseDebugWindow);
|
||||
|
||||
settingsModule = new SettingsModule(
|
||||
settingsPage, widthInputField, heightInputField,
|
||||
@ -338,7 +386,8 @@ namespace MeowmentDebugTool
|
||||
allModules.Add(consoleModule);
|
||||
allModules.Add(parametersModule);
|
||||
allModules.Add(customButtonsModule);
|
||||
allModules.Add(toolbarModule);
|
||||
allModules.Add(customCheckBoxesModule);
|
||||
allModules.Add(customValuesModule);
|
||||
allModules.Add(settingsModule);
|
||||
|
||||
// 注册所有页面
|
||||
@ -575,6 +624,24 @@ namespace MeowmentDebugTool
|
||||
// #endregion
|
||||
|
||||
#region 公共API
|
||||
/// <summary>
|
||||
/// 设置键盘测试模式(用F键代替四指点击)
|
||||
/// </summary>
|
||||
/// <param name="enable">true=使用F键测试, false=使用四指点击</param>
|
||||
public static void SetKeyboardTestMode(bool enable)
|
||||
{
|
||||
useKeyboardTestMode = enable;
|
||||
Debug.Log($"[MeowmentDebugTool] 键盘测试模式: {(enable ? "开启 (按F键触发)" : "关闭 (使用四指点击)")})");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前是否为键盘测试模式
|
||||
/// </summary>
|
||||
public static bool IsKeyboardTestMode()
|
||||
{
|
||||
return useKeyboardTestMode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 显示调试工具
|
||||
/// </summary>
|
||||
@ -745,6 +812,93 @@ namespace MeowmentDebugTool
|
||||
Debug.Log("[MeowmentDebugTool] 暂时隐藏流程结束");
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 四指点击检测
|
||||
/// <summary>
|
||||
/// 检测四指点击屏幕
|
||||
/// </summary>
|
||||
private void DetectFourFingerTap()
|
||||
{
|
||||
// 只在移动设备或Unity编辑器(模拟触摸)上检测
|
||||
#if UNITY_ANDROID || UNITY_IOS || UNITY_EDITOR
|
||||
|
||||
// 检测四指同时按下
|
||||
if (Input.touchCount == 4)
|
||||
{
|
||||
// 检查是否所有触摸都刚开始
|
||||
bool allBegan = true;
|
||||
foreach (Touch touch in Input.touches)
|
||||
{
|
||||
if (touch.phase != TouchPhase.Began)
|
||||
{
|
||||
allBegan = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (allBegan && !wasFourFingerTouch)
|
||||
{
|
||||
wasFourFingerTouch = true;
|
||||
fourFingerTouchStartTime = Time.time;
|
||||
}
|
||||
}
|
||||
|
||||
// 检测四指抬起(点击完成)
|
||||
if (wasFourFingerTouch && Input.touchCount == 0)
|
||||
{
|
||||
float touchDuration = Time.time - fourFingerTouchStartTime;
|
||||
|
||||
// 如果触摸时间小于阈值,认为是点击(而非长按)
|
||||
if (touchDuration < fourFingerTapTime)
|
||||
{
|
||||
OnFourFingerTap();
|
||||
}
|
||||
|
||||
wasFourFingerTouch = false;
|
||||
}
|
||||
|
||||
// 如果触摸数量变化,重置状态
|
||||
if (wasFourFingerTouch && Input.touchCount != 4)
|
||||
{
|
||||
wasFourFingerTouch = false;
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 四指点击触发的事件 - 切换所有UI的显示/隐藏(包括主窗口和浮窗)
|
||||
/// </summary>
|
||||
private void OnFourFingerTap()
|
||||
{
|
||||
Debug.Log("[MeowmentDebugTool] 检测到四指点击!");
|
||||
|
||||
// 检查主窗口或浮窗是否有任意一个显示
|
||||
bool anyUIVisible = (mainWindow != null && mainWindow.gameObject.activeSelf) ||
|
||||
(floatingButton != null && floatingButton.activeSelf);
|
||||
|
||||
if (anyUIVisible)
|
||||
{
|
||||
// 隐藏所有UI
|
||||
if (mainWindow != null)
|
||||
mainWindow.gameObject.SetActive(false);
|
||||
if (floatingButton != null)
|
||||
floatingButton.SetActive(false);
|
||||
|
||||
Debug.Log("[MeowmentDebugTool] 所有UI已隐藏");
|
||||
}
|
||||
else
|
||||
{
|
||||
// 显示浮窗
|
||||
if (mainWindow != null)
|
||||
mainWindow.gameObject.SetActive(false);
|
||||
if (floatingButton != null)
|
||||
floatingButton.SetActive(true);
|
||||
|
||||
Debug.Log("[MeowmentDebugTool] 已显示浮窗");
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
#region 自定义特性
|
||||
@ -777,5 +931,77 @@ namespace MeowmentDebugTool
|
||||
ButtonColor = new Color(r, g, b, 1f);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 调试复选框特性 - 标记方法为调试复选框开关
|
||||
///
|
||||
/// 使用说明:
|
||||
/// 1. 在主项目中使用此特性时,请用条件编译包裹:
|
||||
///
|
||||
/// #if MEOWMENT_DEBUG_TOOL
|
||||
/// [DebugCheckBox("开关名称")]
|
||||
/// public static void MyToggleMethod(bool isOn)
|
||||
/// {
|
||||
/// Debug.Log($"开关状态: {isOn}");
|
||||
/// // 在这里设置某些参数的值
|
||||
/// }
|
||||
/// #endif
|
||||
///
|
||||
/// 2. 方法必须接受一个 bool 参数,表示复选框的状态
|
||||
/// 3. 这样当包被卸载时,代码不会报错,也不会影响主工程的正常运行
|
||||
/// 4. MEOWMENT_DEBUG_TOOL 宏会在包安装时自动定义,卸载时自动移除
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
||||
public class DebugCheckBoxAttribute : Attribute
|
||||
{
|
||||
public string DisplayName { get; set; }
|
||||
public Color CheckBoxColor { get; set; }
|
||||
|
||||
public DebugCheckBoxAttribute(string displayName = "", float r = 0.8f, float g = 0.8f, float b = 0.8f)
|
||||
{
|
||||
DisplayName = displayName;
|
||||
CheckBoxColor = new Color(r, g, b, 1f);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 调试数值特性 - 标记方法为调试数值调整器
|
||||
///
|
||||
/// 使用说明:
|
||||
/// 1. 在主项目中使用此特性时,请用条件编译包裹:
|
||||
///
|
||||
/// #if MEOWMENT_DEBUG_TOOL
|
||||
/// [DebugValue("数值名称", 最小值, 最大值)]
|
||||
/// public static void MyValueMethod(int value)
|
||||
/// {
|
||||
/// Debug.Log($"设置数值: {value}");
|
||||
/// // 在这里使用value来设置游戏参数
|
||||
/// }
|
||||
/// #endif
|
||||
///
|
||||
/// 2. 方法必须接受一个 int 参数,表示调整后的数值
|
||||
/// 3. 可以指定最小值和最大值来限制范围
|
||||
/// 4. 可以设置自定义颜色
|
||||
/// 5. MEOWMENT_DEBUG_TOOL 宏会在包安装时自动定义,卸载时自动移除
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
||||
public class DebugValueAttribute : Attribute
|
||||
{
|
||||
public string DisplayName { get; set; }
|
||||
public Color ValueColor { get; set; }
|
||||
public int MinValue { get; set; }
|
||||
public int MaxValue { get; set; }
|
||||
public int DefaultValue { get; set; }
|
||||
|
||||
public DebugValueAttribute(string displayName = "", int minValue = 0, int maxValue = 100, int defaultValue = -1, float r = 0.8f, float g = 0.8f, float b = 0.8f)
|
||||
{
|
||||
DisplayName = displayName;
|
||||
MinValue = minValue;
|
||||
MaxValue = maxValue;
|
||||
// 如果defaultValue为-1(未设置),则使用minValue作为默认值
|
||||
DefaultValue = defaultValue == -1 ? minValue : defaultValue;
|
||||
ValueColor = new Color(r, g, b, 1f);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
using UnityEngine;
|
||||
|
||||
using MeowmentDebugTool;
|
||||
/// <summary>
|
||||
/// 示例调试函数集合
|
||||
/// 使用 [DebugButton] 特性来标记方法,它们会自动出现在调试工具中
|
||||
@ -185,4 +185,57 @@ public static class SampleDebugFunctions
|
||||
Application.Quit();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 复选框示例 ==========
|
||||
// 注意:复选框方法必须接受一个 bool 参数
|
||||
|
||||
[DebugCheckBox("显示FPS")]
|
||||
public static void ToggleShowFPS(bool isOn)
|
||||
{
|
||||
Debug.Log($"显示FPS: {isOn}");
|
||||
// 这里可以控制FPS显示组件的开关
|
||||
}
|
||||
|
||||
[DebugCheckBox("上帝模式", 1f, 0.8f, 0.2f)]
|
||||
public static void ToggleGodMode(bool isOn)
|
||||
{
|
||||
Debug.Log($"上帝模式: {isOn}");
|
||||
// 这里可以设置角色无敌等参数
|
||||
}
|
||||
|
||||
[DebugCheckBox("无限金币", 0.2f, 0.8f, 0.2f)]
|
||||
public static void ToggleInfiniteCoin(bool isOn)
|
||||
{
|
||||
Debug.Log($"无限金币: {isOn}");
|
||||
// 这里可以设置金币相关参数
|
||||
}
|
||||
|
||||
[DebugCheckBox("显示碰撞体", 0.5f, 0.5f, 1f)]
|
||||
public static void ToggleShowColliders(bool isOn)
|
||||
{
|
||||
Debug.Log($"显示碰撞体: {isOn}");
|
||||
// 这里可以控制碰撞体的可视化
|
||||
}
|
||||
|
||||
[DebugCheckBox("调试模式", 0.8f, 0.2f, 0.8f)]
|
||||
public static void ToggleDebugMode(bool isOn)
|
||||
{
|
||||
Debug.Log($"调试模式: {isOn}");
|
||||
Debug.unityLogger.logEnabled = isOn;
|
||||
}
|
||||
|
||||
[DebugCheckBox("静音音效")]
|
||||
public static void ToggleMuteSound(bool isOn)
|
||||
{
|
||||
Debug.Log($"静音音效: {isOn}");
|
||||
AudioListener.volume = isOn ? 0f : 1f;
|
||||
}
|
||||
|
||||
[DebugCheckBox("锁定帧率")]
|
||||
public static void ToggleLockFramerate(bool isOn)
|
||||
{
|
||||
Debug.Log($"锁定帧率: {isOn}");
|
||||
Application.targetFrameRate = isOn ? 30 : -1;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "com.bywaystudios.meowmentdebugtool",
|
||||
"displayName": "MeowmentDebugTool",
|
||||
"version": "0.3.0",
|
||||
"version": "0.3.7",
|
||||
"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": [
|
||||
{
|
||||
|
||||
Loading…
Reference in New Issue
Block a user