c_trace Reloaded

Top  Previous  Next

Wir haben in Aum 51 damit begonnen, uns das c_trace Kommando anzusehen; dieses Mal werden wir es verwenden, um den Code für eine voll funktionale Spieleraction zu schreiben, die Animationen, Bewegungscode und Schwerkraft enthält. Kopieren Sie das \workshop27 Verzeichnis in Ihren 3DGS Ordner, starten Sie WED und öffnen Sie das Level mit dem Namen work27.wmp, rechnen Sie es aus und starten es mit dem script27.wdl Skript; das folgende Bild begrüßt Sie:

 

w27_02

 

Halten Sie die rechte Maustaste gedrückt, bis Sie das gesamte Level sehen können:

 

w27_03

 

Erraten: Sie können mit der rechten Maustaste heraus und mit der linken Maustaste hereinzoomen. Dieses Feature fand ich wichtig, weil wir ja die Bewegungen des Spielers genauer ansehen wollen. Schauen wir uns das Skript an:

 

w27_01

 

Es sieht etwas kompliziert aus, aber da wir ja begonne haben, C-Skript zu lernen, werden wir keine Probleme haben. Bevor ich anfange, bedenken Sie bitte, dass Sie versprochen haben, den 18. Und den 21. Workshop zu lesen; wir werden die Informationen verwenden. Falls Sie diese Workshops noch nicht kennen, dann ist jetzt Ihre Chance, dies nachzuholen ich laufe auch nicht weg, während Sie das tun, versprochen!

 

function main()

{

       camera.arc = 30; // set the default zoom factor

       level_load (work27_wmb);

       while (player == null) {wait (1);} // wait until the player appears in the level

       camera.x = -650; // set the position of the camera

       camera.y = 0; // on the x, y, z axis

       camera.z = 1000;

       while (1)

       {

               vec_set(temp.x, player.x);

               vec_sub(temp.x, camera.x);

               vec_to_angle(camera.pan, temp); // rotate the camera toward the player

               camera.arc -= 2 * (mouse_left - mouse_right) * time; // zoom in / zoom out

               if (camera.arc < 10) {camera.arc = 10;} // don't allow zoom factors below 10

               if (camera.arc > 100) {camera.arc = 100;} // or over 100

               wait (1);

       }                                

}

 

Sehen wir uns die Funktion “main” an: diese stellt den Zoom (camera.arc) auf einen festen Anfangswert, lädt das Level und wartet bis der “player” Zeiger existiert. Warum sollte man darauf warten?

 

Die Antwort ist ganz einfach: einige Zeilen unterhalb der “while (player == null)” Zeile benutzen einige Anweisungen den “player” Zeiger. Wenn wir nicht warten bis der Zeiger gültig ist (also bis der Spieler im Level geladen ist und seine Action startet), erzeugen diese Anweisungen Fehler, weil sie “player” brauchen und der Zeiger noch nicht auf eine gültige Entity verweist. Zur Veranschaulichung zeige ich mal was geschieht, wenn ich die “while (player == null)” Zeile auskommentiere:

 

w27_04

 

Sehen Sie? Ich werde die Zeile gleich wieder einfügen! Die nächsten drei Zeilen setzen die korrekte Kameraposition auf x, y und z Achse.

 

       camera.x = -650; // set the position of the camera

       camera.y = 0; // on the x, y, z axis

       camera.z = 1000;

 

Sehen wir uns die ersten Zeilen in der Schleife an:

 

       while (1)

       {

               vec_set(temp.x, player.x);

               vec_sub(temp.x, camera.x);

               vec_to_angle(camera.pan, temp); // rotate the camera toward the player

 

Diese Zeilen werden innerhalb der Schleife in jedem einzelnen Frame ausgeführt. Ihr Zweck besteht darin, die Kamera die ganze Zeit auf den Spieler auszurichten, schauen wir uns also an, wie das geht:

 

1) vec_set(temp.x, player.x) kopiert die Koordinaten des Spielers (x, y und z) in die Variable temp; nach dieser Anweisung gilt also temp.x = player.x, temp.y = player.y und temp.z = player.z.

2) vec_sub(temp.x, camera.x) subtrahiert die Position der Kamera von temp und erzeugt somit einen Vektor (in temp), der auf den Spieler zeigt:

 

w27_05

3) vec_to_angle(camera.pan, temp) ist eine wirklich nützliche Anweisung; sie berechnet den pan und tilt Winkel, der durch den Vektor temp gegeben ist und übergibt diese Werte der Kamera, wodurch ihre Ausrichtung bei Bedarf geändert wird. Das bedeutet, dass die Koordinaten von temp sich ändern, wenn der Spieler sich bewegt und die Kamerawinkel automatisch angepasst werden, so dass der Spieler immer in der Bildschirmmitte bleibt.

 

