workshop 19

Top  Zurück  Weiter

Workshop 19: Die Physik-Engine

In diesem Workshop werden ein bißchen was über die dritte, in der letzten Lektion erwähnte Bewegungsmethode lernen: die auf den Gesetzen der Physik basierende Bewegung. Starten Sie SED und öffnen Sie die Datei script19.c. Starten Sie das Projekt und Sie finden sich in einem Spiele-Level wieder, in welchem Sie über die Pfeiltasten einen Ball steuern können.

w19_1

Ziel dieses Spieles (ja, es ist ein Spiel!), ist es, den Ball solange zu bewegen, bis er die kleine Plattform oben im Level erreicht. Drücken Sie auf die Pfeiltasten und Sie sehen, daß der Ball sich bewegt, als wirke eine unsichtbare Kraft auf ihn. Nun, es wird tatsächlich eine unsichtbare Kraft auf ihn angewandt!

Da es sich hier um ein spielbares Game handelt, sollte sein Quellcode ziemlich groß sein, richtig?

///////////////////////////////////////////////////////////////
#include <acknex.h>
#include <default.c>
/////////////////////////////////////////////////////////////// VECTOR ball_force; ENTITY* ball; function main() { level_load("roller.wmb"); // lade das Level ball = ent_create ("ball.mdl", vector(-400, 0, 100), NULL); // erstelle den Ball ph_setgravity (vector(0, 0, -386)); // setze die Schwerkraft phent_settype (ball, PH_RIGID, PH_SPHERE); // setze den Typus der Physik-Entity phent_setmass (ball, 3, PH_SPHERE); // und ihre Masse phent_setfriction (ball, 80); // setze die Reibung phent_setdamping (ball, 40, 40); // setze die Dämpfung phent_setelasticity (ball, 50, 20); // setze die Elastizität while (1) { ball_force.x = 180 * time_step * (key_cur - key_cul); // bewege d. Ball mithilfe d. Pfeiltasten ball_force.y = 180 * time_step * (key_cuu - key_cud); // 180 setzt die x- / y-Bewegungsgeschwindigkeiten ball_force.z = 0; // Bewegen auf der vertikalen Achse ist nicht nötig phent_addtorqueglobal (ball, ball_force); // füge d. Ball ein Drehmoment hinzu camera.x = ball.x - 300; // halte d. Kamera 300 Quants hinter d. Ball camera.y = ball.y; // verwende dasselbe y wie mit d. Ball camera.z = 1000; // und plaziere sie bei z = 1000 Quants camera.tilt = -60; // lasse sie nach unten schauen wait (1); } }

Können Sie das glauben? Wir brauchen lediglich ungefähr 20 Codezeilen und alles spielt sich innerhalb der Main-Funktion ab! Nun, das ist das, was ich das 'lite' von Lite-C nenne!

Wie Sie sehen, haben wir einen VECTOR namens ball_force definiert. Ein VECTOR ist wie eine var, enthält aber 3 Werte: x, y und z. Darin werden wir die Rotations-Kraft des Balls auf den x-, y-, und z-Achsen speichern. Dann haben wir noch einen Entity-Pointer mit dem Namen ball definiert, der für unseren Ball benutzt werden wird.

Ein Level, welches in WED erstellt wurde, roller.wmb, wird geladen. Dann erstellen wir den Ball im Level, und zwar bei x = -400, y = 0, z = 100, wofür wir die Datei ball.mdl verwenden. Wir haben einen namens ball definierten Entity-Pointer und das ist der Name, der unserer neu erstellten Entity gegeben wird.

Die folgenden Codezeilen melden unsere Ball-Entity bei dem Physik-System an und setzen ihre Eigenschaften. Grübeln Sie nicht, wir werden diese eine nach der anderen durchgehen.

- ph_setgravity(vector(0, 0, -386)) setzt die globale Schwerkraft für unseren Ball. Normale Werte für den Schwerkraftsvektoren beinhalten x = 0, y = 0 und ein auf einen negativen wert gesetztes z, was dafür sorgt, daß das Objekt nach unten fällt. Ersetzen Sie doch mal, einfach nur zum Spaß, -386 durch 100 und schauen Sie, was passiert.

- phent_settype (ball, PH_RIGID, PH_SPHERE) sagt unserem Ball, daß er eine Physik-Entity werden soll und zwar mit einem festen Körper (Wasseroberflächen lassen sich auch erstellen) und einer Kollisionshülle, welche sich wie eine Kugel verhält. Wie Sie sehen, setzt die Form unseres Modells nicht automatisch das zur ihr passende physikalische Verhalten. Würde ich meine Kugel earth.mdl einfach durch einen Würfel ersetzen, würde der nagelneue Würfel sich weiter drehen und rollen als wäre er eine Kugel, denn phent_settype hat ihm gesagt, daß er das tun soll. Alles klar?

- phent_setmass (ball, 3, PH_SPHERE) legt die Masse unserer Kugel auf 3 kg (6.6 Pfund) fest.

- phent_setfriction (ball, 80) setzt den Reibungskoeffizienten auf 80. Der Wert kann sich von 0 (wie auf Eis) bis hin zu 100 (einer Reibung wie auf Gummi) erstrecken.

- phent_setdamping (ball, 40, 40) setzt sowohl die lineare, als auch die winkelgemäße Dämpfung auf 40. Dies ist eine einfache und doch effektive Methode Luftwiderstand und Reibung zu simulieren, ohne dieses bremsende Dämpfen würde sich unser Ball unendlich weiterdrehen.

