Tworzyłem coś takiego jak symulator 2D grawitacyjnego, tylko dla zabawy i zauważyłem, że jestem kompletnym idiotą pod względem matematyki. Po prostu nie mogę uzyskać ciężkości do pracy.

Próbowałem śledzić instrukcje znalezione Tutaj , ale wygląda dziwnie, a gdy odległość osiąga zero, idzie całkowicie wózek. Jeśli dodam 1 na odległość zgodnie z zaleceniami w pytaniu, wszystkie obiekty przechodzą w lewo. Próbowałem nawet modyfikować grawitację, gdy odległości osiągnęły zero, ale to nie zmienia zachowania.

demonstration of the problem

Oto algorytm, którego używam, aby zastosować grawitację:

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

Próbowałem też korzystać z innych algorytmów, ale większość z nich nie dba o odległość między obiektami.

Należy pamiętać, że chcę grawitację wpłynąć na odległe obiekty mniej niż bliższe obiekty.

JSFiddle

3
Tiago Marinho 14 sierpień 2014, 18:46

3 odpowiedzi

Najlepsza odpowiedź

Zamiast rozwiązywać wszelkie równania, możemy użyć przybliżenia. dv/dt = G*M*m/r^2, ale dla małych t moglibyśmy użyć przybliżenia Δv = (G*M*m/r^2)*Δt.

Kiedy obiekty zderzają się, wdrożyłem doskonale nieelastyczny kolizja (patrz Wikipedia). Zapobiega to odległości między dwoma obiektami z bycia małym i dlatego maksymalna siła jest ograniczona.

Przesunąłem również część kodu, w którym pozycja obiektu jest zmieniana na oddzielną pętlę, więc siły obliczone dla OBJ1 i Obj2 są równe wielkości.

Próbny

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();
}
4
Afsa 14 sierpień 2014, 22:27

Próbować

                    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;

Jest to bardzo proste, nie biorąc pod uwagę masę, wykorzystując najprostszy możliwy sposób rozwiązywania równań, które powinieneś naprawdę używać czegoś takiego jak 5 rzędu Runga-Kutta W / Błąd korekcji. Ale używa formuły do grawitacji

 F = - G m1 m2 / r^2

Gdzie g jest uniwersalną stałą grawitacyjną, M1 M2 są dwiema masami (mam wszystkie z nich do 1!) R ^ 2 jest kwadratem odległości między obiektami. Siła jest w kierunku drugiego obiektu, niech będzie to wektor jednostkowy rhat więc wersja wektorowa siły, przy użyciu 1 dla stałych

 F = - ( 1 / r^2 ) rhat

Powyższe daje rozsądne wyniki, które zaczynasz

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

Musisz dbać o to, że dwie planety nie mają zbyt blisko lub przyspieszenie wychodzi do nieskończoności, a prędkości są zbyt duże.

Podczas gry próbowałem

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;

Co daje przyjemne, ale fizycznie nieprawidłowe wyniki.

1
Salix alba 14 sierpień 2014, 17:04

Równania Newtona z ruchu F = ma należy rozwiązać tutaj. W swoim kodzie nie robisz nic takiego. Nic dziwnego, że nie pasuje do twojej intuicji.

Pomogłoby zrozumieć fizykę.

To jest równanie wektor . Siła jest grawitacja, która podąża za odwrotną powierzchnią kwadratową.

Wiesz, w jaki sposób powiązane są przyspieszenie, prędkość i przemieszczenie. Musisz znać bliczkę.

W przypadku twojego świata 2D oznacza to sześć równań dla każdego ciała w tym problemie. Dwa ciała oznacza 12 równań sprzężonych.

Rozwiązywanie tych równań oznacza integrację wszystkich połączonych równań różniczkowych w czasie. Musisz wiedzieć coś o metodach numerycznych (np. Runga-Kutta 5th Integracja z korekcją błędów).

Masz wiele do nauczenia się pisać takiej rzeczy samodzielnie. Polecam patrząc na bibliotekę fizyki JavaScript, jak Box2D lub coś innego, że Google może Znajdź.

-1
duffymo 14 sierpień 2014, 15:13