Apply gravity between two or more objects in HTML5 Canvas

I was creating something like a 2nd gravity simulator just for fun, and I noticed that I was a complete idiot from a math standpoint. I just can't get gravity to work.

I tried following the instructions I found here but it looks weird and when the distance reaches zero it completely fails. If I add 1

to distance as recommended in the question, all objects go in the top left corner. I've even tried not to change gravity when the distances reach zero, but that doesn't change the behavior.

demonstration of the problem

Here's the algorithm I'm using to apply gravity:

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));
    }
}

      

I've tried other algorithms too, but most of them don't care about the distance between objects.

Note that I want gravity to affect distant objects less than nearer objects.

JSFiddle

+3


source to share


3 answers


Instead of solving any equations, we could use an approximation. dv/dt = G*M*m/r^2

, but for small t we could use the approximation Δv = (G*M*m/r^2)*Δt

.

When objects collide, I have implemented a completely inelastic collision (see Wikipedia ). This prevents a small distance between two objects and therefore the maximum force is limited.

I also moved the part of the code where the position of the object was changed in a separate loop, so the forces calculated for obj1 and obj2 are equal in size.



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();
}

      

+2


source


Here you need to solve Newton's equations of motion F = ma

. You are not doing anything like this in your code. Unsurprisingly, this doesn't match your gut.

This will help you understand physics.

This is a vector equation. Force is the force of gravity that follows the square of the inverse distance law.

You also know how acceleration, velocity and displacement are related. You must know calculus.



For your 2D world, this means six equations for each body in the problem. Two bodies mean 12 related equations.

Solving these equations means integrating all related related ordinary differential equations over time. You will need to learn a thing or two about numerical methods (for example, 5th order Runga-Kutta integration with bug fixes).

You will have a lot to learn how to write this yourself. I would recommend looking into a JavaScript physics library like Box2D, or whatever Google can find .

0


source


Try

                    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;

      

It is very easy without considering its use, using the simplest possible way to solve equations you really should be using, like the 5th order Runga-Kutta fix. But he uses the formula for gravitational

 F = - G m1 m2 / r^2

      

where G is the universal gravitational constant, m1 m2 are two masses (for me they are all equal to 1!) r ^ 2 is the square of the distance between objects. The force is in the direction towards another object, let it be a unit vector rhat

, so the vector version of the force using 1 for constants

 F = - ( 1 / r^2 ) rhat

      

The above results give reasonable results that you start with

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

      

you need to take care that the two planets don't get too close or the acceleration goes to infinity and the speeds get too high.

During the game, I tried

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;

      

which gives pleasant but physically incorrect results.

0


source







All Articles