Fahrzeug Physik

Dieses Mal habe ich beschlossen, leicht zu verstehenden Code für ein einfaches Auto oder einen Panzer zu schreiben, der die Physik Engine nutzt. Sie brauchen allerdings die A6 Professional, weil ich 5 Physikobjekte verwende: den Rumpf und die vier Räder. Ich fürchte, dass man kein realistisches Verhalten für ein Auto mit einem einzelnen Physikobjekt (wie in der A6 Commercial) hinbekommt.

Klicken Sie hier für ein kleines wmv Video (Sie benötigen eine aktuelle Version des Windows Media Player).

function main()
{
    time_smooth = 0;
    fps_max = 140;
    fps_lock = on;
    level_load (carph_wmb);
    wait (3);
    ph_setgravity (vector(0, 0, -386));
    ent_create (car_mdl, vector(0, 0, 100), players_car);
    while(player == null) {wait(1);}

Die “time”-Variable wird nicht mehr geglättet (das verhindert heftige Sprünge) und begrenzen die Framerate auf 140. Die Physikengine arbeitet konstant mit 70 fps, also sollte max_fps ein Vielfaches davon sein. Die time-Variable wird auf diese Framerate festgesetzt und dann laden wir das Testlevel. Ich habe die Gravitation auf erdähnliche Verhältnisse eingestellt.

Das Auto wird auf der z-Achse 100 Quants über dem Ursprung erstellt und erhält die Funktion players_car(). Schließlich warten wir noch bis der Spieler existiert.

    while (1)
    {
       motor_left = 0;
       motor_right = 0;
       if(key_cuu == on)
       {
          motor_left = -20;
          motor_right = -20;
       }
       if(key_cud == on)
       {
          motor_left = 20;
          motor_right = 20;
       }
       if(key_cul == on)
       {
          motor_left = 0;
       }
       if(key_cur == on)
       {
          motor_right = 0;
       }

Die ersten zwei Zeilen der Schleife setzen die Motorengeschwindigkeit zurück (nur 2 Variablen, aber warten Sie noch ein wenig); wenn der Spieler die “Pfeil Oben”-Taste drückt, bewegen sich beide Motoren (einer links, der andere rechts). Auch bei der “Pfeil Unten”-Taste bewegen sich beide, allerdings natürlich rückwärts. Mit der “Pfeil Links”-Taste bewegen wir nur den Motor auf der rechten Seite und drehen das Auto damit nach links – dasselbe geschieht analog für die “Pfeil Rechts”-Taste.

       phcon_setmotor (joints[0], vector(motor_right, 10000, 0), nullvector, nullvector);
       phcon_setmotor (joints[2], vector(motor_right, 10000, 0), nullvector, nullvector);
       phcon_setmotor (joints[1], vector(motor_left, 10000, 0), nullvector, nullvector);
       phcon_setmotor (joints[3], vector(motor_left, 10000, 0), nullvector, nullvector);

       camera.x = player.x - 300 * cos(player.pan);
       camera.y = player.y - 300 * sin(player.pan);
       camera.z = player.z + 1000;
       camera.pan = player.pan;
       camera.tilt = -60;

       wait (1);
   }
}

Wir nutzen phcon_setmotor, um die Verbindungen für jedes Rad in motorisierte Verbindungen zu ändern und begrenzen die Kraft (also maximale Geschwindigkeit) auf 10.000. Ich habe einen Vektor namens “joints” (“Verbindungen”) definiert, der vier Elemente enthält – jedes ist eindeutig einem der Räder zugeordnet. Diese Konstruktion sorgt dafür, dass die Räder sich drehen! Die letzten Zeilen sorgen für eine Kamera, die 300 Quants hinter dem Auto und 1000 Quants darüber sitzt und von oben auf den Spieler schaut.

