matter-js icon indicating copy to clipboard operation
matter-js copied to clipboard

Sleeping bodies won't wake up if force was applied to waking object each tick from standstill

Open nartallax opened this issue 1 year ago • 2 comments

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.

nartallax avatar Mar 19 '23 01:03 nartallax

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?

liabru avatar Apr 02 '23 23:04 liabru

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
	}
}

nartallax avatar Apr 02 '23 23:04 nartallax