Search code examples
javascriptgame-physicstrigonometrycollision

Javascript: Sphere collisions and resulting impulse


I'm a fairly new programmer, but I'm trying to create my own sort of physics engine without using any libraries. It's for an inter-planetary mini-golf game.

I've gotten the gravity working fine, based on the density and location of the planets, and my collision detection works properly, but what to do when they collide is what I cannot solve.

The planets won't move, but using Javascript to do the trigonometry required to calculate the resulting velocity is proving very difficult.

Here is my physics code, being executed with requestAnimationFrame:

  var a = [0, 0];
  // p is in the format [x, y, radius, density]
  planets.forEach(function (p) {
    var d = Math.sqrt((p[0] - ball.coor[0]) ** 2 + (p[1] - ball.coor[1]) ** 2);
    var m = p[3] * 4 / 3 * Math.PI * p[2] ** 3;
    var f = G * m / d ** 2;
    var angle = Math.atan2(p[1] - ball.coor[1], p[0] - ball.coor[0]);
    a[0] += f * Math.cos(angle);
    a[1] += f * Math.sin(angle);
    if (d < p[2] + ball.radius) {
      var impulse = [0, 0];
      // What to do here?
      // This is the closest I got:
      var velocitya = Math.atan(b.v[1] / b.v[0]);
      var trianglea = Math.abs(velocitya - angle);
      var currV = Math.sqrt(b.v[0] ** 2 + b.v[1] ** 2);
      var newV = currV * bounciness;
      var newa = (Math.PI / 2 - trianglea) * 2 + velocitya;
      b.v[0] = newV * Math.cos(newa);
      b.v[1] = newV * Math.sin(newa);
      // Somehow I just can't get it right.
      // It's the JavaScript, not the math concepts.
    }
  });

  ball.v[0] += a[0];
  ball.v[1] += a[1];
  ball.coor[0] += ball.v[0];
  ball.coor[1] += ball.v[1];

ball is just the object for the golf ball.

I've tried various methods, but I just cannot get the collisions to work properly. It seems working with the directions and angles is giving me grief. I've done a lot of research, but nothing I've found seems to be correctly solving my problem.

So here's my question: How can I calculate the impulse using vanilla JavaScript?

Note: The planets don't move, and the bounciness should be a variable.


Solution

  • Could something like this work? (double check for syntax errors)

    var a = [0, 0];
    // p is in the format [x, y, radius, density]
    planets.forEach(function (p) {
        var d = Math.sqrt((p[0] - ball.coor[0]) ** 2 + (p[1] - ball.coor[1]) ** 2);
        var r = [0, 0]; //radial vector of length one from ball's center to planet's center
        r[0] = (p[0] - ball.coor[0]) / d; // this is the same as cos(angle)
        r[1] = (p[1] - ball.coor[1]) / d; // this is the same as sin(angle)
        // I removed your implementation, it was using redundant expensive calculations
        var m = p[3] * 4 / 3 * Math.PI * p[2] ** 3;
        var f = G * m / d ** 2;
        a[0] = a[0] + f * r[0];
        a[1] = a[1] + f * r[1];
        if (d < p[2] + ball.radius) {
          var dot_v_r = ball.v[0] * r[0] + ball.v[1] * r[1];
          ball.v[0] = bounciness * (ball.v[0] - 2 * dot_v_r * r[0]);
          ball.v[1] = bounciness * (ball.v[1] - 2 * dot_v_r * r[1]);
          // this is complete elastic reflection of the ball, leaving the planet stationary
          // then velocity's magnitude (but not direction) is corrected with bounciness 
        }
    });
    
    ball.v[0] = ball.v[0] + time_step * a[0];
    ball.v[1] = ball.v[1] + time_step * a[1];
    ball.coor[0] = ball.coor[0] + time_step * ball.v[0];
    ball.coor[1] = ball.coor[1] + time_step * ball.v[1];
    
    //time_step is a time step, i.e. how frequently you want to update ball's position and velocity
    //you can choose a value that makes the update of frames appropriate.