function players_car()
{
    player = my;
    my.scale_x = 1.5;
    my.scale_y = my.scale_x;
    my.scale_z = my.scale_x;
    my.shadow = on;
    phent_settype (my, ph_rigid, ph_box);
    phent_setmass (my, 100, ph_box);
    phent_setgroup (my, 2);
    phent_setfriction (my, 20);
    phent_setdamping (my, 20, 30);
    phent_setelasticity (my, 30, 10);
    ent_create(tyreleft_mdl, vector(my.x - 65, my.y - 43, my.z - 35), cars_tyre);
    ent_create(tyreright_mdl, vector(my.x - 65, my.y + 43, my.z - 35), cars_tyre);
    ent_create(tyreleft_mdl, vector(my.x + 48, my.y - 43, my.z - 35), cars_tyre);
    ent_create(tyreright_mdl, vector(my.x + 48, my.y + 43, my.z - 35), cars_tyre);
}

Die obige Funktion ist dem Rumpf des Autos zugeordnet; sie skaliert das Model, schaltet den Schatten an und sagt der Engine dann, dass der Rumpf fest (“rigid”) ist und für Kollisionszwecke wie eine Box behandelt werden soll. Das Auto wiegt 100 Kilogramm (~220 Pfund), die ID ist 2, der Reibungskoeffizient ist 20 und die Geschwindigkeitsdämpfung beträgt 20 (linear) und 30 (angular).

Mit phent_setelasticity bestimmen wir, inwieweit der Wagen abprallen soll und geben dem Auto dann Räder, denen wir die cars_tyre() Funktion zuweisen.

function cars_tyre()
{
    my.shadow = on;
    my.scale_x = 2;
    my.scale_y = my.scale_x;
    my.scale_z = my.scale_x;
    phent_settype (my, ph_rigid, ph_sphere);
    phent_setmass (my, 10, ph_sphere);
    phent_setgroup (my, 2);
    phent_setfriction (my, 60);
    phent_setdamping (my, 40, 5);
    phent_setelasticity (my, 30, 10);
    joints[index] = phcon_add (ph_hinge, player, my);
    phcon_setparams1 (joints[index], my.x, vector(0, 1, 0), nullvector);
    phcon_setparams2 (joints[index], vector(-360, 360, 0), nullvector, nullvector);
    index += 1;
}

Die Reifen bekommen ebenfalls einen Schatten und werden auf den Wert 2 skaliert. Auch sie sind fest und verhalten sich wie Kugeln mit einem Gewicht von 10 Kilo (~22 Pfund). Auch hier ist die ID 2, der Reibungskoeffizient ist 60, der Dämpfungsgrad 40 bzw. 5 und die Elastizität idz 30 bzw. 10. Mit phcon_add werden die Räder am Rumpf befestigt – die eindeutige Identifikationsnummer für jede Verbindung wird in joints[index] abgelegt, der Ankerpunkt der Verbindung ist der Origin des Rad-Models und die Achse wird die y-Achse (0, 1, 0). Die erlaubten Winkel sind (-360, 360, 0), wodurch keine Begrenzung entsteht. Die letzte Zeile erhöht den Index, damit das nächste Rad die Daten nicht überschreibt.

Das war schon der schwere Teil! Wie Sie sehen ist der Code nicht kompliziert, aber dem Wagen fehlen noch ein paar Features. Die Motoren für dieses Auto sind die gleichen, die man auch für einen Panzer verwenden würde! Ein “echtes” Physik-Auto würde verschiedene Parameter für die Räder hinten und vorn haben, wohingegen alle Räder in meiner Demo die gleiche Funktion verwenden. Daher empfehle ich, dass Sie sich Marcos Code für ein Physik-Auto, der auf der Download-Seite erhältlich ist, genauer ansehen; sein Skript ist 5 mal länger, aber das Auto verhält sich mehr wie ein echtes Auto.

Schauen wir uns noch einige kleine Hilfsfunktionen an:

function flip_it()
{
    phent_addtorquelocal (player, vector((random(-200000) + 100000), 0, 0));
}

on_mouse_right = flip_it;

