矩阵真正让人头疼的,通常不是学习阶段,而是线上排查阶段。
我自己现在碰这类问题,第一反应已经不是“这个 API 会不会有坑”,而是先想办法把空间关系画出来。因为只要能画出来,问题大概率就已经解决一半了。
因为大多数矩阵问题表面上都不像矩阵问题:
- 特效位置歪一点
- UI 跟随差半个身位
- 子弹方向总偏一点
- Gizmo 画出来和实际碰撞范围不一致
这篇就不再讲太多理论,专门讲怎么查。
一、先建立一个原则:不要直接猜“哪个 API 有 bug”
多数坐标错位问题,根本原因通常只有几类:
- 局部空间和世界空间搞混
- 把点当向量,或者把向量当点
- 父节点缩放或旋转污染了结果
- 自定义偏移是在错误空间里叠加的
所以排查时最重要的不是换 API,而是把当前数据所属空间说清楚。
二、InverseTransformPoint 是定位“世界点为什么不对”的神器
例如你有一个世界命中点,想知道它相对某个角色是在左边还是右边、前面还是后面。
最稳的做法往往不是自己点乘算,而是先转回角色局部空间:
1 | Vector3 local = transform.InverseTransformPoint(worldPos); |
这样:
local.x > 0说明在右边local.z > 0说明在前方
很多看起来复杂的世界判断,回到局部空间后反而会简单很多。
三、MultiplyPoint 和 MultiplyVector 一定要配合语义去选
这件事前面讲过几次,但在排查时尤其重要。
你要确认自己现在处理的是:
- 一个位置点
- 一个方向向量
例如:
1 | Vector3 p = matrix.MultiplyPoint(localPoint); |
一旦这里选错,后面所有数值都可能“看起来只差一点”,但永远调不正。
四、Gizmos 是把抽象矩阵可视化的最好朋友
很多问题打印日志没感觉,但 Scene 视图一画就清楚了。
例如你可以画:
- 物体局部原点
- 局部 x/y/z 三轴方向
- 经过矩阵变换后的点位
- 目标区域包围盒
只要图能画出来,空间错位问题通常会快很多暴露。
例如你完全可以给自己准备一个最小的调试函数,专门把局部点集映射到世界里画出来:
1 | void DrawPoints(Matrix4x4 matrix, Vector3[] localPoints, Color color) |
这类工具平时像是“多写了点辅助代码”,但一到技能范围、刷怪区域、碰撞盒和棋盘调试阶段,收益会非常直接。
五、一个特别实用的调试模式:同时画“局部版本”和“世界版本”
比如你在做扇形攻击范围。
不要只画最终世界扇形。
更好的做法是:
- 先在局部空间定义扇形点
- 把局部点画出来
- 再乘矩阵得到世界点并画出来
这样如果最后结果歪了,你能立刻判断问题出在:
- 局部定义阶段
- 矩阵变换阶段
- 世界叠加阶段
六、排查父子层级问题时,一定要把父节点也拉进日志
很多人只打印出错物体自己的位置和旋转。
但真正的污染源常常在父节点:
- 父节点转了
- 父节点非等比缩放
- 父节点挂在另一个移动平台下面
所以排查时建议一起看:
1 | Debug.Log(transform.localPosition); |
七、一个很高频的业务例子:技能指示器为什么总偏半步
这种问题常见于:
- 指示器是在角色局部前方定义的
- 但你最后拿世界前方向量又叠了一遍偏移
也就是同一段变换被重复做了两次。
这种 bug 非常容易出现在“先 TransformPoint,后面又自己加 forward * offset”的代码里。
八、用局部空间做判断,通常比直接在世界空间硬算稳
比如扇形检测、矩形攻击框、前后左右判定、小地图相对位置,很多时候:
先把目标点逆变换回局部空间,再做几何判断,会比直接在世界空间里绕角度和投影稳得多。
因为局部空间里很多问题会退化成:
- 看符号
- 看范围
- 看距离
逻辑简单很多。
九、给自己准备几个长期可复用的小工具
我很建议在项目里长期保留一些调试工具:
- 绘制局部坐标轴的小组件
- 把世界点转局部点并显示文本的工具
- 绘制矩阵变换后点集的 Gizmo 帮助函数
- 临时打印
localToWorldMatrix和worldToLocalMatrix的工具方法
这些工具平时看起来没存在感,但一到线上疑难坐标问题,会非常值。
十、总结
矩阵问题最怕“纯脑补排查”。
把空间说清楚、把点和向量分清楚、把 Gizmos 画出来,通常比你盯着数值猜半天有效得多。
下一篇我会把这整个系列收回到更贴近项目的一层:动画、相机、地块、程序化摆放这些场景,矩阵知识到底怎么真正转化成工程收益。