Code works with an even array size, but not odd.

edited September 2015 in Questions about Code

The idea behind this sketch is that I have an array of ball objects that bounce off the edge of the window, and off the other ball objects. The code works as expected for arrays of an even length, but when the array length is odd the ball objects just slide out of the window. I know the logic error is occurring in the nested for loop where I call the ball objects bounce method, but I can't figure out why this error happens for the life of me.

Here is the code:

Ball[] balls = new Ball[10]; // Even size only IDK why 
float size = 10; // Any larger and glitches start to happen more often
                 // (balls initial xpos and ypos are too close, thanks be to random)

void setup(){
  size(400,400);
  smooth();
  // Fill our array with random balls
  for(int i = 0; i < balls.length; i++){
    balls[i] = new Ball(random(size,width-size),random(size,height-size),size,random(-2,2),random(-2,2), color(0));
  }
}

void draw(){
  background(255);

  // Cycle through our array and diplay and move all the balls
  for(int i = 0; i < balls.length; i++){
   balls[i].display();
   balls[i].move();

   // Have each ball check where each other ball is
   for(int j = 0; j < balls.length; j++){
     if(i != j){ // Don't bounce off ourself!
       balls[i].bounce(balls[j]);
     }
   }
  }
}

class Ball{
  float xpos;
  float ypos;
  float size;
  float xspeed;
  float yspeed;
  color c;

  Ball(float xpos_, float ypos_, float size_, float xspeed_, float yspeed_, color c_){
    xpos = xpos_;
    ypos = ypos_;
    size = size_;
    xspeed = xspeed_;
    yspeed = yspeed_;
    c = c_;
  }

  void display(){
    fill(c);
    noStroke();
    ellipse(xpos, ypos, size, size);
  }

  void move(){
    xpos += xspeed;
    ypos += yspeed;
  }

  void bounce(Ball otherBall){
    // Bounce off sides
    if(xpos <= size/2 || xpos >= width - size/2){
      xspeed *= -1;
    }
    if(ypos <= size/2 || ypos >= height - size/2){
      yspeed *= -1;
    }

    // Bounce off other balls
    if(dist(xpos,ypos,otherBall.xpos,otherBall.ypos) < size){
      xspeed *= -1;
      yspeed *= -1;
    }
  }
}
Tagged:

Answers

  • edited September 2015 Answer ✓

    In Java, also in C/C++/D, division operations where both operands are of whole datatype, yields a whole datatype too, removing any fractional part of the result.

    However, I feel that a possible fix got nothing to do w/ integer divisions.
    Perhaps just add an extra move() call after a bounce is detected:

    if (xpos < size/2 | xpos >= width - size/2) {
      xspeed *= -1;
      move();  // call it again...
    }
    
  • edited September 2015

    In regards to the division operation, I did catch that my global size variable was declared as an int (now changed to float), but my ball object's size variable is a float variable. I thought floating type variables preserved the fraction part of the division operation, even if my float value is a whole number (the value 10 is stored as 10.0 for floating type correct?).

    Now your suggestion to add a move() call every time a bounce is detected worked. Thank you! However, I am confused as to why this call was not necessary for an array with an even length, but was for an array with an odd length.

    What I ended up doing was separating the bounce method into two different methods. One for bouncing off the sides, another for bouncing off ball objects.

  • Answer ✓

    you have a problem in that your wall bouncing code isn't run when i = j

    in fact it's not dependent on j at all, should be completely separate from the code that checks balls against other balls.

    you're also comparing ball A against ball B and then later in the double loop comparing ball B against ball A. you only need one.

    also, the *= -1 thing with the walls is likely to cause errors. it's better to split the checks and make the direction -ve if it hits the right wall and +ve if it hits the left.

  • (ah, ok, you figured it out as i was typing)

  • I did fix the comparing each ball twice part by changing the if statement from != to <.

    In regards to your comment about the *= -1 with walls, I am applying that operation to my xspeed and yspeed variables which represent the ball objects velocity. This does change the speed to a negative value if the ball hits the right wall, and a positive value if the ball hits the left wall. Although I have noticed that when the speed of the ball object is slowly increased (i.e *= -1.1) it does cause errors. I typically solve this by storing the object's state (i.e. last hit left wall is state 0, last hit right wall is state 1) and apply the velocity according to the state of the object. Is this what you were talking about?

    Thank you for your comments!

  • Pretty much. There are times, especially if you are applying damping on bounces or a recently bounced ball hits another ball, when the resultant velocity isn't enough for it to escape the wall. In which case it'll still pass the test, have its velocity negated and end up behind the line and going the wrong way...

    It's subtle and it's surprisingly common.

  • edited September 2015

    Better to separate the update and display code from the test for collision. The following code will avoid testing collisions against itself and avoid duplicate tests and dispenses with the if statement.

    for(int i = 0; i < balls.length; i++){
       balls[i].display();
       balls[i].move();
    }
    for(int i = 0; i < balls.length-1; i++ ){
       // Have each ball check where each other ball is
       for(int j = i+1; j < balls.length; j++){
           balls[i].bounce(balls[j]);
        }
    }
    
  • But if you do that you have to change the velocity of both the colliding balls.

  • Why should that be a problem, surely it is better to reduce the number of collidion detections. It would mean altering the code in the Ball class so the bounce(...) is split into 2 methods, one to detect a collision, the second to perform the actual rebound calculstions so it becomes something like

    for(int i = 0; i < balls.length-1; i++ ){
       // Have each ball check where each other ball is
       for(int j = i+1; j < balls.length; j++){
           if (balls[i].collides(balls[j])){
               balls [i].rebound (balls [j]);
               balls [j].rebound (balls [i]);
        }
    }
    
  • yeah, i was talking specifically about the ball-on-ball collision.

    and it's not a problem, no, just a reminder 8)

Sign In or Register to comment.