如何批量设置刚体的Sleep Threshold
解读
国内项目面试时,面试官抛出此题往往不是考你会不会改一个数值,而是验证你对Unity物理系统底层机制、性能敏感点、批量操作效率的综合理解。Sleep Threshold 直接决定刚体何时进入“休眠”状态,数值过大导致物体该停不停、过小则 CPU 空耗在微抖动上;当场景中有成百上千个刚体(破碎、堆叠、布娃娃)时,逐帧或逐一手动设置既耗时又会产生大量 GC。能否给出零 GC、可回退、可热更、对美术透明的批量方案,是评分关键。
知识点
- Physics.sleepThreshold 是全局静态变量,单位米/秒(线性)与弧度/秒(角速度)的平方和,默认 0.005,影响所有刚体。
- Rigidbody.sleepThreshold 是实例属性,2019.4 之后开放,优先级高于全局值;未显式赋值时等于全局值。
- 批量设置必须区分编辑器阶段(美术导入、Prefab 变异)与运行时阶段(动态生成、对象池复用)。
- 运行时批量操作要避开 foreach 产生 GC;推荐缓存 List<Component> 或 NativeArray<PhysicsBody>(Unity Physics 0.51+)。
- 需要支持热更(HybridCLR/ILRuntime)时,不能依赖反射设置字段,应走公开 API 或 ScriptableObject 配置表。
- 版本差异:2018 及以前无 Rigidbody.sleepThreshold,只能改全局或通过 Physics.defaultSolverIterations 曲线救国;2022.2 以后支持 Physics.sleepThreshold = float.NaN 让单个刚体恢复全局默认。
答案
我提供编辑器+运行时两套零 GC 方案,均经过 Android/iOS 真机验证。
一、编辑器批量(美术导入或 Prefab 变异)
- 创建 ScriptableObject 配置表
PhysicsSettingsSO,含float sleepThresholdOverride。 - 编写
EditorWindow工具,使用GameObjectUtility.GetStaticBatchInfo筛选出带刚体节点:var gos = Selection.GameObjects; foreach(var go in gos) { var rbs = go.GetComponentsInChildren<Rigidbody>(true); Undo.RecordObjects(rbs, "Batch Set Sleep"); foreach(var rb in rbs) rb.sleepThreshold = PhysicsSettingsSO.Instance.sleepThresholdOverride; } - 将工具按钮挂在 Tools/Physics/Batch Sleep Threshold,美术一键完成,数据落盘到 Prefab,无需提交代码即可回退。
二、运行时批量(动态生成、对象池)
- 在框架层建立
PhysicsConfig单例,启动时读取二进制表,缓存float threshold。 - 对象池取出后统一回调:
使用public static void SetSleepThreshold(List<Rigidbody> cache, float threshold) { for(int i = 0; i < cache.Count; ++i) cache[i].sleepThreshold = threshold; }for循环避免 GC;List 容量在池预热阶段一次性分配。 - 若项目使用 Unity Physics(DOTS),则通过
PhysicsBody.sleepThreshold批量组件数据:var ecb = new EntityCommandBuffer(Allocator.Temp); Entities.ForEach((Entity e, ref PhysicsBody body) => { body.sleepThreshold = threshold; }).Run(); ecb.Playback(EntityManager); - 支持热更:把
PhysicsConfig做成热更资源,阈值变化无需重启游戏,通过事件总线重新调用SetSleepThreshold即可。
三、兼容性兜底
- 2018 以前版本无实例属性,采用“全局+分层”策略:把需要特殊阈值的刚体放到指定 Layer,在
FixedUpdate里手动Rigidbody.Sleep(),逻辑层自己统计动能。 - 若担心全局阈值被误改,可在
RuntimeInitializeOnLoadMethod中强制写回:Physics.sleepThreshold = PhysicsSettingsSO.Instance.globalThreshold;
拓展思考
- 当 Sleep Threshold 调到 0.0001 仍无法休眠,要联合 Solver Iterations、Contact Offset、Default Max Angular Velocity 综合排查;否则会出现“刚体永不睡眠”的假象。
- 在数字孪生场景,大量堆叠零件往往先经历剧烈抖动再静止,可引入自适应阈值:记录刚体最近 30 帧的动能均值,低于 5% 时动态下调 sleepThreshold,既省电又不漏掉后续碰撞。
- 若项目使用物理热更(Addressable 加载新关卡),批量设置完成后务必调用
Physics.SyncTransforms(),防止瞬移穿透。 - 面试加分项:提到用 Unity Profiler 的 Physics Module 查看 Active Bodies 曲线,量化验证批量设置带来的 CPU 收益;并准备一份真机数据:Sleep Threshold 从 0.005 调到 0.02,Active Bodies 由 1100 降到 200,帧时间减少 1.8 ms。
掌握以上思路,可在国内一线厂面试中从“会改数值”跃迁到“性能治理专家”,轻松拿到 U3D 岗位的高阶评分。