c_trace ohne Ende

Top  Previous  Next

C_trace ist eine wirklich nützliche Anweisung! Sie kommt jedes Mal zur Anwendung, wenn eine Ihrer Entities (der Spieler, ein Monster, ...) prüfen möchte, ob eine andere Entity sich in Sicht befindet. Möchten Sie wissen, ob der Spieler den Gegner trifft? Benutzen Sie c_trace! Wollen Sie prüfen, ob Gegner 1 und Gegner 2 sich sehen können? Dann benutzen Sie einfach c_trace zwischen ihren Positionen! Schauen wir uns den allgemeinen Aufruf an:

 

c_trace(start, target, mode);

 

C_trace sendet einen unsichtbaren Strahl von “start” bis “target” und prüft, ob dieser unterwegs ein Hindernis trifft oder nicht. Die Anweisung ist dabei sehr benutzerfreundlich: Sie geben einfach den Startpunkt und den Zielpunkt an, sowie den Modus, in dem c_trace arbeiten soll. Ich könnte zum Beispiel player.x als Startpunkt und enemy.x als Zielpunkt festlegen, was dann einen unsichtbaren Strahl vom Spielermodel zum Gegnermodel schickt (die Zeiger “player” und “enemy” müssen natürlich vorher definiert werden).

 

shot_0

 

Ob Sie es glauben oder nicht, dies ist ein Screenshot unseres Testlevels! Kopieren Sie den gesamten “workhop26” Ordner in Ihren 3DGS Ordner, öffnen Sie work26.wmp und starten Sie das Level zusammen mit script26.wdl. Mit den Tasten “Z” und “X” können Sie dann die graue Wand bewegen und mit der Leertaste feuern; es wird dann ein Text angezeigt, entweder “Missed the enemy!” oder “Got the enemy!”, abhängig von der Position der Wand (Sie können den Gegner übrigens auch durch Löcher in der Wand treffen).

 

Schauen wir uns das Skript an:

 

w26_1

 

Das sieht etwas kompliziert aus, aber Sie sollten keine Probleme damit haben, wenn Sie die vergangenen Workshops durchgearbeitet haben. Schauen wir uns die wichtigen Teile an...

 

var video_screen = 1; // start the engine in full screen

 

Diese Codezeile macht genau, was der Kommentar sagt: die Engine wird im Vollbildmodus gestartet und nicht im (voreingestellten) Fenster. Beachten Sie, dass im Vollbild nicht alle Fehlermeldungen angezeigt werden können, kommentieren Sie diese Zeile für Debugging-Zwecke also am besten aus.

 

entity* enemy;

 

action first_enemy

{

       enemy = my; // I'm the enemy!

}

 

Die obigen Zeilen erzeugen einen Entity Zeiger namens “enemy” und weisen diesen unserem Gegner zu, dem Typen im blauen Outfit. Damit ist Folgendes gemeint: wenn Sie die action mit Namen “first_enemy” im WED dem entsprechenden Model zuordnen (was ich getan habe), kann das Skript auf seine Daten mit Hilfe des “enemy” Zeigers zugreifen.

Sie haben vielleicht bemerkt, dass ich die Text Definition und die main Funktion nicht erläutert habe; diese kennen Sie ja bereits. Und falls Sie sich nicht genau erinnern: wenn wir keine Schriftart für den Text festlegen, wird die vordefinierte _a4font verwendet. Nun wieder zu dem spannenden Teil:

 

action moving_wall // don't move pieces of level geometry this way unles you have to!

{

       while (1)

       {

               if (key_z == on) {my.y += 5 * time;} // "Z" was pressed? Then move the wall to the left

               if (key_x == on) {my.y -= 5 * time;} // "X" was pressed? Then move the wall to the right

               wait (1);

       }

}

 

Diese Action wird der grauen Wand (einer wmb Entity) zugewiesen, die mit den Tasten “Z” und “X” bewegt werden kann. Ich verwende eine “while”-Schleife; wird die “Z”-Taste gedrückt, bewegt sich die Wand nach links und mit der “X”-Taste entsprechend nach rechts. Um die Wand zu bewegen greife ich direkt auf ihre y-Koordinate zu; das ist für einen Test in Ordnung, aber normalerweise sollten Sie Objekte mit c_move bewegen, wenn Sie Kollisionserkennung haben möchten und nicht wollen, dass sie durch Level-Geometrie hindurchgehen. Im 18. Workshop gibt es mehr Informationen über c_move.

 

action players_gun

{

       player = my; // I'm the player

       while (1)

       {

               while (key_space == off) {wait (1);} // wait until the player presses the "space" key

               while (key_space == on) {wait (1);} // wait until the player releases the "space" key

               c_trace (player.x, enemy.x, ignore_me + use_box); // trace from the weapon to the enemy

               if (you == enemy) // got the enemy?

               {

                       status_txt.string = "Got the enemy!";

               }

               else // something else was hit along the c_trace ray?

               {

                       status_txt.string = "Missed the enemy!";

               }

               status_txt.visible = on; // show the text

               sleep (1); // for 1 second

               status_txt.visible = off; // and then hide it

       }

}

 

