Javascript:球体碰撞和产生的冲量

Javascript: Sphere collisions and resulting impulse

我是一个相当新的程序员,但我正在尝试在不使用任何库的情况下创建我自己的物理引擎。这是一个星际迷你高尔夫游戏。

根据行星的密度和位置,我的重力工作正常,我的碰撞检测工作正常,但当它们发生碰撞时我无法解决。

行星不会移动,但事实证明,使用 Javascript 进行计算最终速度所需的三角函数非常困难。

这是我的物理代码,使用 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只是高尔夫球的对象。

我试过各种方法,但就是无法使碰撞正常工作。似乎与方向和角度一起工作让我感到悲伤。我做了很多研究,但我发现似乎没有任何东西能正确解决我的问题。

所以这是我的问题:如何使用原版 JavaScript 计算冲量?

注:行星是不动的,弹跳应该是个变量。

好的,我认为这可行。

鉴于:

  • 行星位置为 {x,y}
  • 球的位置为 {x,y}
  • 球速为 {dx,dy}

我觉得步骤如下:

  1. 计算 x 轴与行星和球中心之间的线之间的角度。
  2. 将速度矢量顺时针旋转过这个角度,也就是逆时针旋转。现在我们有了速度矢量,就像行星和球在 x 轴上对齐一样。
  3. 现在,要"bounce"离开地球,我们只需要反转速度的 x 分量。
  4. 将新速度 counter-clockwise 旋转角度。

似乎有效。为了清楚起见,我们假设 b(球)、p(行星)和 v(速度)是具有 xy 成员的对象。

function impact(b, p, v) {
    let a = b.x == p.x
            ? Math.PI / 2
            : Math.atan((b.y - p.y) / (b.x - p.x));
    v = rotate(v, -a);
    v.x = -v.x;
    return rotate(v, a);
}

function rotate(p, a) {
    return {
        x: p.x * Math.cos(a) - p.y * Math.sin(a),
        y: p.y * Math.cos(a) + p.x * Math.sin(a)
    );
}

您需要做一些工作才能使用数组等将其转换为您的样式。

如果你想把球放慢一点,你可能也会想出办法;最简单的方法是按比例缩小矢量的 x 和 y 分量。

这样的东西行得通吗? (仔细检查语法错误)

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.