Javascript:球体碰撞和由此产生的冲动

时间:2019-06-21 00:09:49

标签: javascript game-physics trigonometry collision

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

我已经根据行星的密度和位置使重力正常工作了,我的碰撞检测正常工作,但是当它们碰撞时该怎么办呢?

行星不会移动,但是使用Java脚本执行三角函数来计算合成速度非常困难。

这是我的物理代码,通过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计算冲量?

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

2 个答案:

答案 0 :(得分:1)

好的,我认为这可行。

给出:

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

我认为步骤如下:

  1. 计算x轴与行星和球的中心之间的直线之间的角度。
  2. 将速度矢量顺时针旋转此角度,换句话说,将其旋转负角度。现在,有了行星和球在x轴上对齐的速度矢量。
  3. 现在,要在地球上“反弹”,我们只需要反转速度的x分量。
  4. 逆时针旋转新速度。

似乎可行。为了清楚起见,我们假设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分量按比例缩小。

答案 1 :(得分:1)

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

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.