Sehen Sie sich die Action players_gun gut an – das ist unser erstes Gewehr-Skript. Die erste Codezeile sagt dem Model mit dieser Action (unsere Waffe), dass es von nun als “player” bekannt ist. Vergessen Sie nicht, dass “player” ein vordefinierter Zeiger ist, der nicht von uns deklariert werden muss, wie wir es mit dem “enemy” Zeiger mussten.

 

Was kommt als Nächstes? Eine “while(1)”-Schleife! Diese wiederholen jede Anweisung in den geschweiften Klammern, die ganze Zeit über. Die ersten beiden Zeilen warten bis der Spieler die Leertaste drückt und danach wieder losläßt. Auf diese Weise gibt es bei gedrückter Taste kein Auto-Feuer.

 

               c_trace (player.x, enemy.x, ignore_me + use_box); // trace from the weapon to the enemy

 

Sehen Sie sich die lang erwartete c_trace Anweisung gut an! Wir tracen vom Spieler (der Waffe) zum Gegner (das blaue Model) in einem Modus, der aus ignore_me und use_box zusammengesetzt ist. Welche Modi sind überhaupt möglich?

 

- ignore_me läßt die Engine die Entity ignorieren, die den trace ausführt, in diesem Fall die Waffe. Warum sollten wir das tun?

 

workshop2

 

C_trace macht ja genau, was wir von der Anweisung wollen: es wird vom Ursprung des Player Models (player.x) bis zum Ursprung des Gegnermodels (enemy.x) getracet. Wo ist dann das Problem? Der unsichtbare Strahl wird das Gewehrmodel treffen, weil der Ursprung gewöhnlich im Inneren des Models liegt! Durch “ignore_me” wird der Engine mitgeteilt, dieses Model zu ignorieren; für die Experimentierfreudigen gibt es auch den “ignore_you” Modus, die der Engine sagt, das “you” Model zu ignorieren, zum Beispiel den Gegner.

 

- use_box ist eine weitere wichtige c_trace Option; diese sagt der Engine, dass der Strahl dick sein soll, eher wie ein Rohr mit dem Durchmesser der Spielerentity (dem Gewehr) anstelle eines hauchdünnen Drahtes. Brauchen wir dieses Feature? Natürlich! Ohne den “use_box” Zusatz geht der Strahl durch jedes noch so winzige Loch! Damit könnten Sie einen Gegner töten, indem Sie durch einen Riss in der Wand feuern. Für einige Zwecke ist das vielleicht ein nettes Feature, aber für andere eher nicht, also benutzen Sie use_box je nach Situation.

 

Es gibt noch viele andere Tracing Optionen; sie stehen alle im Handbuch. Viele sind dafür da, bestimmte Arten von Entities zu ignorieren, sofern sie vorhanden sind, zum Beispiel die passierbaren Entities, Map Entities, Models, Sprites und so weiter. Sie könnten also ein Fenster mit einem transparenten Sprite bauen und auf folgende Art durch das “Glas” tracen:

 

c_trace (player.x, enemy.x, ignore_me + use_box + ignore_sprites);

 

der Spieler könnte den Gegner also treffen, selbst wenn ein Sprite zwischen ihnen liegt, weil wir der Engine mitgeteilt haben, Sprites zu ignorieren. Ohne den “ignore_sprites” Modus würde der Strahl am Fenster hängenbleiben und dort stoppen, ohne den Gegner zu treffen.

 

Sehen wir uns die letzten Zeile der Action an:

 

               if (you == enemy) // got the enemy?

               {

                       status_txt.string = "Got the enemy!";

               }

               else // something else was hit along the c_trace ray?

               {

                       status_txt.string = "Missed the enemy!";

               }

               status_txt.visible = on; // show the text

               sleep (1); // for 1 second

               status_txt.visible = off; // and then hide it

 

Wenn der Gegner getroffen wurde (you == enemy), wird der String “Got the enemy!” in unseren status_txt Text geschrieben und für 1 Sekunde angezeigt. Wurde der Gegner nicht getroffen, wird entsprechend der Text “Missed the enemy!” gesetzt und angezeigt. Beachten Sie, dass durch c_trace der vordefinierte “you” Zeiger auf die Entity gesetzt wird, die der Strahl trifft, oder auf “null”, wenn keine Entity getroffen wurde (zum Beispiel ein Level Block); natürlich sollten wir “you” direkt nach der Ausführung prüfen, wie ich es im Beispiel tat; wenn eine “wait” oder “sleep” Anweisung dazwischen kommt, könnte “you” schon wieder in einer anderen Funktion umdefiniert worden sein.

 

Die gesamte Schleife läuft erneut, wenn die Leertaste gedrückt wird, Sie können also eine unbegrenzte Zahl unsichtbarer c_trace “Kugeln” feuern und prüfen ob und wann Sie den Gegner treffen.

 

Wir machen hier eine Pause; der nächste Workshop wird zeigen, wie man eine Player Action mit Animationen, Bewegungscode und Schwerkraft programmiert. Wir benutzen ent_animate, c_move und (natürlich!) c_trace, also lesen Sie nochmal in den Workshops Nummer 18 und 21 nach, bevor wir uns wiedersehen.