Puzzle: Ball in a box - why does the rebound fail.

edited July 2016 in General Discussion

This is a bit of fun but it demonstrates what can go wrong with even the simplest code.

In the sketch below, the ball is traveling at a constant 500 pixels a second and is bouncing inside a rectangle. Try running the sketch for several minutes and eventually you should see something strange happening, be patient it took about 10 minutes on my computer.

If you didn't see it on your computer watch this video, I promise you it is exactly the same sketch, no tricks.

So your mission, if you choose to accept it is to explain what is going wrong. :)

I will provide the answer if no one works out what is happening.

float ballSize = 20;
float ballX, ballY, ballVX, ballVY;
float speed = 500;
float left, right, top, bottom;
int currTime, prevTime;  // milliseconds
float elapsedTime;       // seconds

void setup() {
  size(600, 400);
  left = 40; 
  right = width-40; 
  top = 40; 
  bottom = height-40;
  rectMode(CORNERS);
  ballX = random(left + 2 * ballSize, right - 2 * ballSize);
  ballY = random(top + 2 * ballSize, bottom - 2 * ballSize);
  float angle = random(TWO_PI);
  ballVX = speed * cos(angle);
  ballVY = speed * sin(angle);
  // This should be last line in setup
  currTime = prevTime = millis();
}

void draw() {
  // Using elapsed time between frames for accuracy
  currTime = millis();
  elapsedTime = (currTime - prevTime) / 1000.0;
  prevTime = currTime;  // set it up for the next frame
  updateBallPosition(elapsedTime);
  rebound();
  // Draw the frame
  background(255);
  stroke(0);
  strokeWeight(2);
  fill(250);
  rect(left, top, right, bottom);

  fill(0, 140, 0);
  ellipse(ballX, ballY, 2 * ballSize, 2 *ballSize);
}

void updateBallPosition(float et) {
  ballX += ballVX * et;
  ballY += ballVY * et;
}

void rebound() {
  if (ballX - ballSize < left || ballX + ballSize > right)
    ballVX *= -1;
  if (ballY - ballSize < top || ballY + ballSize > bottom)
    ballVY *= -1;
}

Answers

  • Answer ✓

    ballVX *= -1;

    this is always asking for trouble.

    if < left then VX = positive
    if > right then VX = negative 
    

    spliting the two conditions is much safer

  • line 16 is also wrong - initial Y defined in terms of left / right not top / bottom

  • Answer ✓
    void rebound() {
      if (ballX - ballSize < left || ballX + ballSize > right) {
        ballVX *= -1;
        updateBallPosition(elapsedTime);
      }
      if (ballY - ballSize < top || ballY + ballSize > bottom) {
        ballVY *= -1;
        updateBallPosition(elapsedTime);
      }
    }
    
  • edited July 2016

    1 of my old online examples in which I re-invoke some update function right after any bounce:
    http://studio.ProcessingTogether.com/sp/pad/export/ro.989GaZC5t7EkE

    void move() {
      x += sx;
      y += sy;
    }
    
    void bounce() {
      if (x > width  - r | x < r) {
        sx *= -1.;
        move();
      }
    
      if (y > height - r | y < r) {
        sy *= -1.;
        move();
      }
    }
    
  • edited July 2016 Answer ✓

    The good old stuttering in the reverse zone of the ball ....

    *-1 is doing this (when it is applied multiple times in a row)

    better use ballVX=abs(ballVX); OR ballVX = -1 * abs(ballVX); etc.

  • line 16 is also wrong - initial Y defined in terms of left / right not top / bottom

    My mistake, I should avoid cut and paste - I have corrected the original code - thanks @koogs

  • i only noticed because the very first time i ran it the ball started under the box

  • You all got part of the answer the full story can be read here

  • wouldn't it look better to reflect the position on the ball in the wall and negate the velocity?

    so if the wall is at x=400 and the ball ends up at x=408 at the end of a step, set the ball to x=392 and xvel to the opposite of the previous xvel. don't change y.

    the corner case is difficult - where it bounces off both x and y in a step, but i'm pretty sure you can just do the x and y calculations sequentially and it'll be ok.

  • Yes that is another (valid) way of doing it and is a slight variation on solution 3. Unless the ball is moving extremely fast you are unlikely to notice the difference between them. All the solutions, including yours are valid is is a matter of personal preference.

Sign In or Register to comment.