Post

Replies

Boosts

Views

Activity

PhysicsBody.applyForce() is not independent of speed, timeStep and FPS
I've setup a scene with SceneKit where a ball is rolling on a plan. I've simulated the grass friction with applyForce() in order to make it stop after a while (dampen & friction parameters do not reach that effect). Everything works well till I play with runtime parameters, like scnScene.physicsWorld.timeStep, sceneView.preferredFramesPerSecond and scnScene.physicsWorld.speed. I did a comparison with default gravity. Let's take a plan, with a light slope and launch a ball on it: let planSlope = (Float.pi/180.0)*3.0 planNode.rotation = SCNVector4(x:0, y:0, z:1, w: planSlope) ... let impulse =  Float(ballMass) * speed // N.s = m*v let forceVector = simd_float3(1.0, 0.0, 0.0) let force = SCNVector3(forceVector * impulse) ballNode.physicsBody?.applyForce(force, asImpulse: true) The ball will climb the slope up till a certain distance and come back after. If I change timeStep, FPS or speed, the ball is still reaching the same point. That's great. Now, I disable gravity and create a custom force to simulate it using applyForce(): scnScene.physicsWorld.gravity = SCNVector3(0.0, 0.0, 0.0) func physicsWorld(_ physicsWorld: SCNPhysicsWorld, didUpdate physicsContact: SCNPhysicsContact) { ... let gravity = simd_float3(0.0, -9.8, 0.0) let mass = (ballNode.physicsBody?.mass)! let force = SCNVector3(gravity * Float(mass)) ballNode.physicsBody?.applyForce(force, asImpulse: false) This gives exactly the same good result with default constants: scnScene.physicsWorld.speed = 1.0 scnScene.physicsWorld.timeStep = 1.0/60.0 sceneView.preferredFramesPerSecond = 60 But as soon as I change one of them, the distance changes: scnScene.physicsWorld.speed = 2.0 scnScene.physicsWorld.timeStep = 1.0/120.0 sceneView.preferredFramesPerSecond = 30 So, I needed to take them into account on the force: func physicsWorld(_ physicsWorld: SCNPhysicsWorld, didUpdate physicsContact: SCNPhysicsContact) { ... let gravity = simd_float3(0.0, -9.8, 0.0) // Compensate timeStep & FPS & Speed !? gravity *= Float(scnScene.physicsWorld.timeStep)/(1.0/60.0) gravity *= (1.0/60.0)/(1.0/Float(sceneView.preferredFramesPerSecond)) gravity *= 1.0/Float(scnScene.physicsWorld.speed) let mass = (ballNode.physicsBody?.mass)! let force = SCNVector3(gravity * Float(mass)) ballNode.physicsBody?.applyForce(force, asImpulse: false) That's weird, I guess I missed something? The risk is that if FPS changes dynamically due to GPU overload, the result will differ.
0
0
932
Dec ’22