Diese Funktion wird mit Druck auf die rechte Maustaste aufgerufen; damit können Sie das Auto wieder herumdrehen, wenn es mit allen 4 Reifen in der Luft liegenbleibt.

action barrel
{
    my.shadow = on;
    phent_settype (my, ph_rigid, ph_box);
    phent_setmass (my, 20, ph_box);
    phent_setfriction (my, 50);
    phent_setdamping (my, 50, 50);
    phent_setelasticity (my, 30, 30);
}

Die obige Action wird den Fässern zugeordnet; sie schaltet die Schatten ein und sagt der Engine dann, dass jedes Fass ein festes Objekt ist, dass sich wie eine Box verhält und 20 Kilo wiegt. Auch für die Fässer werden Reibung, Dämpfung und Elastizität auf geeignete Werte gesetzt.
Das Auto bewegt sich mit ungefähr 50 – 60 km/h; Sie können es schneller machen, indem Sie die Kraft, die auf die Räder übertragen wird vergrößern, aber dann sollten Sie auch an den Parametern für Rumpf und Reifen etwas ändern. An dieser Stelle möchte ich noch Marco Grubert danken, der sich die Zeit nahm sich meinen Code anzusehen und hilfreiche Anmerkungen machte. Hier ist eine Liste seiner Ratschläge:
1) Bei der Physikengine ist die Masse immer im Zentrum des Objektes konzentriert, obwohl ein echtes Auto am Boden viel schwerer ist als am Wagendach. Das führt dazu, dass sich das Auto bei hohen Geschwindigkeiten leichter überschlägt, was sich negativ auf die Versicherungsbeiträge auswirkt. Um diesem Verhalten entgegenzuwirken und dem Auto mehr Stabilität zu geben, könnte man den Rumpf des Autos leichter machen als die Räder. Auf diese Weise hat der Wagen einen tiefere Schwerpunkt. Es gibt noch andere Lösungen, wie z.B. das Hinzufügen einer unsichtbaren Physik-Entity unterhalb des Wagens, um den Schwerpunkt zu senken.
2) Zu den globalen Variablen: ph_fps_max_lock ist per Default 70; fps_max sollte ein Vielfaches davon sein, z.B. das Doppelte. Damit wird sichergestellt, dass auf 1 Displayframe immer N Physikframes kommen. Wenn Sie diese Regel nicht beachten, könnte sich das Auto im ersten Frame 1 Einheit bewegen, im zweiten dann 2, im dritten wieder 1 und so weiter. Aufgrund von Rundungsfehlern kann es sein, dass es trotzdem ein wenig dazu kommt, sollten Sie also ein leichtes Stottern in der Bewegung sehen, erhöhen Sie fps_max noch um ein paar weitere Frames. Vergessen Sie nicht, die Framerate mit fps_lock = on festzusetzen. Schließlich wird teim_smooth (Default: 0,5) die obigen Überlegungen durch dauerhaftes Adjustieren der “time” Variablen zunichte machen. Das macht sich vor allem am Anfang bemerkbar, nach 10 Sekunden sollte sich alles stabiliseren. Eine gute Lösung ist ein sehr guter Kameracode, der Sprünge durch Interpolation vermeidet. Eine einfache Lösung ist, time_smooth auf 0 zu setzen.
3) Die Masse eines Physikobjektex sollte sich zwischen 0,1 und 100 bewegen, für mehr Verlässlichkeit. Wählen Sie Werte in dem Bereich und passen Sie dann die Graviation, Dämpfung etc. entsprechend an.
4) Ph_setcorrections sollte möglichst so gelassen werden, wie es ist, weil es alles im Level betrifft. Um das Verhalten der Räder zu ändern, sollten Sie besser phcon_setparams2() benutzen und den dritten Vektor ändern. Dieser ändert die ERP/CFM Werte ebenso wie ph_setcorrections, aber nur für den spezifizierten Constraint. Dies erlaubt es Ihnen, das Verhalten der Räder anzugleichen.