[已解决]请问如何保证物理更新帧率不变?

问题:
是这样的,一个基于物理引擎的windows游戏,我想保证物理更新帧率固定,不受影响,而渲染的帧率随机器运行状况的变化而变化。也就是,在能进行游戏的最坏情况下,物理是60帧,而渲染仅几帧(再卡都能玩)。请问如何才能实现?

以下是惨痛的经历:
我查文档查到了cc.PhysicsManager.FIXED_TIME_STEP,将其设置为1/60,它的解释是“会以固定的间隔时间 FIXED_TIME_STEP 来更新物理引擎,如果一个 update 的间隔时间大于 FIXED_TIME_STEP,则会对物理引擎进行多次更新”,很符合我的想法。
然而,当我用cc.game.setFrameRate()设置不等于60帧的帧率时,我写的跳跃挂了,跳跃高度不定(帧数设置得高,跳得高)。跳跃是用applyForce施加了0.1s竖直向上的力(估计少有人这么写吧)。

if(this.timer + dt >= jumpForceTime){//加上这一帧的施力时间如果超出了0.1s,就只施加按时间比例来算的一部分的力 this.rigidBody.applyForceToCenter(this.gravity.neg().normalize().mul(jumpForce * (jumpForceTime - this.timer) / dt), true); this.jumpAccel = false; this.timer = 0; }else{ this.rigidBody.applyForceToCenter(this.gravity.neg().normalize().mul(jumpForce), true); this.timer += dt; }

大概就是在update()里一直applyForce,用timer计时,超过0.1s就不再施加力。
除了跳跃高度随setFrameRate()的帧率变化而变化的问题外,我把施加力换成施加(力*dt)的冲量,效果居然不一样。

之后我又尝试自己手写了一个fixedUpdate(),把物理操作全部装了进去,然后在所有物理更新组件的update()里面写
let aim = Math.floor(cc.director.getTotalTime() / 1000 / this.fixedUpdateStep); while(this.fixedUpdateTimes < aim){ ++ this.fixedUpdateTimes; this.fixedUpdate(this.fixedUpdateStep); }
如果update的dt太长就执行多次fixedUpdate(),感觉和引擎的FIXED_TIME_STEP差不多。
结果居然和设置引擎的FIXED_TIME_STEP效果不同。。
施加力和施加(力*dt)的冲量效果变得一样了,但又出现新的状况:如果setFrameRate()设置超过了物理的60帧,不管这个帧数是多少,物理更新都是稳定的;如果setFrameRate()设置低于了物理的60帧,物理更新又不稳定了。物理更新总是不能比渲染快。

我想这个简单的问题应该很好解决,结果怎么都解决不了,然后就不敢尝试其他方法了,总感觉物理更新和渲染绑在一起,不知道哪里写错了

只要有一种解决这个问题的稳定方法就行!
文档里没找到,网上没查到,自己一个人到处碰壁,现在无从下手。跪求大佬们的帮助!!

/** !#en
Specify the fixed time step.
Need enabledAccumulator to make it work.
!#zh
指定固定的物理更新间隔时间,需要开启 enabledAccumulator 才有效。 */
static FIXED_TIME_STEP: number;

enabledAccumulator 这个值你设置了没有哇?

嗯设置了的。

那应该没啥问题呀,以前我也设置过物理帧率超过渲染帧率的,也没啥问题。这是2.3.3版本的源码部分

1赞

1.除非你物理引擎比渲染快。
2.物理引擎是基于时间轴,不是基于时间分片模拟

好好评估一下,这两条件。

engine\cocos2d\core\physics\CCPhysicsManager.js中,update方法:

首先你要理解world.Step(timeStep, velocityIterations, positionIterations)中的timeStep是什么。
在自然界中,假如你要记录桌面上一个匀速向右运动的小球在n秒的位置,那只要固定一个相机在n秒拍张照再测量就行了。这里的box2d传入timeStep再Step,就是说模拟timeStep秒后的物理世界的样子。
它这里在update函数内调用Step,如果说每一次调用update的间隔是恒定的且timeStep也是恒定的,那肯定能很正确的模拟,但是update的调用间隔会随着其它类的update的执行代码而有延迟,而update(dt)中的就记录了距离上一次调用update的时间间隔。
所以你要做到不管update调用是否有延迟都能模拟到一样的位置,只需要自定义引擎把update(dt)中的dt传入world.Step(dt, velocityIterations, positionIterations);就行了(这里本来就应该传入dt才算正确的,但如果timeStep不恒定就会导致刚体运动时快时慢,给人卡顿的感觉,具体看你怎么衡量了)。

感谢你们的帮助!
现在问题已经解决了。
其实不是引擎中FIXED_TIME_STEP的问题,而是我写的物理逻辑更新顺序的问题,组件们的update()顺序不是我希望的更新顺序。
我给出的那些问题,并就不是根本的问题:joy:
于是我参照官方教程给的“在 Update 中用自定义方法控制更新顺序”的方法保证了更新顺序,现在运作正常了。