Was genau diese “temp” Variable ist? Das ist eine vordefinierte, globale Variable, die für jeden Zweck genutzt werden kann; wenn Sie eine zweite benötigen, definieren Sie sich diese einfach als globale Variable:

 

var my_temp_variable;

 

Zurück zum Code! Die folgende Zeile gleicht den Zoomfaktor (camera.arc) an, je nachdem ob die linke oder die rechte Maustaste gedrückt wird; die Variablen mouse_left und mouse_right sind vordefiniert und gleich 1, wenn der entsprechende Knopf gedrückt ist, ansonsten haben sie den Wert 0.

 

               camera.arc -= 2 * (mouse_left - mouse_right) * time; // zoom in / zoom out

 

Das bedeutet, dass der Ausdruck (mouse_left mouse_right) die folgenden Werte haben kann:

a) 1, falls der Spieler die linke Maustaste drückt;

b) 0, falls der Spieler keine oder beide Maustasten drückt;

c) -1, falls der Spieler die rechte Maustaste drückt.

 

Wird die linke Maustaste gedrückt und festgehalten, dann verringern wir camera.arc um (-2) * 1 * time = -2 * time in jedem Frame, wodruch hereingezoomt wird. Werden beide Maustasten oder keine gedrückt, ändert sich der Wert von camera.arc nicht und falls die rechte Maustaste gedrückt wird, vergrößert sich camera.arc um (-1) * (-1) * time = 2 * time pro Frame. Ändern Sie den Wert “2”, wenn Sie eine andere Zoom-Geschwindigkeit wünschen.

 

               if (camera.arc < 10) {camera.arc = 10;} // don't allow zoom factors below 10

               if (camera.arc > 100) {camera.arc = 100;} // or over 100

               wait (1);

 

Die obigen Zeilen limitieren camera.arc auf den Bereich zwischen 10 und 100; falls camera.arc unter 10 fällt, wird die Variable auf 10 gesetzt und für 100 entsprechend. Sie können Werte unter 10 und über 100 zulassen, aber bereiten Sie sich auf einige seltsame Effekte vor. :)

 

Soviel zur Funktion main; schauen wir uns die Action für den Spieler an:

 

action players_code

{

       var anim_percentage; // animation percentage

       var movement_speed; // player's movement speed

       var distance_to_ground; // the distance between player's origin and the ground

       player = my; // I'm the player

 

Ich habe drei lokale Variablen definiert: anim_percentage bestimmt den richtigen Animationsframe, movement_speed bewegt den Spieler und distance_to_ground wird verwendet, um den Abstand zwischen dem Origin des Spieler Models und dem Boden darunter zu bestimmen. Die letzte Codezeile sagt der Engine, dass die “player” Entity diejenige mit dieser Action ist.

 

       while (1)

       {

               my.pan += 6 * (key_a - key_d) * time; // rotate the player using the "A" and "D" keys

 

Die erste Codezeile richtet den Spieler korrekt aus, indem der pan durch Druck auf “A” oder “D” geändert wird; my.pan wird um 6 * time erhöht oder verringert je nach Taste. Wenn Sie die 6 durch einen anderen Wert ersetzen, ändern Sie damit die Drehgeschwindigkeit des Spielers.

 

               vec_set (temp.x, my.x); // copy player's position to temp

               temp.z -= 5000; // set temp.z 5000 quants below player's origin

               distance_to_ground = c_trace (my.x, temp.x, ignore_me | use_box);

 

Nun ist es Zeit, c_trace erneut in Action zu sehen! Die drei Codezeilen fügen Schwerkraft hinzu; damit schwebt der Spieler nicht in der Luft und versinkt nicht im Boden, sondern folgt der exakten Form des Bodens, auf dem er läuft. Schauen wir uns an, wie das alles zusammenspielt:

 

1) vec_set(temp.x, my.x) kopiert die Koordinaten des Spielers in temp.

2) temp.z -= 5000 bezeichnet dann eine Position 5000 Quants unterhalb vom Spieler. Wir können hier beinahe jeden großen Wert verwenden, der wichtige Punkt ist nur, dass wir eine Position erzeugen, die auf jeden Fall unter dem Boden liegt.

 

w27_06

 

3) distance_to_ground = c_trace(my.x, temp.x, ignore_me | use_box) führt einen trace zwischen my.x (also player.x) und temp.x aus und liefert den Abstand zwischen dem Origin des Spielers und dem Boden in der Variablen distance_to_ground zurück.

 

