Array of Bouncing objects

I'm trying to make a simple version of pong. My code works when I simply draw a single ellipse and code it to bounce off of the Paddle (rectangle), but when I create an array of balls (ellipses) the balls no longer bounce off of the paddle. I'm not sure where I'm going wrong, I thought I followed the P5.js intersecting objects tutorial pretty well. Again I want any Ball from the array to bounce off of the Paddle, if the objects intersect.

var gravity = 0.1;
var balls = [];
var n = 0;


function setup() {
  createCanvas(400, 400);
  Paddle = new Paddle(180, height / 4 * 3);
  for (n = 0; n < 7; n++) {
    append(balls, new Ball(random(width), random(height)));
  }
}

function draw() {
  background(51);
  Paddle.display();
  Paddle.borders();

  for (n = 0; n < balls.length; n++) {
    balls[n].display();
    balls[n].move();
  }

for (i = 0; i < balls.length; i++){
 if (balls[i].intersects(Paddle)) {
   balls[n].bounce();
 } 
}

}

function mouseClicked() {
  if (n < 9 && mouseY < height - 50) {
    append(balls, new Ball(mouseX, mouseY));
  }
}

function keyPressed() {
  if (keyCode === RIGHT_ARROW) {
    Paddle.move(1);
  } else if (keyCode === LEFT_ARROW) {
    Paddle.move(-1);
  }
}

function Paddle(x, y) {
  this.x = x;
  this.y = y;
  this.r = 25;

  this.move = function(dir) {
    this.x += dir * 15;
  }
  this.borders = function() {
    if (this.x > width)
      this.x = 0;
    if (this.x < 0)
      this.x = width;
  }
  this.display = function() {
    stroke(0, 255, 0);
    fill(0, 255, 0);
    rectMode(CENTER);
    rect(this.x, this.y, this.r * 4, this.r * 2)
  }
}

function Ball(x, y) {
  this.x = x;
  this.y = y;
  this.r = 25;
  this.yspeed = 0.1;
  //  this.toBounce = false;

  this.move = function() {
    this.y += this.yspeed;
    this.yspeed += gravity;

    if (this.y > height) {
      this.y = height;
      this.yspeed *= -1
    }
    if (this.y < 0) {
      this.y = 0;
      this.yspeed *= -1
    }
  }
    this.intersects = function(Paddle) {
    var d = dist(Ball.x, Ball.y, Paddle.x, Paddle.y);
    if (d < Ball.r + Paddle.y) {
      return true;
    } else {
      return false;
    }
  }
  this.bounce = function() {
    this.y = this.y *= -1
    this.yspeed *= -1;
  }

  this.display = function() {
    stroke(255)
    fill(255, 0, 0);
    ellipse(this.x, this.y, this.r * 2, this.r * 2);
  }
}

