Skip to main content🕵️♂️ 福尔摩斯调试法:像侦探一样修 Bug
“排除了一切不可能的,剩下的不管多难以置信,那都是真相。” —— 夏洛克·福尔摩斯
调试不是瞎猜 (Guessing),而是推理 (Reasoning)。
1. 🔍 核心法则:二分查找 (Divide and Conquer)
当你面对一个庞大的系统 Bug 时,不要试图一行行看代码。
🔪 代码二分法
-
现象: 游戏启动就崩,不知道是哪个系统的问题。
-
操作:
- 把
GameBootstrapper 里一半的 Manager 注释掉。
- 跑一下。还崩吗?
- 崩: 问题在剩下的一半里。继续二分。
- 不崩: 问题在被注释掉的那一半里。把那一半恢复,再注释掉其中一半。
-
效率: 1000 个模块只需要 10 次就能定位到也是 O(logN)。
🕰️ 时间二分法 (Git Bisect)
-
现象: 昨天是好的,今天坏了。但我今天提交了 20 个 Commit。
-
操作: 使用
git bisect。
git bisect start
git bisect bad (当前版本)
git bisect good <commit-hash> (昨天的版本)
- Git 会自动跳到中间的一个 Commit。你测一下,告诉 Git 是
good 还是 bad。
- 几次之后,Git 会告诉你:“就是这行代码搞崩了全场”。
2. 🧠 内存侦查 (Memory Leak Hunting)
内存泄漏通常是隐形的杀手。
📸 内存快照对比 (Snapshot Diffing)
-
打开 Unity Memory Profiler。
-
进入主菜单,点击 Capture (Snapshot A)。
-
进入战斗,打一架,退回主菜单。
-
点击 Capture (Snapshot B)。
-
核心操作: 查看 Diff (B - A)。
- 理论上,回到主菜单后,内存应该大致回到 A 的水平。
- 如果你发现多了 100 个
Texture2D 或 500 个 EnemyInstance,那就是没卸载干净(比如被静态 Action 引用了)。
3. 🎨 渲染诊断 (RenderDoc)
当画面出现紫块、黑块,或者 DrawCall 莫名其妙爆炸时。
🎞️ 逐帧分析
-
在 Game 窗口点击右键 -> Load RenderDoc。
-
点击 Capture Frame。
-
在 RenderDoc 里打开这一帧。
-
Event Browser: 你可以看到这一帧 GPU 做的每一件事。
- DrawSkybox
- DrawOpaque
- DrawTransparent
-
查案:
- 为什么这个模型没有被合批 (Batching)?看它的 State,是不是材质不一样?是不是 Lightmap Index 不一样?
- 为什么这个像素是黑的?点击 Pixel History,看是哪个 Shader 计算出了 (0,0,0)。
4. 📝 崩溃分析 (Crash Dump Analysis)
不要只说“游戏崩了”。看日志。
📜 读懂 Stack Trace
- NullReferenceException: 90% 是因为你没判读
if (obj != null) 或者初始化顺序错了。
- MissingReferenceException: 你试图访问一个已经被
Destroy 的物体。
- IndexOutOfRangeException: 数组越界。
📱 移动端真机调试
- Android: 使用 Android Logcat (Unity 2019+ Package)。过滤
Unity 标签,可以看到真机的红色报错。
- iOS: 打开 Xcode -> Window -> Devices and Simulators -> View Device Logs。看崩溃报告 (Crash Report)。
5. 🌡️ 性能分析 (Profiling First)
不要先优化,先分析!
- 不要觉得 “我觉得这里卡” 就去改代码。
- 打开 Unity Profiler (Deep Profile 慎用,卡爆)。
- 看 CPU Usage 下的 Timeline。
- 找到那个耗时最长的函数长条。
- 那才是凶手。 优化其他地方都是浪费时间。