Waffen - Teil 3

Top  Previous  Next

In diesem Monat werden wir einen Granatwerfer erstellen. Wie sieht die Flugbahn einer Granate aus? Hier ist ein Bild von mir in der Armee, wie ich eine werfe; wie Sie sehen bin ich ein großer und gutaussehender Typ. :)

 

aum60_workshop1

 

Einige mögen jetzt vielleicht einwenden, dass ich nur eine Zwiebel werfe, aber glauben Sie mir, auch eine Zwievel kann tödlich sein, vor allem wenn man sie isst ohne dass eine Scheibe Brot in der Nähe ist...

 

Wie Sie sehen bewegt sich die Granate in gerader Richtung vom Spieler weg und ändert dabei ihre Höhe, während sie vom Boden abprallt. Sieht theoretisch nicht schwer aus und ist sogar noch einfacher zu programmieren mit Hilfe des “bounce” Vektors. Sehen Sie selbst im Skript nach oh, ich sollte vielleicht erwähnen, dass das Hauptskript und das Skript für den Spieler nahezu identisch zu denen von letztem Monat sind, daher schauen wir uns nur das neue weapons3.wdl Skript an.

 

aum60_workshop2

 

Nichts neues, oder? Ich benutze eine Action, die beinahe aufs Haar derjenigen für das Maschinengewehr aus AUM 58 gleicht (mit anderem Namen natürlich). Da unser Granatwerfer keine Auto-Feuer Funktion haben soll, habe ich die entsprechenden Stellen etwas modifiziert: wenn der Spieler die linke Maustaste drückt, ermitteln wir den Startpunkt der Granate (der 37. Vertex des Granatwerfer Models), erstellen dort das Model für die Granate (grenade.mdl) mit der Action move_grenade und dann ertönt das Geräusch grenade.wav mit einer Lautstärke von 100.

 

Schauen wir uns die Neuheiten an.

 

aum60_workshop3

 

Die erste Codezeile von move_grenade setzt den Skill99 der Granate auf 1234; auf diese Weise können wir später feststellen, ob unsere Gegner mit einer Granate oder einer anderen scannenden Entity in Berührung gekommen sind. Wie Sie sehen übernimmt die Granate den Pan und Tilt Winkel von der Kamera, daher bewegt sie sich in die Richtung, in die der Spieler schaut.

 

Die Granate reagiert auf Kollisionen mit anderen Entities und Level Blocks, woraufhin die Event Funktion (bounce_grenade) aufgerufen wird. Ich verwende Skill1 bis Skill3 als einen einzelnen, lokalen Vektor, in dem die Geschwindigkeit der Granate gespeichert wird; in Skill1 steht die horizontale Geschwindigkeit (100 * time_step), in Skill2 die Geschwindigkeit für Seitwärtsbewegungen (0) und in Skill3 die vertikale Geschwindigkeit (10 * time_step im Moment).

 

Die folgende “While”-Schleife sorgt dafür, dass die Granate sich für 3 Sekunden bewegt, wobei die vertikale Geschwindigkeit (Skill3) fortwährend verringert wird. Das Ergebnis der c_move Anweisung (die im letzten Frame zurückgelegte Distanz) wird hierbei in Skill4 abgelegt.

 

Schauen wir uns eine vereinfachte Version der Schleife mal an:

 

my.skill10 = 0;

while (my.skill10 < 24) // this loop will run for 24 seconds

{

       my.skill10 += time_step / 16;

       wait (1);

}

 

Die Grundlagen sind schnell verstanden: wir erhöhen Skill10 bis dieser 24 erreicht und die Schleife abbricht. Aber woher wissen wir, dass die Schleife genau 24 Sekunden laufen wird? Zunächst stellen wir sicher, dass Skill10 auf 0 gesetzt wird, bevor die Schleife beginnt. Das wird der Fall sein, wenn Skill10 bisher nicht benutzt wurde, aber falls er an anderer Stelle schon vorkam, muss er vorher zurückgesetzt werden.

 

Was ist time_step? Diese Variable gibt die Länge des letzten Frames in Ticks an, wobei 1 Tick einer Sechzehntel Sekunde entspricht. Bei einer Framerate von 16 Frames per Second ist time_step also 1, bei 32 fps ist time_stop 0,5 und so weiter. Es gilt immer: time_step * frame rate = 16. Wenn ich also time_step / 16 zu Skill10 addiere, dann entspricht das 1 / 16 bei einer Framerate von 16 fps, 1 / 32 bei 32 fps und 1/ 94 bei 94 fps und so weiter. Aber dies wird einmal pro Frame gemacht, also wird Skill10 pro Sekunde genau um 1 erhöht: bei einer Framerate von 16 kommt 16 Mal pro Sekunde 1 / 16 hinzu, bei einer Framerate von z.B. 63 kommt einmal pro Frame, also 63 Mal pro Sekunde 1 / 63 hinzu. Diese Methode ist nicht sehr präzise, da sich time_step von einem Frame zum nächsten ein wenig ändern kann, aber in den meisten Fällen funktioniert es gut.

 

Da wir nun wissen, warum die Granate für eine festgesetzte Zeit lebt, schauen wir uns den Code erneut an. Sobald diese Sekunden abgelaufen sind, wird die Granate unsichtbar und passierbar gemacht (das Model wird verborgen), wir erzeugen ein Explosionssprite, lassen ein Geräusch ertönen und scannen für 2 Sekunden in einem Umkreis von 200 Quants. Die letzte Codezeile entfernt die Granate komplett aus dem Level.

 