Answers

  • edited January 2017

    line 26

    balls[n].bounce();

    that n is 0. Should change it to i?

    Don't use global variables as the for-loop iterator and you won't run into these kinds of problems so easily

  • Thanks I did not notice that, however it does not fix the issue.

  • Sorry I can't run your code at the moment. However, do you need line 97? I would think you just need to change the speed orientation aka. sign. The value of y gets updated in your move() function.

    Kf

  • Well for a single ellipse to bounce off of the Paddle I believe I had to add it, as I tried without.

  • Answer ✓

    @wjsandbe - there are a lot of errors in your code :/

    In the context of your specific problem the following line is the main cause:

    var d = dist(Ball.x, Ball.y, Paddle.x, Paddle.y);

    To calculate distance based on the current ball's position that should be:

    var d = dist(this.x, this.y, Paddle.x, Paddle.y);

    ...but there's a lot of other stuff wrong - e.g.

    this.bounce = function() {
        this.y = this.y *= -1// think about what this actually does!
        this.yspeed *= -1;
      }
    

    You also do some very strange things like:

    Paddle = new Paddle(180, height / 4 * 3);

    Here you've overwritten the Paddle constructor with a reference to your Paddle instance. It looks like you can get away with it in this case; but it's not good practice. Also the i variable in your for loop is undeclared so is a global variable - which is a Bad Thing. It should be:

    for (var i = 0; i < balls.length; i++){
        //...
    }
    

    But you don't even need this since it duplicates the preceding loop that uses an n variable (which - as @Sayid mentioned - also happens to conflict with the globally declared n).

    Sorry - I guess this might seem a little disheartening. I'll try and get a mostly working version together and post some code later ;)

  • Thanks, this was very helpful. I'm still learning I'll see if I can clean this up! Appreciate it.

  • Answer ✓

    Here's a more or less working version (it would still benefit from quite a few improvements):

    var gravity = 0.1;
    var balls = [];
    // globally declared variable to host paddle instance
    var paddle;
    // perhaps this is what you meant n to do
    var limit = 20;
    
    function setup() {
      createCanvas(400, 400);
      paddle = new Paddle(180, height / 4 * 3);
      // loops use i by convention
      for (var i = 0; i < 7; i++) {
        append(balls, new Ball(random(width), random(height)));
      }
    }
    
    function draw() {
      background(51);
      paddle.display();
      paddle.borders();
    
       // you only need a single loop to iterate over balls
      for (var i = 0; i < balls.length; i++) {
        balls[i].display();
        balls[i].move();
    
        if (balls[i].intersects(paddle)) {
          balls[i].bounce(paddle);
        } 
      }
    
    }
    
    function mouseClicked() {
      if (balls.length < limit && mouseY < height - 50) {
        append(balls, new Ball(mouseX, mouseY));
      }
    }
    
    function keyPressed() {
      if (keyCode === RIGHT_ARROW) {
        paddle.move(1);
      } else if (keyCode === LEFT_ARROW) {
        paddle.move(-1);
      }
    }
    
    function Paddle(x, y) {
      this.x = x;
      this.y = y;
      this.r = 25;
    
      this.move = function(dir) {
        this.x += dir * 15;
      }
    
      this.borders = function() {
        if (this.x > width)
          this.x = 0;
        if (this.x < 0)
          this.x = width;
      }
    
      this.display = function() {
        stroke(0, 255, 0);
        fill(0, 255, 0);
        rectMode(CENTER);
        rect(this.x, this.y, this.r * 4, this.r * 2)
      }
    }
    
    function Ball(x, y) {
      this.x = x;
      this.y = y;
      this.r = 25;
      this.yspeed = 0.1;
      //  this.toBounce = false;
    
      this.move = function() {
        this.y += this.yspeed;
        this.yspeed += gravity;
    
        if (this.y > height) {
          this.y = height;
          this.yspeed *= -1
        }
        if (this.y < 0) {
          this.y = 0;
          this.yspeed *= -1
        }
      }
    
      this.intersects = function(paddle) {
        // this isn't an accurate hit test: it's more appropriate for testing hits between balls
        // NOT balls and a rectangular object
        var d = dist(this.x, this.y, paddle.x, paddle.y);
    
        // the following conditions can be simplified to:
        // return d < this.r;
        if (d < this.r) {
          return true;
        } else {
          return false;
        }
      }
    
      this.bounce = function(paddle) {
        // reposition ball depending on which side it's on
        // this is a crude - but effective - approach as it checks the ball 
        // direction at the time of impact
        if(this.yspeed > 0) {
           this.y = paddle.y - this.r;
        }
        else {
           this.y = paddle.y + this.r;        
        }
        this.yspeed *= -1;
      }
    
      this.display = function() {
        stroke(255)
        fill(255, 0, 0);
        ellipse(this.x, this.y, this.r * 2, this.r * 2);
      }
    }
    
  • I am curious, why is it bad practice to connect a loop to a global variable? For example I used the variable n so that I could follow the number of the array throughout my code, if it wasn't global I couldn't do this.

    as well on line 22 I used the first loop for the subset function, so that the users cannot add more than 8 balls. When this is taken out the user can continuously add balls, which is not idea, as I link the ball movements to sound so people can compose a song by playing pong.

    Thanks though this was very helpful.

  • Answer ✓

    For example I used the variable n so that I could follow the number of the array throughout my code, if it wasn't global I couldn't do this.

    But in practice what you most likely need to know is balls.length. See how I test against this (line 35) in order to determine whether a new ball can be added.

    Testing against n however could produce inconsistent results: in principle the value of n could be anything between 0 and balls.length when the mouseClicked event is triggered. So even if balls.length were 100 n would sometimes be less than 9, allowing a new ball to be added - probably not what you intended :/

    globals in general are bad practice and should be avoided...

  • I understand. Thanks again you have been very helpful!

Sign In or Register to comment.