4.3 物理引擎:帧间的积分步骤


4.3 物理引擎:帧间的积分步骤

本节摘要:物理引擎按固定时间片对速度与位置做数值积分,时间片固定是稳定性的前提,渲染帧率波动时引擎用"帧长换步数"的方式消化差值。本节从一次穿模事故切入,讲物理后端接入、刚体三要素、定步长机制与失真排查,并给出动画与物理的分工边界。承接 4.1 的帧时间概念,通向第6章的游戏装配。

学习目标

读完后你应当能够:

  1. 复现并解释"帧率一低物体就穿模"的失真现象
  2. 为网格配置质量、 restitution、摩擦力三要素并预测行为
  3. 说出固定步长与渲染帧率解耦的机制,以及最大步长参数的保险作用
  4. 判断一段运动该交给动画系统还是物理引擎

从穿模事故说起

演示日前的彩排,保龄球撞瓶的环节在开发机上一切正常,投到大屏的备用机上球直接穿过了瓶子——什么都没改,只是机器慢了。这不是玄学,是物理引擎的数值积分在作怪:每帧用"当前速度乘帧长"推进位置,帧长越长单步位移越大;帧长超过物体厚度时,一步跨过瓶子,碰撞检测根本没机会发生。帧率越低穿得越欢,是物理失真的第一面孔。

解药说来简单:步长固定。不管渲染一帧花多久,物理永远按小而恒定的时间片前进,帧率只决定"这帧跑几个时间片"。画成时间轴一目了然:

图 4-3 固定步长:帧率波动时物理时间片保持恒定

图 4-3 固定步长:帧率波动时物理时间片保持恒定

接入物理后端

引擎本体不带物理,按插件接入。启用插件、创建 impostor(物理代理)两步:

// 第一步:启用物理插件(后端以 Havok 为例,接口随版本演进,接入行以后端文档为准) const hk = new BABYLON.HavokPlugin(); scene.enablePhysics(new BABYLON.Vector3(0, -9.81, 0), hk); // 重力向量:现实取值 // 第二步:给网格挂物理代理——形状与质量决定行为 const ball = BABYLON.MeshBuilder.CreateSphere("ball", { diameter: 1 }, scene); ball.physicsBody = new BABYLON.PhysicsBody(ball, BABYLON.PhysicsMotionType.DYNAMIC, false, scene); const shape = new BABYLON.PhysicsShapeSphere(BABYLON.Vector3.Zero(), 0.5, scene); ball.physicsBody.shape = shape; ball.physicsBody.setMass(2); // 质量 2 千克:轻球被撞飞,重球推得动 ball.physicsBody.setRestitution(0.6); // 弹性 0 到 1:0.6 约为篮球手感 ball.physicsBody.setFriction(0.4); // 摩擦:影响滚动衰减与落地滑行

三要素的手感可以背下来:质量决定"谁推动谁",弹性决定"落地弹多高",摩擦决定"滑多远"。地面是静态代理:质量无穷大、不受力,只负责被撞。

固定步长与保险丝的配置,通常在启用时一次设好:

// 步长与保险:物理按 60 赫兹步进,单帧补步数设上限 hk.setTimeStep(1 / 60); // 固定步长:稳定性的一半答案 hk.setMaxSteps(3); // 积压超过 3 步就丢弃,进入慢放而非雪崩

失真排查与分工边界

物理问题的排查顺序固定:先看穿透(步长或速度超标),再看抖动(接触点重叠求解反复,垫高初始间隙或加休眠),再看漂浮(质量设了零或重力没启用)。其中"休眠"值得展开:静止的刚体持续参与碰撞求解是白花钱,引擎在静止一段时间后让它休眠,被撞再唤醒——大量静态堆积物(货堆、石阵)的帧率救星。

最后是分工边界,一条判断就够:运动有没有"必须精确到帧"的剧本?门开 1.2 秒、角色跳起 0.6 秒,剧本明确,交给 4.1 的动画系统;球撞瓶、塔倒坍,结果不可预知,交给物理。混用的标准姿势是"动画演完交物理"——角色抛球的挥臂是动画,离手的球是刚体。

⚠️ 常见坑:物理代理与网格的变换同步是单向的(物理驱动网格),再往同一网格上叠动画就回到了 4.1 的"属性抢所有权"问题;抛物阶段的球别挂物理代理,离手那一刻再挂。

把彩排事故修好

开头那个穿模事故值得修到底。背景:备用机 22 帧、球速 12 米每秒,帧长 45 毫秒下单步位移超过半米,瓶厚才 4 厘米,一步跨过毫无悬念。操作三步。第一步复现:在开发机上把循环限到 22 帧,穿模稳定出现,证明与环境无关、只与帧长相关。第二步上固定步长:步长 1/60、补步上限 3,穿模消失,但备用机上出现了慢动作——补步上限太低,积压的物理时间被丢弃了。第三步权衡:把补步上限提到 5,同时给球速加保险丝,压到单步位移小于瓶厚。结果:两台机器观感一致,慢动作消失。解读:慢放与跳步是一对不可兼得的保险,取舍的杠杆是"最薄物体厚度除以步长"得到的速度红线,把红线写进代码,两头的风险同时封死。这个保险丝还有个隐藏收益:它同时约束了爆炸、连撞这类瞬间多物体高速的场景,帧率再低也不会把求解器推进不可信的速度区间。变式:雨点、子弹这类天然高速的小物体别硬扛积分精度,用射线扫略(把一步位移当成一段线段查碰撞)绕开厚度限制。

// 速度保险丝:单步位移不得超过场景最薄物体的厚度 const STEP = 1 / 60, THINNEST = 0.04; // 步长与最薄物体厚度(米) scene.onBeforeRenderObservable.add(() => { const v = ball.physicsBody.getLinearVelocity(); const speed = v.length(); if (speed * STEP > THINNEST) { // 单步位移越线 v.scaleInPlace(THINNEST / (speed * STEP)); // 等比收缩到安全速度 ball.physicsBody.setLinearVelocity(v); } });

小结

  • 穿模的第一嫌疑是步长:帧长超过物体厚度,一步跨过碰撞
  • 固定步长换稳定:帧率波动被换算成步数差,超限宁可慢放也不跳步
  • 三要素定性格:质量、弹性、摩擦,手感先背再调
  • 休眠救静态堆:静止刚体白烧求解时间,休眠唤醒机制免费救帧率
  • 速度红线双保险:最薄厚度除以步长定上限,高速小物体改走射线扫略
  • 分工边界:有剧本交动画,不可预知交物理,交接在离手瞬间

世界能动、能点、能撞了。还差两样伴随物——浮在画面上的界面与不占帧预算的声音,下一节一并收进流水线。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U