- phent_setelasticity (ball, 50, 20) setzt den Abprallfaktor unseres Balls auf 50 und seine Mindestgeschwindigkeit auf 20. Was hat es mit dieser Mindestgeschwindigkeit auf sich? Nun, hat der Ball bei der Kollision mit irgendetwas eine Geschwindigkeit von unter 20, wird sein Abprallfaktor auf Null gesetzt - auf diese Weise lassen sich diese vielen kleinen und unnützen Abpralleffekte vermeiden.

Ich weiß, hier haben wir jede Menge neuer Informationen, wenn Sie mit all den numerischen Werten aber ein wenig herumspielen, werden Sie deren Sinn und Zweck sehr schnell begreifen. Kümmern wir uns doch gleich noch um den while-Loop:

while (1)
{

       ball_force.x = 180 * time_step * (key_cur - key_cul);
       ball_force.y = 180 * time_step * (key_cuu - key_cud);
       ball_force.z = 0;

Diese Codezeilen setzen je nach Status der Cursor- bzw. Pfeiltasten (auf der Tastatur) die passenden Werte von ball_force.x und ball_force.y. key_cur ist die vordefinierte Variable, die den Status der rechten Pfeiltaste annimmt, key_cul den der linken usw.. Betrachten wir uns als Beispiel nun einmal die möglichen Werte für ball_force.x in Abhängigkeit von key_cur und key_cul:

a) ball_force.x = 0 wenn keine der beiden Tasten gedrückt ist, denn key_cur = 0, key_cul = 0, also folgt daraus 180 * time_step * (0 - 0) = 0; der Ball bekommt keine Kraft.

b) ball_force.x = 180 * time_step wenn der Player die rechte Pfeiltaste drückt, denn key_cur = 1, key_cul = 0, also wird 180 * time_step mit (1 - 0) multipliziert, der Ball bekommt eine positive Kraft.

c) ball_force.x = -180 * time_step wenn der Player die linke Pfeiltaste drückt, denn key_cur = 0, key_cul = 1, also wird 180 * time_step mit (0 - 1) multipliziert, der Ball bekommt eine negative Kraft.

d) ball_force.x = 0 wenn der Player rechte und linke Pfeiltaste gleichzeitig drückt, denn key_cur = 1, key_cul = 1, also ist der Multiplikationsfaktor (1 - 1) = 0, der Ball bekommt keine Kraft.

Dasselbe passiert bei der drauffolgenden Codezeile, die für die Kraft in y-Richtung zuständig ist. Indem wir die x- und y-Kräfte des Balls miteinander kombinieren, können wir jedmöglichen Punkt im Level erreichen. Diese Art der Operation (Taste_a - Taste_b) ist eine sehr häufig vorkommende Sache und wird in vielen Spielen zum Steuern der Player-Bewegung etc. benutzt. - In AUM finden Sie eine Menge an ähnlichen Beispielen.

Sie sehen, daß sich der Ball überhaupt nicht auf der z-Achse bewegt. Mit einer ähnlichen Codezeile können Sie, wenn Sie wollen, dafür sorgen, daß der Ball sich je nach Status von zwei anderen Tasten auf der Tastatur nach oben und unten bewegt. Werfen wir einen Blick auf die nun folgende Codezeile innerhalb unserer Schleife:

       phent_addtorqueglobal (ball, ball_force);

Die Anweisung phent_addtorqueglobal weist einer Entity ein Drehmoment (Winkel-Kraft) zu. In unserem Fall wird die vom Vektor namens ball_speed angegebene Kraft der Entity namens ball zugewiesen und sorgt so dafür, daß der Ball sich dreht, wenn der Player die Pfeiltasten drückt.

Wenn Sie sich noch an unsere time_step Erklärung in Workshop 10 erinnern, wundern Sie sich vielleicht, warum wir die Winkelkraft mit time_step multiplizieren. Das macht die Kraft um so kleiner, je höher die Framerate ist. Wir haben verstanden, dass eine zurückgelegte Distanz pro Frame von der Framerate abhängen sollte, aber eine Kraft? In diesem Fall auch. Wir wenden diese Kraft in jedem Frame innerhalb der while Schleife an. Unser Ball bekommt also jeden Frame einen kleinen Schubs. Wenn wir ihn jedesmal mit der gleichen Kraft schubsen würden, dann würde er bei einer hohen Framerate schneller rollen, denn er bekommt dann mehr Schubse. Wir gleichen dies per time_step aus, indem wir die Kraft abhängig von der Framerate reduzieren.

       camera.x = ball.x - 300;
       camera.y = ball.y;
       camera.z = 1000;
       camera.tilt = -60;

Die letzten paar Codezeilen in der Schleife setzen eine für die Kamera (unser "Auge" im Level) passende Position. Sie plazieren die Kamera 300 Quants hinter dem Ball auf der x-Achse während sie zu jeder Zeit dasselbe y beibehält. Und schließlich befindet sich die Kamera bei 1000 Quants auf der z-Achse und schaut nach unten, denn ihr tilt-Winkel ist auf -60 Grad gesetzt.

Zum Schluß noch ein Rat: arbeiten Sie möglichst mit kleinen Leveln, kleinen Entities etc. - die Physik-Engine mag das. Wenn Sie sich an die passenden Proportionen halten, sehen kleine Level kein bißchen kleiner aus als die Größeren!

Ich hoffe, Sie haben Lust, noch mehr über die Physik-Engine zu lernen. Lesen Sie das Gamestudio-Handbuch, dort finden Sie weitere verfügbare Funktionen. Und vor allem - machen Sie Experimente!

 

Weiter: Sprites. Animierte Sprites