恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
BakeMesh粒子层级错乱怎么办?三种方案解决网格飞回原点
首页
资讯中心
/
BakeMesh粒子层级错乱怎么办?三种方案解决网格飞回原点
BakeMesh粒子层级错乱怎么办?三种方案解决网格飞回原点
发布时间:2026/10/18 11:51:56
简介针对Unity开发中粒子特效与UI层级冲突的常见痛点这份资源提供了一套基于BakeMesh的完整解决方案。文中系统对比了三种处理思路通过RenderTexture渲染虽能完美解决层级问题但需要额外摄像机且性能开销较大利用Canvas与Sorting Layer管理在多特效穿插时容易出现层级管理混乱和合批问题而BakeMesh方案不仅可像UGUI一样灵活调整上下层级排序还能正常接受Mask裁剪处理兼顾效果与性能。资源包共84个文件压缩后约758KB以C#脚本、Unity场景、材质、预制体、Shader着色器及RenderTexture等类型为主内容覆盖完整可直接运行的Demo工程示例并配有说明文档目录结构清晰便于快速定位与上手。已有782人学习下载适合需要在UI界面中集成粒子特效、精细控制特效层级并关注渲染性能的Unity开发者参考使用。1. 粒子层级错乱BakeMesh 后的网格为什么会飞回原点我最早被“BakeMesh粒子层级问题解决”这个标题拦住是因为一次特效合批翻车。把粒子挂在角色手骨下的空节点发射、飘散、拖尾一切正常然后我调用了一次 BakeMesh 想把一百多个粒子烘焙成静态网格好把 DrawCall 降下来。结果网格一挂回去整个特效瞬间飞到场景原点角色走动时拖尾像一条错位的披风一样被甩在地板上。更折磨人的是坐标轴怎么改都不对同一个特效一会儿跟着手骨走一会儿定在原地。这种问题在特效烘焙、美术导出和场景合并场景里极其常见而且它跟引擎版本无关跟烘焙函数“输出的是哪个坐标空间”直接相关。你踩过一次之后就会明白把 Mesh 挂到哪个层级其实是在回答一个问题顶点里的数据到底是世界坐标还是本地坐标。如果这里答错了矩阵就会算两遍或者干脆不参与计算。这篇文章就是围绕这个坑展开的适合所有正在用烘焙网格替代动态粒子、又必须保留正确父子跟随关系的开发者。2. 为什么粒子层级会乱坐标空间与父矩阵的双重陷阱2.1 粒子系统到底有多少个坐标系先说一个容易被忽略的常识。粒子系统生成粒子时每个粒子在出生那一刻都有一个“本地位置”这个本地是相对于粒子系统所在的游戏对象的。随后引擎按 Simulation Space 来决定后续每一帧的位置计算如果模拟空间是 World粒子的坐标会被换算成世界坐标哪怕粒子系统所在的节点动了粒子也不会跟着动如果模拟空间是 Local粒子始终以粒子系统自身坐标系为基准父节点移动、旋转、缩放时粒子会一起变换。也就是说粒子在屏幕上的最终位置通常由“粒子本地坐标 父节点矩阵”共同决定。这个矩阵可能在运行时一直变化但只要你不调用 BakeMesh粒子系统会每一帧重新计算它所以你不觉得层级是个问题。但 BakeMesh 不一样。BakeMesh 做的是把当前存活的粒子采样到一张静态 Mesh 里把那一刻的位置写进顶点缓冲。写进去之后它就不再具备“每一帧跟随父节点刷新”的能力了。Mesh 本来就没有父子关系的概念它只知道顶点坐标是多少然后交给渲染管线。所以真正决定网格出现在哪里的是两件事一是顶点里装的坐标是什么空间二是这个 Mesh 挂在哪个节点下渲染时谁又会把它的矩阵乘上去。如果烘焙时引擎提供了useTransform或者类似参数你选择的就是“写入顶点时是否应用粒子系统的矩阵”。很多中文资料把这个参数翻译成“是否烘焙到世界空间”反而容易误导人。它实际上决定了顶点里存的是“没算父节点矩阵的原始位置”还是“已经算了父节点矩阵的世界位置”。层级问题由此产生你把一个已经包含父节点矩阵的网格又挂回了原来的父节点上等于同一个变换被乘了两次或者你把一个完全没有矩阵信息的网格放到场景根节点等于父节点矩阵完全没参与。这两个方向都会让特效看起来彻底错位。2.2 父矩阵被算了两遍还是根本没算为了把问题说清楚这里用一点矩阵记号推导。假设粒子系统 P 挂在节点 N 下面节点 N 相对场景根节点的模型矩阵是 M粒子系统的本地坐标是 p_local。粒子真正应该出现在世界坐标的位置是 M × p_local。烘焙时有两种情况。第一种情况你选择了世界坐标输出顶点里存的已经是 M × p_local。如果此时把 Mesh 再次挂到节点 N 下渲染时 N 的矩阵会再次作用到顶点上最终坐标变成 M × (M × p_local)也就是父矩阵被平方了一次。这会导致什么角色转身烘焙网格并不是跟着转一次而是转了“两次”的量拖尾会甩到几倍距离之外如果父节点有缩放缩放也被平方圆形粒子会被压成扁片。第二种情况你选择了本地坐标输出顶点里存的是 p_local但你把 Mesh 放到了场景根节点这样一个没有父矩阵的地方。最终渲染坐标就是 p_local完全没有乘上 M。于是粒子系统原本在角色手骨位置发射烘焙网格却出现在粒子系统的原点也就是角色模型脚下附近看起来就像特效掉在了原地。所以粒子层级错乱本质上只有两种错误矩阵没算或者矩阵算了两次。判断是哪一种可以用一个很直白的方法先把节点 N 的位置、旋转、缩放依次归零。如果网格立刻“跳”到和粒子系统正常位置重合说明顶点里其实带上了父矩阵你的挂载层级又在额外乘它如果网格一动不动只是位置概念上不对那就属于缺了父矩阵。这个测试在后续定位章节里还会展开。2.3 冻结状态下的粒子也一样有坐标系歧义除了坐标系数选择BakeMesh 的时间点也很关键。烘焙只针对“当前存活粒子”它不会自动往前推时间去模拟一整个发射周期。很多人在粒子系统播放中某一帧调用 BakeMesh得到的网格并不完整然后把这个不完整归结到层级问题上开始瞎调 parent。还有一种情况是粒子系统已经 Pause。Pause 状态下部分引擎会把粒子的位置冻结在本地坐标系里如果此时调用 BakeMesh得出的网格顶点坐标和正常播放时的世界坐标相差很多。再加上粒子系统自身在层级里又带了偏移最终烘焙出来的结果就可能同时包含“生命周期”和“坐标系”两个问题。因此在第 4 章给方案之前先把这句话记住烘焙前先确保粒子系统处于正常模拟状态并且确认particleCount不为零。这一步不做后面所有层级排查都会建立在错误的样本上。真正干净的烘焙应该发生在粒子运动到某一帧、粒子数量稳定、且你能明确说出“我到底要本地坐标还是世界坐标”之后。3. 定位粒子层级报错的三个步骤从现象到根因3.1 最小复现先把问题缩小到一个节点和一次烘焙定位层级问题最怕的是在复杂特效里来回试。一个角色身上可能挂了七八个粒子系统每个下边又有子节点烘焙出来的 Mesh 还可能被合并进别的模型。这时候你根本无法判断是哪个矩阵出了问题。正确做法是先造最小复现场景一个空节点 N节点下面挂一个粒子系统 PP 下面不再套别的子级。然后写一个脚本只对这一个粒子系统烘焙一次把结果复制到一个独立的 MeshFilter 上。脚本不需要复杂逻辑就做一件事把 ParticleSystemRenderer 的当前状态烘焙到指定 Mesh 里。// 最小复现把粒子系统烘焙成 Mesh并返回 public Mesh BakeSingleParticleSystem(ParticleSystem ps, Mesh targetMesh, bool outputAsWorldSpace) { // 每次先从旧 Mesh 清空避免残留上一帧的数据 targetMesh.Clear(); var renderer ps.GetComponentParticleSystemRenderer(); if (renderer null) return null; // 第二个参数决定顶点里写本地坐标还是世界坐标 renderer.BakeMesh(targetMesh, outputAsWorldSpace); return targetMesh; }这里的关键参数是outputAsWorldSpace。很多人误以为传false表示“输出模型文件里那个本地坐标”传true表示“在场景中看起来正确”。其实这个值只决定 BakeMesh 在写顶点时是否把 ParticleSystem 所在的整条父链矩阵一起乘进去。传true时顶点已经包含了 N 到根节点的所有变换传false时顶点只反映粒子相对对象本身的偏移。做完这一步后不要急着把 Mesh 挂到正式角色节点上而是先放到场景根节点对比它与原粒子系统的位置差异。如果差异恰好等于 N.WorldPosition那说明缺矩阵如果差异远大于 N.WorldPosition并且有翻倍的特征那说明烘焙时已经算过一次矩阵。3.2 用包围盒中心做数学比较而不是目测截屏肉眼定位粒子层级问题经常被视觉骗到因为粒子本身有半透明、有拖尾、有大有小看起来差不远实际上偏移了几十米。每次做这类排查我都会依赖数字而不是截屏。先在烘焙前记录粒子系统的世界包围盒中心烘焙后记录 MeshRenderer 的世界包围盒中心两者在控制台里打印出来。代码可以这样写// 对比粒子系统与烘焙网格的世界中心偏差 void CompareWorldCenters(ParticleSystem ps, MeshFilter filter) { var psRenderer ps.GetComponentParticleSystemRenderer(); var meshRenderer filter.GetComponentMeshRenderer(); Vector3 psCenter psRenderer.bounds.center; Vector3 meshCenter meshRenderer.bounds.center; Vector3 diff meshCenter - psCenter; Debug.Log($粒子系统中心: {psCenter}); Debug.Log($烘焙网格中心: {meshCenter}); Debug.Log($两者偏差: {diff.magnitude}); }注意这里必须用ParticleSystemRenderer.bounds和MeshRenderer.bounds而不是用mesh.bounds。mesh.bounds是顶点自身的包围盒不经过 TransformRenderer.bounds才是世界空间下的包围盒。如果你拿mesh.bounds去跟世界坐标比会发现数字永远是“对不上”那是比较对象错了。判断标准很简单偏差差不多等于粒子系统的世界坐标说明父节点矩阵没有作用到顶点里偏差接近粒子系统世界坐标的两倍或带旋转变化说明父节点矩阵被作用了两次。前者走方案一后者走方案二。3.3 两个容易误判的现象生命周期与渲染剔除有时候你辛苦定位半天最后发现根本不是层级问题。第一个误判是“烘焙结果特别少”。粒子系统生命周期 2 秒你刚好在它发射到第 50 帧时去 BakeMesh存活粒子自然只有一瞬的数量。比较稳妥的方式是先用Simulate推一段固定时间把粒子系统跑到一个特定状态再执行烘焙并打印particleCount。只有确认了存活粒子数量符合预期后面层级判断才有意义。第二个误判是“烘焙后的网格被裁剪了”。很多引擎的视锥剔除依赖渲染器的包围盒而BakeMesh之后网格的顶点已经变化包围盒却没有自动更新。于是网格明明在屏幕上却被判定为不在相机范围内结果整个物体消失或闪烁。这种问题看起来也像“位置不对”但只要你把相机缩到很远的距离网格又会突然出现。层级问题的网格不会因为相机远近而出现消失两者很好区分。3.4 用“归零法”区分烘焙位移和渲染位移还有一个很实用的现场判断手法我叫它“归零法”。把烘焙出来的 Mesh 挂到目标父节点下然后在编辑器里把它的LocalPosition归零LocalRotation归零LocalScale设为 1。注意观察网格的变化。如果网格跟着一起回到了粒子系统原本的本地坐标说明这个网格的顶点其实已经包含了父节点矩阵归零只是让它“又少算了一次妈妈”。如果网格纹丝不动还在你原有的偏移位置说明烘焙时根本没算父矩阵顶点里是裸的本地坐标移动 transform 自然不会改变它。我一般会把这一步放在“确认根因”之前因为它是最快区分两种坐标空间的方式世界坐标烘焙原父节点 双重矩阵本地坐标烘焙根节点 缺失矩阵。搞清楚这两个方向后面所有解决方案都能直接对应上。4. 解决 BakeMesh 粒子层级问题三种适配不同层级的方案4.1 方案一本地坐标烘焙挂回原父节点大多数单粒子系统转静态网格的需求用方案一就够了。做法是让 BakeMesh 输出本地坐标然后把烘焙网格挂到原来粒子系统的同一个父节点下让父节点矩阵继续负责位移、旋转和缩放。// 方案一本地烘焙保持原有层级跟随 public GameObject BakeToLocalAndAttach(ParticleSystem ps, Mesh mesh, Transform parentNode, Material mat) { var renderer ps.GetComponentParticleSystemRenderer(); // 清理 mesh 并烘焙到本地空间 mesh.Clear(); renderer.BakeMesh(mesh, false); // 新建一个网格对象放在原粒子系统同一父级下 var meshGo new GameObject(BakedParticles_Local); meshGo.transform.SetParent(parentNode, false); meshGo.transform.localPosition Vector3.zero; meshGo.transform.localRotation Quaternion.identity; meshGo.transform.localScale Vector3.one; meshGo.AddComponentMeshFilter().sharedMesh mesh; meshGo.AddComponentMeshRenderer().sharedMaterial mat; return meshGo; }这里关键点有二。第一BakeMesh(mesh, false)里的false必须和挂载层级配套不能一边烘焙成世界坐标一边又挂回原父节点。第二新建出来的meshGo必须把本地位置、旋转、缩放全部归零确保父节点矩阵不会出现二次叠加。这个方案的隐藏好处是如果原父节点在后续动画里发生位移网格会跟着走和原来的粒子系统表现一致。缺点也很明显如果父节点本身包含非均匀缩放本地烘焙把父矩阵留给 transform 后缩放依然会正确作用到网格上不会有二次缩放问题这点比世界坐标烘焙更安全。4.2 方案二世界坐标烘焙网格放到根层级当你需要把多个粒子系统合并进同一个静态 Mesh或者导出场景给别的程序用时方案一就不适用了因为不同粒子系统可能挂在不同节点下合并后无法同时保留各自的父级矩阵。这时必须统一用世界坐标烘焙并把合并后的网格放到场景根节点。// 方案二世界坐标烘焙合并后放到根节点 public Mesh BakeAllToWorld(ListParticleSystem psList, Mesh combinedMesh, Transform rootNode) { combinedMesh.Clear(); // 先收集所有粒子的世界坐标顶点 var vertices new ListVector3(); var triangles new Listint(); var normals new ListVector3(); foreach (var ps in psList) { int vertexOffset vertices.Count; // 先从每个粒子系统分别烘焙 var tempMesh new Mesh(); var renderer ps.GetComponentParticleSystemRenderer(); renderer.BakeMesh(tempMesh, true); // 把烘焙结果合并到大 Mesh 里 vertices.AddRange(tempMesh.vertices); normals.AddRange(tempMesh.normals); foreach (var tri in tempMesh.triangles) triangles.Add(vertexOffset tri); Object.Destroy(tempMesh); } combinedMesh.SetVertices(vertices); combinedMesh.SetNormals(normals); combinedMesh.SetTriangles(triangles); combinedMesh.RecalculateBounds(); // 挂到根层级坐标归零 var meshGo new GameObject(BakedParticles_World); meshGo.transform.SetParent(rootNode, false); meshGo.transform.position Vector3.zero; meshGo.transform.rotation Quaternion.identity; meshGo.transform.localScale Vector3.one; return combinedMesh; }这段参考实现里要特别注意的是每个子粒子系统烘焙时都用了true也就是把各自的父链矩阵都乘进了顶点。合并完成后大 Mesh 的位置已经包含所有粒子系统的偏移所以根的 transform 必须保持单位矩阵。如果根节点不是场景原点也应该把它的位置、旋转、缩放都设成中性值否则会把已经写进顶点的世界坐标再乘一次。方案二适合“魔法烘焙成模型导出”的场景也适合把多个技能特效合并成一个整体来减少 DrawCall。缺点是调试起来不如方案一直观一旦某个粒子系统的层级变化了你看到的网格位置会和实际粒子系统位置不一致因为它已经变成一份静态快照。4.3 方案三层级不能动就用父矩阵的逆矩阵把顶点压回来有些项目的节点结构是设计好的不允许你把烘焙网格重新挂到根节点。比如角色胳膊上必须有一个子物体后续动画脚本只认这个层级。这时候你既不能挂根节点也不能把烘焙网格直接挂到原父节点下。绕过办法是先烘焙成世界坐标再用父节点矩阵的逆矩阵把顶点“压”回局部坐标最后挂回原父节点让父矩阵恢复原来的作用。// 方案三把世界坐标顶点转换回父节点本地坐标 public void BakeWorldAndRemapToLocal(ParticleSystem ps, Mesh mesh, Transform parentNode) { var renderer ps.GetComponentParticleSystemRenderer(); mesh.Clear(); renderer.BakeMesh(mesh, true); // 先输出世界坐标 // 取父节点矩阵的逆把世界坐标还原成局部坐标 Matrix4x4 worldToLocal parentNode.localToWorldMatrix.inverse; Vector3[] vertices mesh.vertices; for (int i 0; i vertices.Length; i) { vertices[i] worldToLocal.MultiplyPoint3x4(vertices[i]); } mesh.vertices vertices; mesh.RecalculateBounds(); mesh.RecalculateNormals(); // 现在可以安全挂回原父节点 var meshGo new GameObject(BakedParticles_Remap); meshGo.transform.SetParent(parentNode, false); meshGo.transform.localPosition Vector3.zero; meshGo.transform.localRotation Quaternion.identity; meshGo.transform.localScale Vector3.one; }这里的核心逻辑是已知最终挂回父节点后会再乘一次 M所以先在顶点里乘一次 M⁻¹让最终世界坐标保持不变。注意MultiplyPoint3x4处理的是三维点不是方向。如果你还需要保留法线不能直接用worldToLocal去乘法线否则法线方向会被扭曲。正确做法是用法线矩阵也就是worldToLocal.transpose的逆或者干脆在RecalculateNormals里让引擎重新计算。这个方案看起来完美但有一个代价如果父节点包含非均匀缩放逆矩阵运算会引入浮点误差尤其是顶点数量很多、而且反复烘焙时误差会被放大。我通常只在层级结构不可动且父节点矩阵较干净的时候用方案三否则宁愿去说服美术改一下挂点结构。4.4 三种方案怎么选用一张表直接给出结论场景推荐方案原因单个粒子系统需要继续跟随父节点方案一本地烘焙 原父节点保留矩阵无二次叠加多个粒子系统合并成一个模型方案二世界烘焙 根节点统一坐标空间最稳定粒子系统挂在骨骼节点下且无法迁移方案三逆矩阵回写局部保住层级但要接受精度损失父节点有非均匀缩放方案一优先方案三慎重非均匀缩放的逆矩阵容易引入形变UI 粒子转静态网格不改方案需要检查 Canvas 坐标系烘焙网格无法直接参与 UI 屏幕空间变换选择错误的最常见原因是开发者在同一个工程里混用了几种方案。比如某个系统的烘焙网格挂在了根节点另一个系统又挂在原父节点结果合在一起后坐标风格不一致让定位变得非常困难。我一般会在烘焙工具里加一个枚举参数把三种模式显式命名为LocalKeepParent、WorldAtRoot、WorldRemapToLocal任何新增烘焙调用都必须先声明自己用哪种模式不允许随便裸调BakeMesh。5. 避坑BakeMesh 粒子层级问题的高频翻车记录5.1 第二次烘焙出现滞留残影现象第一次烘焙后网格正确。第二次调用同一个 Mesh 后新粒子旁边还留着上一次的旧粒子轮廓而且在场景里跟着捣乱。原因没有先清空 Mesh。BakeMesh 在写入顶点时默认追加还是覆盖取决于引擎实现。一旦旧顶点残留就会出现新旧粒子同时显示。层级调整得再对也没办法把这些旧顶点重置它们只是备份放在那里。解决每次烘焙前必须调用Mesh.Clear()并且在Clear后再次确认顶点数组长度归零。如果用的是模板 Mesh也要小心共享网格问题多个对象引用同一个 Mesh 资源时清空会对所有引用生效所以最好为每次烘焙单独生成一个新 Mesh 实例。5.2 非均匀缩放下的父节点把圆形粒子压成椭圆现象角色骨骼某段在 X 方向被压扁粒子系统挂在下面烘焙网格在其他方向正常X 方向却拉伸了一倍。原因父节点矩阵 M 里包含非均匀缩放。世界坐标烘焙时顶点先被 M 缩过一次挂回原节点后又被 M 缩一次于是缩放被平方。平面粒子原本是圆形经过 M² 之后就变成椭圆。解决先检查父节点的lossyScale三个轴不能有数量级差。最稳的办法是采用方案一把粒子系统的本地坐标烘焙出来把矩阵留给父节点处理这样缩放只作用一次。如果一定要用世界坐标烘焙就放弃挂回原父节点放到根层级。5.3 烘焙后的网格偶尔消失或只有一半可见现象粒子明明在屏幕中央烘焙网格却整个消失。拉远相机后它又出现或者转动视角时网格的一半瞬间被裁掉。原因不是层级问题而是剔除问题。BakeMesh 后顶点坐标虽然变化了但 Mesh 的包围盒可能没有跟着更新渲染器拿旧的包围盒去做视锥剔除把已经偏掉的对象裁掉。尤其是在逐帧烘焙、逐帧替换 Mesh 的动态方案里这个问题更容易出现看起来像位置不对其实位置是对的。解决每次烘焙后强制调用mesh.RecalculateBounds()。如果粒子飞行范围很大比如子弹拖尾横跨半个场景可以把mesh.bounds手动扩大到足够覆盖最大范围避免剔除误判。这个动作放在所有修改顶点数据的代码之后属于强制项。5.4 粒子系统暂停时烘焙导致粒子数量不完整现象在ParticleSystem.Pause()状态下调用 BakeMesh得到的网格只有零星几个粒子和画面完全不匹配。原因Pause 只是暂停了模拟但现存粒子数量和所处帧位置可能和视觉画面并不是同一状态。有些引擎在 Pause 后粒子会停在某个内部缓冲里BakeMesh 只读取其中存活的一小部分。开发者误判为层级问题于是开始查父节点矩阵完全走错方向。解决先确认粒子系统已经正常播放再用Simulate固定到一个预期时间点再调用BakeMesh。我一般会在烘焙工具入口打印ps.particleCount如果为 0 直接中止避免后续所有步骤建立在空数据上。5.5 UI 层级的 Canvas 与烘焙网格坐标系不一致现象血条旁边挂了一个粒子特效烘焙后网格在 3D 空间里却不在 Canvas 所在平面上特效要么出现在屏幕角落要么被 UI 遮挡得看不到。原因UI 粒子本身经过 Canvas 的层级变换最终坐标是屏幕空间或相机平面空间而 BakeMesh 输出的只是场景世界坐标。把世界坐标网格放到 Canvas 下Canvas 不会再帮你做一次屏幕换算于是坐标在 3D 世界里是正确位置在 UI 平面上却完全对不上。解决如果必须烘焙就不让烘焙网格直接参与 UI 渲染而是把它作为一个中间模型每一帧手动把它的世界坐标换算成 Canvas 的本地坐标再赋值给一个 UI 元素。或者保留动态粒子只有非 UI 部分才做烘焙。这是层级问题里最容易被忽略的一类因为它的报错表现不是“飞到原点”而是“坐标看起来不正常”。6. 快速定位层级问题的检查清单四步验证法如果你现在正在为一个 BakeMesh 层级问题头疼不要急着改代码先按下面四步走一遍。这套流程不依赖具体引擎只依赖你对“这个网格应该在世界空间还是本地空间”的判断。第一步确认烘焙前提。调用烘焙前打印particleCount确认有存活粒子粒子系统不能处于 Pause 状态。这一步排除了“烘焙结果为空”造成的假象。如果粒子数量本身就不对先解决生命周期问题不要碰层级。第二步记录原始位置。在烘焙前记录粒子系统 Renderer 的世界包围盒中心记作 A。烘焙后把网格挂到目标层级再记录 MeshRenderer 的世界包围盒中心记作 B。如果 B 与 A 几乎重合说明层级没问题问题出在别处如果偏差很大进入第三步。第三步做一次“归零实验”。把网格的 localPosition、localRotation、localScale 全部清空。观察网格有没有跳到另一个位置。如果归零后网格反而和粒子系统位置重合说明 BakeMesh 已经算过父矩阵而你又挂回了原父节点双重矩阵实锤。如果归零后变化不大说明顶点缺少父矩阵网格需要放到一个有正确 transform 的父节点下。第四步对照父矩阵特征。查看父节点的旋转和缩放如果只有一个位移试试把网格挂到根节点并归零可能立刻正确如果父节点有非均匀缩放优先选择本地坐标烘焙而不是世界坐标烘焙。遇到“网格在屏幕上忽隐忽现”先检查RecalculateBounds有没有调用而不是继续调坐标。我自己的习惯是每次接新项目或换引擎时都会先写一个最小的BakeAndCompare工具把上面四步固化进去任何新的烘焙需求都先过这套脚本再接入正式业务。从那以后“粒子烘焙后飞回原点”这类问题基本上都能在十分钟内定位到根因不再需要靠玄学猜坐标。完整的问题定位脚本和三种方案示例我都整理进了配套的文档资料里方便以后对照使用。希望帮到你。本文还有配套的精品资源点击获取