This recent article on the Ansca web site about solutions to common physics challenges is helpful, but as one commenter notes, the issue of objects “sticking” to walls isn’t easily solved by using the suggestions provided in the article. The commenter notes that the Corona Physics API doesn’t expose the collision properties required to properly handle these sticky (ha ha) situations. So here’s how you can hack around the problem when your walls are completely horizontal or vertical (situations where the collision is with non-horizontal/vertical objects are somewhat more complicated, and I’ll deal with those in a subsequent blog post).
Here’s a simple
main.lua that creates four walls and a bouncing ball:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
A force is applied to the ball, and when the ball hits the bottom wall, it will ‘stick’, which is not the behaviour that we want. As you’ll see from the output of the
onCollision event handler, eventually, the
y velocity becomes 0, meaning that the object is not moving up or down at all, which creates the effect of sticking.
One other thing you may notice from the terminal output: the
y velocities follow a consistent pattern from the
began phase of the event to the
ended phase: one of the values will flip from positive to negative depending on which way the ball was moving before and after the collision. Here’s how it breaks down:
- Bounce to the left: the
xvalue goes from negative to positive
- Bounce to the right: the
xvalue goes from positive to negative
- Bounce downwards: the
yvalue goes from negative to positive
- Bounce upwards: the
yvalue goes from positive to negative
In the example code above, the
y velocity toggles between 12.73 and -12.73 (I’m rounding off for the sake of convenience). Then, at some point instead of remaining at 12.73, it becomes 0. The ball is moving downward (positive
y velocity) and instead of moving upward (negative
y velocity), it stops moving up/down at all.
So the immediate solution presents itself: we know what the
y velocity should be, so we can override the calculated value of zero and set it to what it should be. Here’s the new
resetLinearVelocity function with the modified
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
onCollision will call
resetLinearVelocity after a delay of 0 milliseconds - all this does is force Corona to call the function on the next frame. We wait a frame so that we don’t interfere with the calculations being performed by the physics engine, and so that we don’t have to wait for another collision before resetting any values that need it (try calling the
resetLinearVelocity function immediately to see the difference).
y velocity is zero, we just need to set them to the signed opposite of their last non-zero value. The
ball object has a couple of new properties,
lastY that stores the values for the
y velocities, respectively, from the previous collision. If either of the new velocities are zero, then we manually set the linear velocity to the proper values.
All of this is being done by the
resetLinearVelocity function. Here’s what it does, in order:
- It gets the current linear velocity values, storing them in local variables.
- It checks to see if the current
yvelocity (stored in
thisY) is 0. If it is, then it's set to the negative value ofball.lastY`.
- The same check is done for the
:setLinearVelocity()is called. If
thisYhave changed, then this means the ball should have bounced after the last collision, but didn’t. This will force the bounce.
yvelocities are stored for the next time there’s a collision.
One inefficiency in the code is that it will call
:setLinearVelocity() even if neither value has changed, so you may want to add some conditional logic to only make that call when necessary - I haven’t tested it, but my suspicion is that a couple of
if statements will have less overhead than the
:setLinearVelocity call, especially in an app with a lot of collisions.
Keep one thing in mind: the physics engine is meant to more or less accurately emulate real-world physics. My guess is that the real problem is that, in real life, the ball is supposed to stop bouncing. The
y velocity is simply not great enough for an object of that (simulated) size to bounce off the wall. For example, if you comment out the
collision event listener and if you play around with the values used in the
:applyForce() function call (e.g.
ball:applyForce(100, 30)), you’ll find that the workaround isn’t even required, and the visual effect of the bouncing ball in this case isn’t significantly different from the lesser initial
y velocity of 10. However, just because Box2D is meant to model the real world, doesn’t mean you have to use it to do that, so hopefully you can use this to get you out of the ‘sticky’ situations you may sometimes find yourself in with the Corona physics engine.