We are going to create a class in P5JS to contain all the functionality for a ball. It will be responsible for movement and drawing the ball on the canvas. Each ball has a position vector, a velocity vector and a radius which are all initialised in the constructor.
class Ball {
constructor(x, y, vx, vy, r) {
this.pos = createVector(x, y);
this.vel = createVector(vx, vy);
this.r = r;
}
update() {
this.pos.add(this.vel);
}
show() {
stroke(0);
fill(127);
strokeWeight(2);
circle(this.pos.x, this.pos.y, this.r * 2);
}
}
The update() function is responsible for moving the ball. The first line simply adds the velocity vector to the position vector. If we left the function like that then the ball would eventually travel off the canvas. We can’t have the ball bounce off the wall because that would not preserve the momentum of the ball (momentum is a vector quantity). So when the ball has fully disappeared from view, we want to magically wrap it around to the opposite wall so it continues its path as if nothing happened! Like the old arcade game ‘Asteroids’.
In the diagram below, ball A is moving to the right. It will fully disappear from the window when its x position, plus its radius is greater than the width of the canvas. When this happens we want to change the “x” coordinate to be -r so that direction of travel remains the same and the ball reenters the canvas from the left.
Similarly, ball B is moving to the left, so we want it to wrap to the right wall when its “x” coordinate is less that -r. The same logic applies to wrapping in the “y” dimension.
To achieve this we need a new function to handle this wrapping behaviour. We would like to pass the function the minimum and maximum values for our coordinate, as well as a coordinate value. If the coordinate is between the minimum and maximum then it is just returned as is. If it is outside the allowable range, then the value is “wrapped” around.
Many programming languages have a modulo function to help with this, but unfortunately Javascript doesn’t, so we need to do some extra work.
Here is the wrap() function:
function wrap(n, min, max) {
var divisor = max - min
n = n - min
var modulo = ((n % divisor) + divisor) % divisor
return modulo + min
}
With the help of this we can modify the update() function to handle the wrapping of the ball when it passes out of view…
update() {
this.pos.add(this.vel);
var x = wrap(this.pos.x, -this.r, width+this.r)
var y = wrap(this.pos.y, -this.r, height+this.r)
this.pos.set(x, y)
}
This approach ensures that whenever the ball disappears off the side of the canvas, it will reappear again in a predictable position. But the velocity (and therefore the momentum and energy) do not change.