Beachten Sie, dass ich Skill10 vor der zweiten Schleife auf 0 zurückgesetzt habe; ohne diese Maßnahme stünde Skill10 noch auf 3 von der vorigen Schleife und die c_scan Schleife wäre überflüssig. Mit der c_scan Anweisung sollten Sie sich mittlerweile auskennen; in AUM 54 steht noch mehr darüber.

 

Wenden wir uns nun lieber “bounce” zu. Dies ist ein Vektor, der bei einer Kollision oder von c_trace automatisch gesetzt wird und er zeigt die Richtung an, in die eine Entity von der Oberfläche abprallen würde. Wenn Sie jemals einen Arkanoid oder Breakout Klon gespielt haben, dann wissen Sie, wie der Ball dort von den Wänden prallt genauso funktioniert der “bounce” Vektor in 3DGS, alle benötigten Koordinaten werden automatisch berechnet. Großartig, oder?

 

Ich habe den Granatwerfercode so modifiziert, dass eine Partikelspur die Bouncewinkel nach einer Kollision anzeigt so sieht es dann aus:

 

aum60_workshop5

 

In meinem Beispiel kommt die Granate von links, trifft auf den Boden, wobei der bounce Vektor gesetzt wird und fliegt dann weiter nach rechts. Sobald die Granate mit dem Boden kollidiert, wird bounce.x auf die korrekte Vorwärtsrichtung, bounce.y auf die Seitwärts- und bounce.z auf die richtige Vertikalrichtung gesetzt. Wir müssen nur die Granate entsprechend ausrichten, indem Pan und Tilt angeglichen werden und dann funktioniert es! Warum ist das so? Naja, die Granate bewegt sich ja in die Richtung, welche durch Pan und Tilt gegeben ist, weil wir bei c_move den Geschwindigkeitsvektor relativ zur Ausrichtung angegeben haben.

 

Da wir all das jetzt wissen, können wir die bounce_grenade() Funktion beruhigt anschauen:

 

aum60_workshop4

 

Sie erinnern Sich vielleicht, dass wir die zurückgelegte Distanz  in Skill4 speichern; die Eventfunktion stellt sicher, dass die Granate ihre Richtung nur dann mit Hilfe des “Bounce” Vektors ändern soll, wenn die zurückgelegte Distanz größer als 5 Quants pro Frame ist, d.h. die Granate bewegt sich noch mit einer akzeptablen Geschwindigkeit. Dies soll verhindern, dass die Granate bei geringen Geschwindigkeiten wild auf einem Fleck hüpft, wodurch die Landung natürlicher wirkt. Ist die Geschwindigkeit groß genug, bewirkt die Anweisung “vec_to_angle (my.pan, bounce)”, dass die Pan und Tilt Winkel und damit die Bewegungsrichtung dem “Bounce” Vektor angepasst werden, genau wie im Bild.

 

Jede Kollision verringert die Horizontalgeschwindigkeit aus Skill1; wenn die Granate mit 100 * times_step beginnt, bewegt sie sich nach der ersten Kollision nur noch mit 100 * 0,75 * time_step und nach der zweiten mit 100 * 0,75 * 0,75 * time_step usw.

 

Falls die Kollision mit einer ebenen Oberfläche stattfand (bounce.z != 0), wird das Vorzeichen der Vertikalrichtung geändert, wodurch die Granate vom Boden fort nach oben prallt. Dies geschieht, weil wir Skill3 mit einem negativen Wert multiplizieren, was das Vorzeichen ändert. Falls die Vertikalgeschwindigkeit unter 0,01 fällt, stoppt die Granate endgültig, indem Skill1, Skill2 und Skill3 zurückgesetzt werden.

 

Nicht einschlafen! Wir müssen nur noch zwei Funktionen betrachten:

 

aum60_workshop6

 

Diese Funktion ist für das Explosionssprite zuständig: es wird passierbar gemacht, etwas über dem Boden plaziert und skaliert; einige Flags sorgen für besseres Aussehen, der Ambientwert wird auf 100 gesetzt, ein zufälliger Rollwinkel wird gewählt (wodurch Explosionen jedes Mal etwas anders aussehen), es wird transparent gemacht, durchläuft einmal die Animation und am Ende wird das Sprite dann entfernt.

 

Ok, nun haben wir einen Granatwerfer, der prallende, explodierende Granaten feuert... was fehlt noch? Genau! Einige Dummy Gegner, die auf c_scan reagieren.

 

aum60_workshop7

 

Die Gegner reagieren auf c_scan Anweisungen mit ihrer got_scanned() Event Funktion. Diese prüft, ob die scannende Entity eine Granate ist (also ob Skill99 = 1234 ist. Jetzt sehen Sie, warum unsere Granaten ihren Skill99 auf 1234 gesetzt haben, denn im Level könnten andere c_scan Anweisungen vorkommen (z.B. um eine Tür zu öffnen), denn wir wollen ja nicht, dass andere harmlose c_scans irgendwen umbringen.

 

Wenn also Skill99 auf 1234 steht, hören die Gegner auf, auf Events zu reagieren (sie müssen nicht mehrmals sterben) und zeigen dann ihre “death” Animation, an deren Ende sie passierbar werden.

 

Ich hoffe, Sie hatten Spaß mit diesem Artikel, ich hatte ihn beim Schreiben auf jeden Fall! Bis in einem Monat dann!