Splitball

Splitball (Splitscreen Ballspiel) benutzt eines der herausragenden Features von Acknex: Die Möglichkeit, mehrere Views zur gleichen Zeit zu haben. Schauen wir uns die Funktion set_cameras an - sie wird in main aufgerufen:

function set_cameras()
{
     camera.size_x = 400;
     camera.size_y = 600;
     camera.pos_x = 0;
     camera.pos_y = 0;
     camera.x = first.x;
     camera.y = first.y;
     camera.z = first.z;
     camera.tilt = -90;
     camera.pan = first.pan;
     camera.visible = on;

     camera2.size_x = 400;
     camera2.size_y = 600;
     camera2.pos_x = 400;
     camera2.pos_y = 0;
     camera2.x = second.x;
     camera2.y = second.y;
     camera2.z = second.z;
     camera2.tilt = -90;
     camera2.pan = second.pan;
     camera2.visible = on;
}

Ich habe die Bildschirmauflösung auf 800x600 gesetzt, daher haben meine Views (camera und camera2 genannt) eine Größe von jeweils 400x600 Pixel. Ich habe mein (fast schon) berühmtes arrow.mdl in das Level plaziert. Seine Position (xyz) und Orientierung (pan) werden an die entsprechende View übergeben. Die Pfeile hab ich first und second genannt, um ehrlich zu sein, dies ist der Name der Synonyme die ihnen zugewiesen werden in den Aktionen player1eye und player2eye.

Die Spieler benutzen gleiche Aktionen, sehen wir uns eine davon an:

action player1
{
     my.push = 5;
     my.enable_block = on;
     while (1)
     {
          while (score1 == 9 || score2 == 9)
          {
               wait (2);
          }
          if (key_z == 1 && my.x > -270)
          {
               ent_move (move_left, nullvector);
          }
          if (key_x == 1 && my.x < 270)
          {
                ent_move (move_right, nullvector);
          }
          move_left.x = -25 * time;
          move_right.x = 25 * time;
          wait (1);
     }
}

Alles läuft in einer while(1)-schleife ab. Wenn das Spiel zu Ende ist (score1 = 0 oder score2 = 9), können sich die Spieler nicht bewegen. Ansonsten bewegt sich Spieler 1 nach links oder rechts, abhängig von der Taste, die gedrückt wurde (y oder x). Schließlich berechnen wir noch jeden Frame die Geschwindigkeit der Paddlebewegung. Es stimmt, dass wir die Framerate auf 50 limitiert haben, daher werden sich diese Werte nicht allzusehr ändern, aber auf langsamen Rechnern und / oder Software Modus könnte die Framerate unter 50 fps sinken.

Die Wand, die das Spielfeld umrahmt ist ein einfaches Entity, bei dem Skill11 auf 1 gesetzt ist. Ich habe dies getan, damit ich prüfen kann, ob der Ball mit der Wand oder einem der Paddles kollidiert.

Der Ball hat folgende Aktion angehängt:

action ball
{
     ball_syn = me;
     my.skill10 = 1;
     my.enable_entity = on;
     my.event = change_direction;
     if (random(1) > 0.5)
     {
          my.pan = 45 + random(90);
     }
     else
     {
          my.pan = 225 + random(90);
     }
     while (1) 
     {
          ball_speed.x = 30 * time;
          ball_speed.y = 0;
          ball_speed.z = 0;
          ent_move (ball_speed, nullvector);
          wait (1);
     }
}

Der Ball hat Skill10 auf 1 gesetzt und löst sein Event (change_direction) aus, falls er mit einem anderen Entity (einem der Spieler, der Wand oder dem Tor-Entity) kollidiert. Wenn das Spiel startet, kann der erste Ball in Richtung Spieler 1 rollen falls random(1) > 0.5 oder zu Spieler 2 bei random(1) <= 0.5. Dies gibt den Spielern gleiche Chancen zum Spielstart. Ich habe anständige pan-Winkel gewählt - sehen Sie sich das folgende Bild an:


 

Der Ball bewegt sich ständig mit ein paar wenigen Codezeilen - dort gibt es nichts spezielles.

Jedesmal wenn Sie ein Tor schießen, wird die Aktion goal (dem goal.wmb Entity zugewiesen) ihr Event auslösen: die Funktion add_score:

action goal
{
     my.enable_impact = on;
     my.event = add_score;
}

function add_score()
{
     if (you.skill10 == 1)
     {
          if (score1 == 9 || score2 == 9) {return;}
          play_sound goal_snd, 50;
          if (my.flag1 == on)
          {
              score1 += 1;
              wait (1);
              ball_syn.x = 0;
              ball_syn.y = 0;
              ball_syn.pan = 225 + random(90);
          }
          if (my.flag2 == on)
          {
              score2 += 1;
              wait (1);
              ball_syn.x = 0;
              ball_syn.y = 0;
              ball_syn.pan = 45 + random(90);
          }
     }
}
 
Als erstens überprüfen wir, ob der Ball mit der goal.wmb Entity kollidiert ist (you.skill10 = 1). Falls das Spiel bereits zu Ende ist (score1 = 9 oder score2 = 9), gibt es nichts zu tun, ansonsten spielen wir das goal_snd Sample ab und erhöhen die Punktzahl für Spieler 1 oder Spieler 2 (falls Flag1 oder Flag2 des goal.wmb Entities in WED gesetzt ist). Falls Spieler1 punktet, wird der Ball in die Mitte gesetzt und wird in die Richtung des Spielers rollen (das ist das einzig richtige was man tun kann).

Das Spiel wird jedesmal, wenn wir die Punktzahlen zurücksetzen, neu gestartet - dies startet die while(1)-Schleife für die Spieler und erlaubt ihnen, sich wieder zu bewegen.

 

Passwortabfrage

Dies ist der Code für Ihren NSC (Nichtspielercharakter). Sie kommen ihm nahe, er fragt Sie „Wie lautet das Passwort?“, Sie tippen das Passwort ein und wenn Sie es wissen, können Sie bestimmte Türen öffnen oder irgendwelche anderen Aktionen oder Funktionen auslösen.

Ich benutze drei Strings: der erste zeigt die Passwortaufforderung an, der zweite speichert meine Eingabe (zehn Zeichen, aber Sie können die Länge erhöhen), und der dritte String enthält das vordefinierte Passwort.

string passreq_str = "What's the password?";
string pass_str[10];
string password_str = "geronimo";

Ich benutze eine klassische Testdefinition um die Strings auf dem Bildschirm anzuzeigen und eine einfache request_pass Aktion die meinem NSC zugewiesen wird.

text pass_txt
{
     pos_x = 20;
     pos_y = 20;
     layer = 15;
     font standard_font;
}

action request_pass
{
     my.enable_impact = on;
     my.enable_push = on;
     my.event = ask_pass;
}

Das interessante Zeug passiert in der Funktion ask_pass:

function ask_pass ()
{
     if (you == player)
     {
          if (pass_txt.visible == on) {return;}
          pass_txt.string = passreq_str;
          pass_txt.visible = on;
          waitt (32);
          pass_txt.string = pass_str;
          while (pass_txt.visible == on)
          {
               inkey pass_str;
               if(result == 13) 
               {
                    pass_txt.visible = off;
               }
          }
          if (str_cmpi (pass_str, password_str) == 1)
          {
                key1 = 1; 
                new_event();
          }
     }
}

Diese Funktion wird nur laufen, falls die Entity, die mit dem NSC kollidiert ist, der Spieler selbst ist. Falls Sie einen anständigen Computer haben, können mehrere Events gleichzeitig ablaufen, und das wollen Sie nicht. Wir können exclusive_global oder meine Methode benutzen:

if (pass_txt.visible == on) {return;}

Falls pass_text schon sichtbar ist, stoppt die Funktion, daher kann sie nur einmal laufen (das erste mal wird der Text sichtbar gemacht).
Wir zeigen den passreq_str String für zwei Sekunden an und warten dann auf die Eingabe des Spielers. Dies ist der fall, solange der Spieler nicht Enter drückt (result entspricht 13, falls Enter gedrückt wird). Sobald wir das Passwort fertig eingegeben und Enter gedrückt haben, wird der Text unsichtbar gemacht und eine einfache str_cmpi-Anweisung wird ausgeführt. Falls pass_str dem Passwort entspricht (Usereingabe = vordefiniertes Passwort) und wir die Templates benutzen, bedeutet key = 1, dass wir Türen öffnen können, die man mit Schlüssel 1 öffnen kann, key2 = 1 bedeutet dass wir Türen, die Schlüssel 2 benutzen, öffnen können, usw.