Was ist mit diesem “Zurückliefern” gemeint? George, ich bin verwirrt!

 

Ok, lassen Sie es mich erklären. Die meisten C-Skript Anweisungen, wie c_trace, sind in Wahrheit Funktionen und davon gibt es zwei Arten:

a) Funktionen, die nichts zurückliefern, auch bekannt als “void” Funktionen, wie beep(). Sie rufen sie in Ihrem Skript auf und das wars!

b) Funktionen, die etwas zurückliefern, also Funktionen, die eine externe Variable auf einen Ergebniswert setzen können. Zum Beispiel liefert c_move die Distanz zurück, die von der sich bewegenden Entity zurückgelegt wurde und in unserem Fall liefert c_trace die Distanz zwischen den beiden trace Punkten zurück und speichert sie in einer Variablen.

 

               movement_speed.x = 5 * (key_w - key_s) * time; // move the player using "W" and "S"

               movement_speed.y = 0; // don't move sideways

 

Die obigen Zeilen kontrollieren den Spieler; wir erlauben Bewegung mit den Tasten “W” und “S” mit einer Geschwindigkeit von 5. Movement_speed.y ist stets 0, weil wir den Spieler nicht seitwärts bewegen wollen.

 

               movement_speed.z = - (distance_to_ground - 17); // 17 = experimental value

 

In z-Richtung ist movement_speed negativ; wir bewegen den Spieler nach unten und zwar mit der Geschwindigkeit, die durch (distance_to_ground 17) vorgegeben ist. Dies sorgt dafür, dass der Spieler dem Boden verhaftet bleibt, aber wo kommt die 17 her? Jedes Model hat den Origin woanders sitzen; ändern Sie den Wert 17 falls nötig, bis die Füße Ihres Models perfekt auf den Boden abgestimmt sind.

 

               movement_speed.z = max (-35 * time, movement_speed.z); // 35 = falling speed

 

Diese Zeile stellt sicher, dass der Spieler auf realistische Art und Weise fällt. Wenn Sie versucht haben, von der Plattform auf der rechten Bildschirmseite zu springen, haben Sie bermerkt, dass der Spieler schön fällt das liegt an der Codezeile oben. Denn c_trace ist brutal; der Anweisung macht es nichts aus, den Spieler in weniger als 1 Frame auf den Boden zu ziehen, selbst wenn er 1000 Meter fällt! Diese Zeile begrenzt die Fallgeschwindigkeit und stellt sicher, dass sie in jedem Frame größer ist als (-35) * time.

 

Wie “max” funktioniert? Nun, eine Anweisung wie temp = max(-5, 10) würde temp auf 10 setzen, weil 10 das MAXimum der beiden Werte ist, eben der Größere. Die Zeile temp = max(-5, temp) würde also sicherstellen, dass temp nicht kleiner als 5 sein kann. Auf die gleiche Weise stellen wir sicher, dass movement_speed.z nicht unter (-35) * time fallen kann, was im Klartext bedeutet, dass der Spieler nicht plötzlich große Distanzen zurücklegen kann, sondern etwas realistischer fällt, mit der Geschwindigkeit von (-35) * time. Ändern Sie diesen Wert gegebenenfalls, wenn Sie die Fallgeschwindigkeit ändern wollen.

 

               c_move (my, movement_speed.x, nullvector, glide); // move the player

 

Nun ist alles bereit! Alle Komponenten der movement_speed Variablen sind vorbereitet, wir können also jetzt die Entity relativ zu ihrer Ausrichtung bewegen und dabei sicherstellen, dass sie an Hindernissen nicht hängenbleibt, sondern an ihnen entlanggleitet.

 

               if ((key_w == off) && (key_s == off)) // the player isn't moving?

               {

                       ent_animate(my, "stand", anim_percentage, anm_cycle); // play the "stand" animation

               }

               else // the player is moving?

               {

                       ent_animate(my, "walk", anim_percentage, anm_cycle); // play the "walk" animation

               }

               anim_percentage += 5 * time; // 5 = animation speed

               wait (1);

 

Die letzten Zeilen kümmern sich um die Animation; wenn die Tasten “W” oder “S” nicht gedrückt sind (der Spieler sich also nicht bewegt), spielen wir die “stand” Animation; ansonsten wird die “walk” Animation gezeigt. Die Animationsgeschwindigkeit ist 5 * time; ändern Sie diesen Wert entsprechend.

 

Wir haben heute einiges gelernt, nicht wahr? Der nächste Workshop kümmert sich um verschiedene Kameraarten, die wir in unseren Spielen verwenden können!