0

QUESTION: Given the position, velocity, and acceleration of two balls (Ball#1 and Ball#2). Is there a way to figure out when they will collide?

(The acceleration is constant and caused by friction)

WARNING I'm looking for a function that can take an input of two balls and their properties and then return the time they will collide. I'm not looking for the solution of stepping forward a tiny amount every frame and checking for collision. I want to be able to predict collisions very fast.

WHY?: The purpose is to make a highly efficient and "perfect" 8-ball pool physics engine.

NOTE: I already have a collision response system in place. Calculate x/y point that 2 moving balls will collide. However this method breaks down whenever friction is involved.

WHAT I'VE TRIED: I've tried to solve this problem myself and I will do my best to explain my method below.

To start, I took some help from the kinematic equations and created a function that gives me the position of the balls at any specified time. f(t)=ball position at time t

Then I put the functions for the positions of each of the two balls into the distance formula. The result is a new function that can give me the distance between the balls at any specified time. d(t)=distance between balls

The next step would be to check when the distance is 2r.

so, d(t)=2r

However, The function d(t) is not a simple function. It's a quartic function. d(t) = at^4 + bt^3 + ct^2 + dt + e). This means that I would have to solve a quartic equation to find the point of collision.

I know how to solve quartic functions, but I would rather not. (my program (Scratch) has no way of dealing with complex numbers)

Welp, that's it. I'll be here if you have any questions.

Coltroc
  • 1
  • 2
  • ... and why a quartic function? You should have a first-degree function, to which a friction-related function is added. – Dominique Aug 17 '22 at 11:43
  • The distance equation squares the x and y position (d=sqrt(dx^2+dy^2)). So a t^2 becomes a t^4. The kinematic equation for distance travelled looks like this(x=v*t+(a/2)(t)^2). So after being squared it would have a t^4 term. I probably made mistake somewhere in my calculations though... Let me know if I did. – Coltroc Aug 18 '22 at 13:44
  • You seem to mix up regular arithmetic with vector arithmetic. I won't say that it does not work, but you'll need to take into account (and calculate) not only "v" and "a", but also "v_x", "v_y", "a_x" and "a_y". Therefore it's better to understand the movement of the ball as if moving over an axis (like the X-axis). The most important thing you'll need to take into account, is the radius of the ball: I imagine that the distance between the balls is the distance between the centres? (that might be crucial if you have the balls bouncing from the sides) – Dominique Aug 18 '22 at 14:08
  • My explaination was kinda bad. Sorry. I actually have two seperate equations for the balls x and y position. The equation I wrote was only for the x position and it should've looked like this instead. (X=sx+vx(t)+(ax/2)(t)^2) where sx is the start x position, vx the x velocity and ax the x acceleration. And I already have all the values for the balls velocities and acceleration. – Coltroc Aug 18 '22 at 14:47

1 Answers1

0

If I'm understanding correctly you are just looking for how to implement friction into your physics engine. If you are solely looking for "when" they will collide then maybe this guys CodePen will help, otherwise continue on.

Like you said calculate the distance between two balls by getting the distance. If the distance is <= 2r then call a response function. In the response function get the vector between the two balls and normalize it. Calculate the relative speed of the balls and then set their x and y velocity accordingly. In the update function of the class I am multiplying the velocity by 0.993 which is just a number I picked and liked. You can change it to change how much friction there appears to be.

This example take mass into account if you feel you want to use it. For this example the mass is the same for all balls so it's really unnecessary but you may choose to make the cueball a different mass so I added it. This example only shoots the cueball in one direction to keep the code small.

Check out this wiki on Impulse Wikipedia Impulse and here's as good video on 2d collision using it YouTube

Just click anywhere on the canvas

let canvas = document.getElementById("canvas");
let ctx = canvas.getContext("2d");
canvas.width = 400;
canvas.height = 700;
let billiardBalls = [];

class Ball {
  constructor(x, y, color) {
    this.x = x;
    this.y = y;
    this.color = color;
    this.r = 10;
    this.vx = 0;
    this.vy = 0;
    this.mass = this.r * 0.25; //using 1/4th of the radius as mass
    this.speed = 0.5;
  }
  draw() {
    ctx.beginPath();
    ctx.fillStyle = this.color;
    ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2);
    ctx.fill();
  }
  update() {
    this.x += this.vx;
    this.y += this.vy;
    this.vx *= 0.993; //change to alter friction amount
    this.vy *= 0.993; //change to alter friction amount
    this.canvasCollision();
  }
  canvasCollision() {
    if (this.x - this.r <= 0 || this.x + this.r >= canvas.width) {
      this.vx *= -1;
    }
    if (this.y - this.r <= 0 || this.y + this.r >= canvas.height) {
      this.vy *= -1;
    }
  }
}

