1
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var h = canvas.height;
var w = canvas.width;
var sAngle = 0;
var numB = 10; 
var speed = 50;
var dt = 0.01;
const PI = Math.PI;
function resetCanvas () {
  canvas.width = canvas.width;
};
function createBalls (){
for(var i = 1; i <= numB; i++){ 
if (i % 2 == 0) {
  window['ball' + i] = 
  {r:10, color:"white", x:w*Math.random(), y:h*Math.random(), v:speed}} else {
   window['ball' + i] = 
  {r:10, color:"white", x:w*Math.random(), y:h*Math.random(), v:-1 *speed}};
      }
    }
  createBalls();
function drawBalls () {
for (var i = 1; i <= numB; i++) {
ctx.beginPath();
ctx.arc(window['ball' + i].x, window['ball' + i].y, window['ball' + i].r, sAngle, 2*PI);
ctx.fillStyle = window['ball' + i].color;
ctx.fill();
ctx.strokeStyle = window['ball' + i].color;
ctx.stroke();
    }
}
drawBalls();
function moveBalls () {
for (var i = 1; i <= numB; i++) {
if (0 < window['ball' + i].x < w && 0 < window['ball' + i].y < h) 
{window['ball' + i].x = window['ball' + i].x + window['ball' + i].v * dt; 
window['ball' + i].y = window['ball' + i].y + window['ball' + i].v * dt}
if (window['ball' + i].x < 0 || window['ball' + i].x > w) 
{window['ball' + i].x = window['ball' + i].x + ((-1) * window['ball' + i].v * dt); 
window['ball' + i].y = window['ball' + i].y + window['ball' + i].v * dt}
if (window['ball' + i].y < 0 || window['ball' + i].y > h) 
{window['ball' + i].y = window['ball' + i].y + ((-1) * window['ball' + i].v * dt);
window['ball' + i].x = window['ball' + i].x + window['ball' + i].v * dt}
    }
}
function animate () {
resetCanvas();
drawBalls();
moveBalls();
};
setInterval(animate, 100 * dt);

I'm trying to make the balls bounce off the canvas walls in the opposite direction; however, right now they just hit the canvas border and slide to the corners and disappear. Any ideas on how I can improve the if conditions in my moveBall function so that the balls bounce off the canvas walls?

Kol
  • 33
  • 4

2 Answers2

1

Not sure if it's your only problem but your if-statements are not doing what you expect. For example:

if (0 < window["ball" + i].x < w && 0 < window["ball" + i].y < h)

here you seem to want to say 0 < ball.x < w to mean that ball.x is between zero and the screen width. The problem is that this is not how the comparison works. You would need to say:

if (0 < window["ball" + i].x && window["ball" + i].x < w && 
    0 < window["ball" + i].y && window["ball" + i].y < h)

The reason is that the expression 0 < x < w gets executed like (0 < x) < w and the first part (0 < x) returns either true (1) or false (0), and both of those are probably less than w so the expression is always true.

For example, try this snippet:

let x = 7;
console.log("Nope:",x,"is bewteen 0 and 6:",0 < x < 6);
console.log("Yep! ",x,"is between 0 and 6:",0 < x && x < 6); 

So try writing moveBalls like this:

function moveBalls() {
  for (var i = 1; i <= numB; i++) {
    // precompute values used multiple times for easier reading
    const ball = window["ball" + i];
    const offset = ball.v * dt;
    if (0 < ball.x && ball.x < w && 0 < ball.y && ball.y < h) {
      ball.x = ball.x + offset;
      ball.y = ball.y + offset;
    }
    if (ball.x < 0 || ball.x > w) {
      ball.x = ball.x - offset;
      ball.y = ball.y + offset;
    }
    if (ball.y < 0 || ball.y > h) {
      ball.y = ball.y - offset;
      ball.x = ball.x + offset;
    }
  }
}
Always Learning
  • 5,510
  • 2
  • 17
  • 34
  • 1
    Thank you. I tried writing it like this and the balls are still floating to the corners. Maybe its the calculation of the new velocity that's the problem? – Kol Apr 19 '20 at 19:07
0

In addition to what Always Learning said, the edges are not being handled correctly. I'm guessing you want them to bounce?

Let's look at the code:

if (window['ball' + i].x < 0 || window['ball' + i].x > w) {
  window['ball' + i].x = window['ball' + i].x + ((-1) * window['ball' + i].v * dt);
  window['ball' + i].y = window['ball' + i].y + window['ball' + i].v * dt
}

Let's get rid of some of the noise:

if (x < 0 || x > w) {
  x = x + ((-1) * v * dt);
  y = y + v * dt
}

You were right with -1 * v, but this still only moves the ball just inside the border of the canvas again. Next loop it will be inside the border, and act normally, and its velocity will be added again, once again putting it outside the border, once again triggering this code. This effectively "sticks" the balls to the border. When it hits one border, the other coordinate still moves until that one goes outside the border, too. All of the balls end up around 0,0 or at the diagonal opposite of the canvas.

What you want to do here is make the velocity opposite:

if (x < 0 || x > w || y < 0 || y > h) {
  v *= -1
}

This will cause the ball to bounce the opposite direction.

This code still isn't quite perfect, though, because in reality velocity has dimensional components, i.e. an x velocity and a y velocity.

The code is a lot more readable using local variables, and especially using an array for balls. I may come back later and dimensionalize the velocity, but here's what the code looks like now:

const canvas = document.getElementById("canvas");
const height = canvas.clientHeight;
const width = canvas.clientWidth;
canvas.height = height;
canvas.width = width;
const ctx = canvas.getContext("2d");
const h = canvas.height;
const w = canvas.width;
const sAngle = 0;
const numB = 10;
const speed = 50;
const dt = 0.01;
const PI = Math.PI;
const balls = createBalls();

function resetCanvas() {
  canvas.width = canvas.width;
}

function createBalls() {
  const balls = [];
  for (let i = 1; i <= numB; i++) {
    const ball = createBall();
    if (i % 2 == 0) {
      ball.v *= -1;
    }
    balls.push(ball);
  }
  return balls;
}

function createBall() {
  return { r: 10, color: "white", x: w * Math.random(), y: h * Math.random(), v: speed };
}

function drawBalls() {
  balls.forEach(ball => {
    ctx.beginPath();
    ctx.arc(ball.x, ball.y, ball.r, sAngle, 2 * PI);
    ctx.fillStyle = ball.color;
    ctx.fill();
    ctx.strokeStyle = ball.color;
    ctx.stroke();
  });
}

function moveBalls() {
  balls.forEach(ball => {
    const { x, y } = ball;

    if (x < 0 || x > w || y < 0 || y > h) {
      ball.v *= -1
    }
    ball.x = x + ball.v * dt;
    ball.y = y + ball.v * dt
  });
}
function animate() {
  resetCanvas();
  drawBalls();
  moveBalls();
};
setInterval(animate, 100 * dt);

Codebling
  • 10,764
  • 2
  • 38
  • 66