matter-js
matter-js copied to clipboard
Sleeping bodies won't wake up if force was applied to waking object each tick from standstill
Suppose we have two objects, both sleeping. Then we continuously (each update tick) applying force to one of the objects to make it collide with other one; after collision happens, it is expected for second object to wake up and start moving too, but that's not happening: second object stands still, like if it was static.
Code that reproduces the issue:
const engine = Matter.Engine.create({enableSleeping: true, gravity: {x: 0, y: 0}})
const render = Matter.Render.create({element: document.body, engine: engine})
// isSleeping: true here just to avoid waiting for object to sleep naturally
const movingBody = Matter.Bodies.rectangle(-50, 0, 50, 50, {isSleeping: true})
const sleepingBody = Matter.Bodies.rectangle(50, 0, 50, 50, {isSleeping: true})
Matter.Composite.add(engine.world, [movingBody, sleepingBody])
Matter.Render.lookAt(render, Matter.Composite.allBodies(engine.world), {x: 10, y: 10})
const update = () => {
requestAnimationFrame(update)
// applying force EACH TICK
Matter.Body.applyForce(movingBody, movingBody.position, {x: 0.0005, y: 0})
Matter.Engine.update(engine, 1000 / 60)
}
update()
Matter.Render.run(render)
Note that initially object was standing still (motion
= 0), and then was accelerated each tick by applying force.
Potential fix: in Sleeping.update
, if object has nonzero force
- its motion
is not updated; update of the motion
probably should happen regardless of force presense. I tried it locally and it works (I just moved motion update code above force check), but I don't have time to make a proper PR.
Also possibly related to #1112 , but I'm not sure.
Thanks for sharing this test case.
It looks like moving L38-L42 to come after L48 in Matter.Sleeping might be what's needed here - is that what you tried out?
Yup. Right now that loop looks like this for me:
// update bodies sleeping status
for(var i = 0; i < bodies.length; i++){
var body = bodies[i],
motion = body.speed * body.speed + body.angularSpeed * body.angularSpeed
var minMotion = Math.min(body.motion, motion),
maxMotion = Math.max(body.motion, motion)
// biased average motion estimation between frames
body.motion = Sleeping._minBias * minMotion + (1 - Sleeping._minBias) * maxMotion
// wake up bodies if they have a force applied
if(body.force.x !== 0 || body.force.y !== 0){
Sleeping.set(body, false)
continue
}
if(body.sleepThreshold > 0 && body.motion < Sleeping._motionSleepThreshold * timeFactor){
body.sleepCounter += 1
if(body.sleepCounter >= body.sleepThreshold){
Sleeping.set(body, true)
}
} else if(body.sleepCounter > 0){
body.sleepCounter -= 1
}
}