Setting enable_sleep() isn't persistent

I’ve bumped into what I think is a bug to do with preventing a dynamic body from sleeping in Box2D v3.

This code doesn’t stop the body from falling asleep, and the body is not affected by the joint when created:

function init(self)

	self.body = b2d.get_body("/ball#collisionobject")
	b2d.body.enable_sleep(self.body, false)

	timer.delay(2, false, function()
		create_joint() -- This doesn't work because the ball body is asleep
	end)

end

However, if enable_sleep() is continuously triggered in the update(), the body does wake up:

function update(self, dt)

	b2d.body.enable_sleep(self.body, false) 

end

Maybe these two Box2D functions have been mixed up?

b2Body_SetAwake()
Wake a body from sleep.

b2Body_EnableSleep()
Enable or disable sleeping for this body. If sleeping is disabled the body will wake.

From the Box2D reference:

Minimal repro project:

Sleep.zip (4.6 KB)

Maybe these two Box2D functions have been mixed up?
No.

It’s easy to check:

Quick fire response!

Hm, any idea what could be causing this then?

Searching further, you’ll find this, which I’m currently reading to understand:

1 Like

I believe it’s related to dynamic scaling of object.
Since Box2d doesn’t have that concept in V2 (and our V3 implementation is modeled after V2), we need to wake up the object if we alter the transform. And then allow it to go to sleep again.

Perhaps there is another way to do this though. I’ll read some more.

1 Like

If the body needs waking after being scaled, wouldn’t it be better to use b2Body_SetAwake() instead, since it’s a one-shot, rather than a persistent flag?

So this:

                    if (dp > POS_EPSILON || fabsf(da) > ROT_EPSILON)
                    {
                        b2Vec2 b2_position;
                        ToB2(position, b2_position, scale);

                        b2Rot b2_rot = b2MakeRot(angle);
                        b2Body_SetTransform(body_id, b2_position, b2_rot);
                        b2Body_EnableSleep(body_id, false);
                    }
                    else
                    {
                        b2Body_EnableSleep(body_id, true);
                    }

Could be changed to:

                    if (dp > POS_EPSILON || fabsf(da) > ROT_EPSILON)
                    {
                        b2Vec2 b2_position;
                        ToB2(position, b2_position, scale);

                        b2Rot b2_rot = b2MakeRot(angle);
                        b2Body_SetTransform(body_id, b2_position, b2_rot);
                        b2Body_SetAwake(body_id, true);
                    }

And then the body would respect the EnableSleep flag afterwards automatically, because it was never used.

Precisely!
That’s my intended fix, for both Box2d V2 and V3.
I’m not sure why I didn’t do that 6 years agod, so I’ll have to double check :thinking:

I should be able to make a fix to beta today.

1 Like

Ha ha. Fantastic!

In the meantime, as a workaround: If you don’t need dynamic scaling, you can turn it off with physics.allow_dynamic_transforms = 0

1 Like