5
\$\begingroup\$

I am creating a Breakout game using Box2D (of LibGdx if anyone interest). Everything works well until when the ball hits the wall when moving with a very small angle. Please look at the image for details:

enter image description here

I tried to set the wall friction to 0 and restitution to 1, as well as the ball's friction and restitution, but it still move along the wall (I have no World Gravity). This also happens to the vertical wall if the angle is small enough, it will lose X velocity.

How can I move the ball the way I expected? If there is no friction, what caused the problem?

EDIT: In case of LibGdx only, this is a fix:

World.setVelocityThreshold(float threshold);

I set it to 0.1f, and it helped. If you encounter problem, maybe 0 can help, but it is unrecommended..

\$\endgroup\$
2
  • \$\begingroup\$ your conversation with zee, made me thinking... are you just applying an impulse in one frame, and let the ball move on it's own for the rest of game, or are you applying the force every frame? \$\endgroup\$
    – Ali1S232
    Commented Apr 23, 2015 at 9:02
  • \$\begingroup\$ I only apply impulse when the ball hits the paddle, because I have no world gravity. \$\endgroup\$
    – Luke Vo
    Commented Apr 23, 2015 at 9:05

2 Answers 2

4
\$\begingroup\$

What's happening

First of all, note that this behavior is not specific to oblique collisions. Below is an example of two balls colliding head on with a wall. All fixtures have m_restitution = 1.0, and m_friction = 0.0. The ball on the left is traveling with v = 0,1 and the ball on the right has v = 0,1.01.

enter image description here

I found three similar questions asked on the box2d forums a while back, which suggest lowering a value in b2Settings.h, b2_velocityThreshold.

It didn't take me long to find the code block where b2_velocityThreshold is referenced. It turned out to be line 211 of the file b2ContactSolver.cpp, and inside the function b2ContactSolver::initializeVelocityConstraints:1

It is here that the velocity bias factor is set based on whether or not the relative velocity exceeds b2_velocityThreshold. That velocity bias factor is then referenced in the iterated function b2ContactSolver::SolveVelocityConstraints. When the bias velocity is not set (defaults to zero), the initial accumulated impulse is becomes just enough to counteract the initial momentum of the ball, and it sticks to the wall.

I have tried to understand why the b2_velocityThreshold exits in the first place (many web searches) and haven't come up with a definitive answer. The best I can do is quote Erin Catto (author of box2d) who posted in one of the above threads:

If you set the [b2_velocityThreshold] to zero, non-zero restitution will cause your bodies to bounce forever.

So I'm extrapolating that this behaviour is implemented for the sake of games with bouncing balls, where balls with restitution 0<e<1 would otherwise never stop bouncing (even though the bounce heights would eventually become very small). This would waste tons of cpu time creating/deleting the contact between the two bodies, and in addition the bouncing body would never sleep.

Recommended Solutions

Solution 1
The recommendation from the above forum threads is to reduce the parameter in b2Settings.h:

#define b2_velocityThreshold 1.0f

A velocity threshold for elastic collisions. Any collision with a relative linear velocity below this threshold will be treated as inelastic.

Unfortunately, this parameter is a #define constant and is therefore not editable unless box2d is re-compiled. I'm assuming that you don't wan to re-compile libgdx to do this.

Solution 2
Zee was on the right track with his answer, but it needs a little more detail. A good approach to doing this is using collision callbacks as you suggest. You sound like you know what you're doing with this one, so I won't go into detail here.

Solution 3
This one's a bit of a trick, and I'm only recommending this because you say you're working on a breakout clone, which only has a couple of moving parts.

You should leave almost everything as is, but increase your ball speed by a factor of s where s>1. If s is high enough, then very oblique collisions will still have a significant relative velocity and will therefore trigger the velocity bias-setting else branch in the referenced code, and your ball will almost always bounce. The problem is that now your game is impossible because the ball is so fast and the player cannot keep up. To compensate for this, just multiply your time-step by a factor of 1/s before calling b2World::Step. That way box2d will technically be operating in slow motion, but since your ball has been sped up by the same amount, everything will look normal.

Here are 2 more gifs. The left one shows a ball with v=1,1 and time step size ts = dt. The second is with v=2,2 (magnitude doubled), and time step size ts = 0.5*dt. Voila:

wall_stick wall_bounce

I hope this helps!

1 This is the only place I could find where that threshold parameter is referenced. (i.e. it has the same effect for all contact types, regardless of the shapes involved in the collision)

\$\endgroup\$
4
  • \$\begingroup\$ Only nitpicking: wouldn't a vector u with double the magnitude of v=1,1 be u=2,2? After all, sqrt(2² + 2²) = 2 * sqrt(1² + 1²). \$\endgroup\$
    – Mutoh
    Commented Apr 23, 2015 at 20:58
  • \$\begingroup\$ Wow, your third solution is great! One question though, instead of scaling the time step, what do you think about scaling the whole world instead? I am using the constant WORLD_TO_BOX = 100f (100 camera pixel = 1 Box2D World meter) and can just increase/decrease that constant to render the needed size. Since I do not feel very safe about the scaling the world would be better? \$\endgroup\$
    – Luke Vo
    Commented Apr 23, 2015 at 20:59
  • 1
    \$\begingroup\$ @DatVM That could work, but you'd need to be careful about the limits of box2d. See the box2d manual section 1.7 \$\endgroup\$ Commented Apr 23, 2015 at 21:35
  • 1
    \$\begingroup\$ Also a solution for LibGdx is to set the VelocityThreshold, it is possible for LibGdx. (I added to my question) \$\endgroup\$
    – Luke Vo
    Commented Apr 24, 2015 at 18:41
1
\$\begingroup\$

I am not familiar with libgdx, so I apologize if this might be a bit out of place; but don't you need a script for the wall class? Right now it seems the wall is simply acting like it should react and do nothing to affect the ball's movement, so the ball will continue going in the same direction on the black arrow.

Make it such as that if the wall is hit, it will multiply by -1 the ball's Y axis vector component. And if it hits any of the right / left walls, multiply by -1 the X axis component.

\$\endgroup\$
5
  • \$\begingroup\$ Your solution seems nice. Yes I can make that happen in the Contact Callback. However, I am really curious about what cause that problem? Shouldn't the physical interactions themselves be like the blue arrow? \$\endgroup\$
    – Luke Vo
    Commented Apr 23, 2015 at 7:29
  • \$\begingroup\$ You said you were moving the ball. Say you are translating the ball on a (1,1) vector, it means that in every frame, the ball is moved according to the vector (1,1). No matter what the collision is, after the frame is done, on the next one the ball will only move that way. If you want physics to control the occurence of things, I think you should be using an impulse/initial force on the ball, and stop controlling its movement every frame, maybe then, the scenario will go as you expect it. Mind that I might be wrong, I really never used Box2D. \$\endgroup\$
    – Zee
    Commented Apr 23, 2015 at 7:39
  • \$\begingroup\$ No, I don't move the ball manually. I used Force to move it. \$\endgroup\$
    – Luke Vo
    Commented Apr 23, 2015 at 7:48
  • \$\begingroup\$ I do not know then. :) I apologize, hopefully someone who knows more than me about Box2D physics logic can help you out. \$\endgroup\$
    – Zee
    Commented Apr 23, 2015 at 7:52
  • \$\begingroup\$ I tried your solution, but I don't know why after the physic step, it set the velocity to 0 again (exactly 0f). Guess I will have to wait for another solution then. \$\endgroup\$
    – Luke Vo
    Commented Apr 23, 2015 at 8:04

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .