Events

Top  Previous  Next

Jede Entity kann so eingestellt werden,dass sie auf gewisse Events (Ereignisse) reagiert: ihre Kollision mit einem Level Block, einer anderen Entity und so weiter. Warum sollten wir uns mit diesen Events befassen? Vielleicht möchten wir Code für einen Gegner schreiben, der umfällt, wenn der Spieler auf ihn feuert. Der Gegner wäre ein Model, die Kugel ebenfalls und wenn diese Models kollidieren, dann fällt der Gegner um, ganz einfach.

 

Bevor ich beginne, beachten Sie bitte, dass der Code aus diesem Workshop nur mit Acknex 6.4 oder neueren Versionen funktioniert. Öffnen Sie work29.wmp im WED und starten Sie das Level mit script29.wdl als Skript; Sie werden folgendes Bild sehen:

 

w29_01

 

Sie sehen mehrere Sprites, die über einigen Entities plaziert sind. Jede Entity demonstriert ein oder mehrere Events in Aktion; darauf kommen wir gleich zu sprechen. Sehen wir uns zunächst die main Funktion an:

 

function main()

{

       mouse_range = 5000;

       level_load (work29_wmb);

       while (player == null) {wait (1);} // wait until the player is created

       camera.tilt = -15; // look down at the player, play with this value

       mouse_map = cursor_tga;

       mouse_mode = 2;

       while (1)

       {

               camera.x = player.x - 250 * cos(player.pan);

               camera.y = player.y - 250 * sin(player.pan);

               camera.z = player.z + 150; // place the camera above the player, play with this value

               camera.pan = player.pan; // the camera and the player have the same pan angle

               mouse_pos.x = pointer.x;

               mouse_pos.y = pointer.y;

               wait (1);

       }

}

 

Die erste Codezeile stellt die Reichweite von Events ein, die mit der Maus ausgelöst werden können (durch Anklicken einer Entity oder indem der Mauszeiger darüber plaziert wird); wir setzen diese auf 5000 Quants. Wenn mouse_range auf einen kleinen Wert wie z.B. 100 gesetzt wird, werden durch die Maus keine Events mehr ausgelöst, wenn der Abstand zur auslösenden Entity größer als 100 Quants ist.

 

Die folgenden Codezeilen sind sehr einfach, deshalb besprechen wir sie nicht mehr im Einzelnen. Beachten Sie, dass ich den Code für die Maus und die isometrische Kamera aus dem letzten Workshop eingebaut habe.

 

Schauen wir uns an, mit welchen Tasten der Spieler bewegt werden kann:

 

action players_code

{

       var anim_percentage; // animation percentage

       var movement_speed; // player's movement speed

       var trace_dist;

       player = my; // I'm the player

       while (1)

       {

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

               c_move (my, vector(10 * (key_w - key_s) * time, 0, 0), nullvector, glide); // move the player

               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

 

Der Spieler kann sich mit den Tasten “A” und “D” drehen und mit “W” und “S” bewesen. Wenn der Spieler sich nicht bewegt, befindet er sich in seiner “stand” Animation, andernfalls in der “walk” Animation. Diese Codezeilen sind nichts weiter als eine komprimierte Form des Bewegungsskriptes aus dem 27. Workshop.

 

               if (key_1 == on) // press "1" to start scanning around the player

               {

                       c_scan(player.x, player.pan, vector(120, 60, 300), ignore_me);

               }

 

Wenn der Spieler die Taste “1” drückt, dann wird c_scan ausgeführt. Schauen wir uns an, wie ein c_scan Aufruf aussieht:

 

c_scan (position, angle, range, mode);

 

Die c_scan Anweisung kann Entities, Kamerapositionen, Pfade und statische Lichter in einem bestimmten Bereich orten; schauen wir uns die Parameter einzeln an:

“position” gibt den Ort des Scanners an;

“angle” gibt die Scanrichtung an;

“range” ist ein Vektor mit 3 Komponenten: horizontaler und vertikaler Scanwinkel und Scanreichweite;

“mode” setzt den Modus, man kann z.B. “ignore_me” einstellen, was die “my” Entity ignoriert, oder “ignore_you” (ignoriert die “you”-Entity), “scan_paths” (erkennt Pfade im Level), “scan_lights” und so weiter. Schauen Sie im Handbuch unter c_scan für eine komplette Liste der Features nach.

 

Das folgende Schaubild erklärt, wie c_scan funktioniert; vergleichen Sie die Werte mit denen aus dem Code.

 

w29_02

 

Wir scannen vom Origin des Spielermodels (player.x) in die Richtung, die durch seinen Winkel gegeben ist, mit einem horizontalen Scanwinkel von 120 Grad, einem vertikalen Winkel von 60 Grad und einer Reichweite von 300 Quants. Der Scanningmodus wird auf “ingore_me” gesetzt, damit die scannende Entity (der Spieler) sich nicht selbst erkennt. Schauen wir uns den restlichen Code in der Action des Spielers an:

 

               if (key_2 == on) // press "2" to start tracing

               {

                       vec_set(trace_dist, vector(1000, 0, 0));

                       vec_rotate(trace_dist, vector(player.pan, 0, 0));

                       vec_add(trace_dist.x, my.x);

                       // now trace from the player to a position (stored in trace_dist) that is placed 1000 quants in front of it

                       c_trace (my.x, trace_dist.x, ignore_me | use_box | activate_shoot);

               }

               wait (1);

       }

}

 

Wenn der Spieler die “2” Taste drückt, erzeugen wir einen Vektor der Länge 1000 namens trace_dist, der dann so gedreht wird, dass er in die Blickrichtung des Spielers weist (player.pan) und dann addieren wir ihn zur Position des Spielers (my.x). Für den Fall, dass Sie sich mit Vektorrechnung nicht so gut auskennen, zeige ich Ihnen noch ein Bild, auf dem besser zu sehen ist was geschieht.

 

w29_03

 

Die erste Phase (1) zeigt den ursprünglichen Vektor; er hat eine Länge von 1000 Quants und zeigt auf die rechte Seite des Bildschirms (in x-Richtung). Die zweite Phase zeigt den mit Hilfe von vec_rotate gedrehten Vektor, je nach Ausrichtung des Spielers. Die dritte Phase schließlich zeigt den Vektor , nachdem er zur Position des Spielers addiert wurde.

 

Warum das Ganze? Wir möchten gern unsicthbare Strahlen “schießen” (eigentlich nutzen wir c_trace) und zwar vom Spieler aus nach vorne, d.h. in die Richtung, in die der Spieler schaut. Das ist genau, was diese Codezeile leisten, sie stellen sicher, dass der Endpunkt vom c_trace 1000 Quants direkt vor dem Spieler liegt. Sehen wir uns den c_trace Aufruf genauer an:

 

                       c_trace (my.x, trace_dist.x, ignore_me | use_box | activate_shoot);

 

Der Strahl geht vom Origin des Spielers zur Position trace_dist, die sich ja 1000 Quants vor dem Spieler befindet, wobei der Spieler ignoriert wird; der Trace-Strahl ist dick und es wird das event_shoot für die getroffene Entity aktiviert, falls es eine solche gibt. Keine Sorge, über event_shoot reden wir gleich.

 

Schauen wir uns den “echten” Event Code an; als erstes event_block. Bewegen Sie den Spieler zu der Ecke mit dem “event_block” Schild; Sie sehen einen armen roten Ball, der von Wänden abprallt und versucht, aus seinem Gefängnis aus Blöcken zu entkommen.

 

w29_04

 

Und hier ist der Code zu dem Ball:

 

action test_block

{

       my.enable_block = on;

       my.event = event_blk;  // never put parenthesis to the function here!

       while(1)

       {

               c_move (my, vector(20 * time, 0, 0), nullvector, ignore_passable);

               wait(1);

       }

}

 

Die Action test_block ist dem roten Ball zugewiesen. Die erste Codezeile läßt den Ball auf Kollisionen mit Level Blocks reagieren; das heißt, sobald der Ball mit einem der Level Blocks kollidiert, wird die Event Funktion aufgerufen, was in unserem Fall die Funktion event_blk() ist. Ein beliebter Anfängerfehler ist es, bei der Zuweisung der Eventfunktion die Klammern zu setzen; das funktioniert nicht, weil durch Setzen der Klammern erzwungen wird, dass sie Funktion sofort ausgeführt wird und ihr Rückgabewert wird “my.event” zugewiesen, was für die Engine keinen Sinn macht.

 

Sehen Sie sich die “While”-Schleife an und Sie werden feststellen, dass der Ball sich einfach mit konstanter Geschwindigkeit 20 * time ständig in die Richtung bewegt, die sein pan angibt. Trifft er dabei auf eine Wand, läuft seine Event Funktion.

 

function event_blk()

{

       if (event_type == event_block)

       {

               my.pan = random(360);

       }

}

 

Beachten Sie, dass die eigentliche Funktion ein Klammerpaar aufweist; die dürfen Sie nicht vergessen. Wenn das Event eine Kollision mit einem Level Block war (event_type == event_block) sucht sich der Ball eine zufällige neue Richtung für seinen pan Winkel aus, irgendetwas zwischen 0 und 360 Grad. Auf diese Weise bleibt er nicht in der Wand stecken. Sie wußten nicht, dass es so leicht ist, einen Breakout Klon zu schreiben?

 

Aber so funktionieren alle Events! Wir teilen der Entity mit, dass sie auf bestimmte Events reagieren soll und falls ein solches eintritt, wird einfach die Event Funktion aufgerufen. Schauen wir uns das nächste Event an: event_impact.

 

w29_05

 

Nähern Sie sich dem Guard Model und berühren Sie es; wie Sie sehen, beginnt das Model zu wachsen. Sie haben es sicher schon erraten: event_impact wird immer dann aufgerufen, wenn zwei Entities miteinander kollidieren.

 

action test_impact

{        

       my.enable_impact = on;

       my.event = event_imp; // never put parenthesis to the function here!

}

 

function event_imp()

{

       if (event_type == event_impact)

       {

               my.scale_z += 0.01;

       }

}

 

Wie Sie sehen reagiert das Guard Model auf Zusammenstöße mit anderen Entities; wir können diese Art Event für eine Gewehrkugel oder ein Schwert verwenden, mit der das Model getroffen werden kann. Falls event_impact ausgelöst wird, addieren wir 0,01 zum z_scale des Models und lassen es damit wachsen.

 

w29_06

 

Das nächste Event auf der List ist event_scan; nähern Sie sich dem Model erneut und drücken Sie dann die Taste “1”; das Model wird gedreht, solange Sie die Taste festhalten, denn der Spieler scannt, solange die “1” Taste gehalten wird.

 

action test_scan

{        

       my.enable_scan = on;

       my.event = event_scn; // never put parenthesis to the function here!

}

 

function event_scn()

{

       if (event_type == event_scan)

       {

               my.roll += 5;

       }

}

 

Der Code für das Wachenmodel, das auf Scans reagiert, ist einfach; wir setzen “enable_scan” und wenn die Entity gescannt wird, addieren wir einfach 5 auf den roll Winkel. Der horizontale Scanwinkel ist groß genug (120 Grad), dass der Spieler die Wache nicht direkt anschauen muss. Wenn 120 durch 360 ersetzt wird, scannt der Spieler alle Entities in seinem Umkreis, weil sein horizontaler Scanwinkel ein voller Kreis geworden ist dann rotiert das Model selbst, wenn der Spieler ihm den Rücken zuwendet.

 

w29_07

 

Das nächste Model reagiert auf traces (event_shoot). Kommen Sie in die Nähe, stellen Sie sicher, dass der Spieler in Richtung des Models schaut und drücken Sie die “2” Taste. Wie Sie sehen, leuchtet das Model jedes Mal auf, wenn Sie “2” drücken.

 

w29_07

 

C_trace feuert einen unsichtbaren Strahl zwischen zwei Positionen; daher leuchtet das Model nur auf, wenn der Spieler näher als 1000 Quants ist und das Model direkt ansieht. Der c_trace Code befindet sich in der Funktion für den Spieler und wir kennen ihn bereits; sehen wir uns also den Code für das Guard Model an:

 

action test_trace

{        

       my.enable_shoot = on;

       my.event = event_sht; // never put parenthesis to the function here!

}

 

function event_sht()

{

       if (event_type == event_shoot)

       {

               my.ambient = 100;

               sleep (0.1);

               my.ambient = 0;

       }

}

 

Das Model reagiert auf den trace und startet die event_sht Funktion, wann immer es getroffen wird. Wenn der Spieler mit einem trace trifft, dann setzt das Model für 0,1 Sekunden seinen Ambient auf 100 und dann zurück auf 0; auf diese Weise leuchtet das Model nur kurz auf.

 

Wie Sie sehen ist es ganz einfach, mit Events zu arbeiten; bevor ich Ihnen allerdings den wohlverdienten “Event Master” Armreif (limitierte Auflage) aushändige, möchte ich Ihnen noch zeigen, wie man verschiedene Events in einer einzelnen Action kombinieren kann. Nähern Sie sich dem letzten Guard Model, das sich auf der rechten Seite des Levels befindet.

 

w29_08

 

Dieses Models reagiert auf Berührung mit der Maus und auf Klicks. Das bedeutet, dass die Event Funktion aufgerufen wird, wenn der Spieler mit der Maus über das Model fährt und wenn das Model angeklickt wird.

 

action test_touch_click

{        

       my.light = on;

       my.enable_touch = on;

       my.enable_click = on;

       my.event = event_tch_clk; // never put parenthesis to the function here!

}

 

Die erste Codezeile beleuchtet die Entity je nach ihren Werten für red, green und blue (RGB Farben). Da wir keine speziellen Farben gesetzt haben, wird noch nichts spektakuläres passieren. Die folgenden Zeilen lassen das Model auf Berührung mit der Maus und Mausklicks reagieren; Sie können für jede Entity eine beliebige Zahl Events nutzen, sofern Sie diese in Ihrer Eventfunktion behandeln.

 

function event_tch_clk()

{

       if (event_type == event_touch)

       {

               my.pan += 30;

       }

       if (event_type == event_click)

       {

               my.red = random(255);

               my.green = random(255);

               my.blue = random(255);

       }

}

 

Diese Eventfunktion sieht etwas anders aus; sie besitzt verschiedene “if” Zweige für jedes Event. Wenn der Spieler mit der Maus über das Model fährt, wird der pan Winkel um 30 vergrößert; wird das Model angeklickt, erhält es eine zufällige RGB-Farbkombination. Klicken Sie ein paar Mal auf das Model und Sie werden sehen, wie es jedes Mal die Farbe wechselt.

 

Das war schon alles! Es gibt noch weitere Events, die wir hier nicht behandelt haben, aber die funktionieren alle auf die gleiche Art; im Handbuch steht eine komplette Liste. Da wir die Events nun gemeistert haben, können wir uns auf den folgenden Workshop stürzen, in dem es um die wundervolle Welt der künstlichen Intelligenz geht, auch genannt KI.