function createBalls() {
  for (let i = 0; i < 5; i++) {
    billiardBalls.push(new Ball(canvas.width / 2 + 40 - i * 20, 100, "black"));
  }
  for (let i = 0; i < 4; i++) {
    billiardBalls.push(new Ball(canvas.width / 2 + 30 - i * 20, 118, "black"));
  }
  for (let i = 0; i < 3; i++) {
    billiardBalls.push(new Ball(canvas.width / 2 + 20 - i * 20, 136, "black"));
  }
  for (let i = 0; i < 2; i++) {
    billiardBalls.push(new Ball(canvas.width / 2 + 10 - i * 20, 154, "black"));
  }
  billiardBalls.push(new Ball(canvas.width / 2, 172, "black"));
}
createBalls();

let cueBall = new Ball(canvas.width / 2, 450, "white");

function collisionDetection(obj1, obj2) {
  let dist = Math.hypot(obj1.x - obj2.x, obj1.y - obj2.y);
  //if collision is detected send it to the response function
  if (dist <= obj1.r + obj2.r) {
    collisionResponse(obj1, obj2, dist);
  }
}

function collisionResponse(obj1, obj2, dist) {
  //get the vector of the angle the balls collided and normalize it
  let vec = { x: obj2.x - obj1.x, y: obj2.y - obj1.y };
  let vecNorm = {
    x: vec.x / dist,
    y: vec.y / dist
  };
  //get the relative velocity between the balls
  let vecRelVelocity = { x: obj1.vx - obj2.vx, y: obj1.vy - obj2.vy };
  //calc speed after hit
  let speed = vecRelVelocity.x * vecNorm.x + vecRelVelocity.y * vecNorm.y;
  if (speed < 0) {
    return;
  }
  //change vx and vy for each object
  let J = (2 * speed) / (obj1.mass + obj2.mass);
  obj1.vx -= J * obj2.mass * vecNorm.x;
  obj1.vy -= J * obj2.mass * vecNorm.y;
  obj2.vx += J * obj1.mass * vecNorm.x;
  obj2.vy += J * obj1.mass * vecNorm.y;
}

canvas.addEventListener("click", (e) => {
  cueBall.vy -= 12; //change this to see the difference in how hard it hits
  //too fast can cause issues with collision detection
  //this is only for this simple example
});

function animate() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.fillStyle = "green";
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  cueBall.draw();
  cueBall.update();
  for (let i = 0; i < billiardBalls.length; i++) {
    billiardBalls[i].draw();
    billiardBalls[i].update();
    collisionDetection(cueBall, billiardBalls[i]);
    for (let j = i + 1; j < billiardBalls.length; j++) {
      collisionDetection(billiardBalls[i], billiardBalls[j]);
    }
  }
  requestAnimationFrame(animate);
}
animate();
<canvas id="canvas"></canvas>
Justin
  • 2,873
  • 3
  • 9
  • 16
  • This is a great simulation. And I learned a lot about impulses from the wikipedia page (had no idea what it was). Btw, does the codepen thingy account for friction? – Coltroc Aug 18 '22 at 14:39
  • The Codepen doesn't account for friction. Seems like the person that made it was just calculating time of collision. If you are already having the balls collide in your engine then is there an update function where you can multiply the velocity x and y by a given amount like in the example above? – Justin Aug 18 '22 at 15:36
  • Yes! this is already implemented (though my friction is a constant force instead of a percentual decrease in velocity). The problem is that when predicting future collisions between two moving balls the game is wrong. (the future prediction assumes the velocities are constant, like the Codepen thingy) – Coltroc Aug 18 '22 at 17:49