在HTML5 Canvas中的两个或多个对象之间应用重力

时间:2014-08-14 14:46:59

标签: javascript html5 math canvas html5-canvas

我正在创建类似于2D重力模拟器的东西,只是为了好玩,并注意到我在数学方面是一个完全白痴。我无法让重力发挥作用。

我已经尝试按照发现here的说明进行操作,但看起来很奇怪,当距离达到零时,它会完全失灵。如果我按照问题中的建议将1添加到距离,则所有对象都会左上角。当距离达到零时,我甚至尝试不修改重力,但这并没有改变行为。

demonstration of the problem

以下是我用来应用重力的算法:

var distX = obj1.x - obj2.x,
    distY = obj1.y - obj2.y;
if (obj1 != obj2) {
    if (distY != 0) {
        obj1.vy += -(1 / (distY));
    }
    if (distX != 0) {
        obj1.vx += -(1 / (distX));
    }
}

我也尝试过使用其他算法,但大多数算法并不关心对象之间的距离。

请注意,我希望重力影响远距离物体而不是更近的物体。

JSFiddle

3 个答案:

答案 0 :(得分:3)

我们可以使用近似值而不是求解任何方程式。 dv/dt = G*M*m/r^2,但是对于小t,我们可以使用近似值Δv = (G*M*m/r^2)*Δt

当物体碰撞时,我实现了完全非弹性碰撞(见Wikipedia)。这可以防止两个物体之间的距离变小,因此最大力是有限的。

我还将对象位置更改的代码部分移动到单独的循环中,因此为obj1和obj2计算的力大小相等。

Demo

function tick() {
   allObjs.forEach(function (obj1) {
      allObjs.forEach(function (obj2) {
         var diffX = obj2.x - obj1.x,
         var diffY = obj2.y - obj1.y;
         var distSquare = diffX*diffX + diffY*diffY
         var dist = Math.sqrt(distSquare);
         if (obj1 != obj2) {
            if (dist > obj1.w/2 + obj2.w/2) {
               //If you add mass to the objects change to obj2.mass
               //instead of 50
               var totalForce = 50/distSquare;
               obj1.vx += totalForce * diffX / dist;
               obj1.vy += totalForce * diffY / dist;
            } else {
               //Collision has occurred
               //If you add mass to the objects change to
               //tempX = (obj1.mass*obj1.vx + obj2.mass*obj2.vx)/(obj1.mass+
               //obj2.mass);
               //tempY = (obj1.mass*obj1.vy + obj2.mass*obj2.vy)/(obj1.mass+
               //obj2.mass);
               var tempX = (obj1.vx + obj2.vx)/2;
               var tempY = (obj1.vy + obj2.vy)/2;
               obj1.vx = tempX; obj2.vx = tempX;
               obj1.vy = tempY; obj2.vy = tempY;
             }
          }
       });
   });

   allObjs.forEach(function (obj1) {
      obj1.x += obj1.vx / 25;
      obj1.y += obj1.vy / 25;
   });
   stage.update();
}

答案 1 :(得分:0)

牛顿的运动方程F = ma需要在这里解决。您在代码中没有做过类似的事情。难怪它与你的直觉不符。

这将有助于理解物理学。

这是 vector 等式。力是重力,遵循反距离平方定律。

您还知道加速度,速度和位移是如何相关的。你必须知道微积分。

对于您的2D世界,这意味着问题中每个身体的六个方程式。两个物体意味着12个耦合方程。

求解这些方程意味着及时积分所有耦合的常微分方程。您需要了解一些有关数值方法的知识(例如Runga-Kutta 5阶积分与纠错)。

你自己要学会写这样的东西有很多东西。我建议您查看像Box2D这样的JavaScript物理库或Google可能find的其他内容。

答案 2 :(得分:0)

尝试

                    var distX = obj1.x - obj2.x,
                        distY = obj1.y - obj2.y;
                    var rsq = distX *distX + distY * distY;
                    var r = Math.sqrt(rsq);
                    var F = 50 / rsq;        // constant chosen to be pleasing
                    var rhat_x = distX / r;
                    var rhat_y = distY / r;
                    var Fx = F * rhat_x;
                    var Fy = F * rhat_y;

                    obj1.vx += -Fx;
                    obj1.vy += -Fy;
                    obj2.vx += Fx;
                    obj2.vy += Fy;

这是非常基本的,它没有考虑到质量,它使用最简单的方法求解方程式,你应该真正使用像5阶Runga-Kutta w /纠错的东西。但它确实使用了引力公式

 F = - G m1 m2 / r^2

其中G是万有引力常数,m1 m2是两个质量(我所有这些都是1!)r ^ 2是物体之间距离的平方。力是朝向另一个物体的方向,让它成为单位向量rhat所以力的向量版本,使用1作为常量

 F = - ( 1 / r^2 ) rhat

上面给出了合理的结果,你开始用

createPlanet(50, 200, 2, 0, 1);         
createPlanet(400, 200, 2, 0, -1);      

你必须注意两颗行星不要太靠近,否则加速度会变得无穷大,速度会变得太大。

在玩的时候我试过

var distX = obj1.x - obj2.x,
    distY = obj1.y - obj2.y;
var rsq = distX *distX + distY * distY; // square of the distance
var r = Math.sqrt(rsq);
var Fx = distX / r;
var Fy = distY / r;
obj1.vx += -Fx;
obj1.vy += -Fy;
obj2.vx += Fx;
obj2.vy += Fy;

给出令人愉悦但物理上不正确的结果。