Morrowing

Achtung! Es gibt eine neue RPG-Serie! Wir starten in diesem Monat mit einer neuen Art Spiel, indem wir Module verwenden, die sich (hoffentlich) in Ihre Projekte einbinden lassen, ganz gleich um welche Art Spiel es sich dabei im Einzelnen handelt. Einige WDLs sind schon fertig; schauen Sie sich die an.

function main()
{
    fps_max = 60;
    on_f2 = null;
    on_f4 = null;
    on_f5 = null;
    on_f6 = null;
    on_f7 = null;
    on_f8 = null;
    on_f9 = null;
    on_f10 = null;
    on_f11 = null;
    on_f12 = null;
    level_load (level1_wmb);
    wait (3);
    mouse_map = pointer_pcx;
    while (1)
    {
       if (key_space == on)
       {
          mouse_mode = 2;
       }
       else
      {
          mouse_mode = 0;
       }
       mouse_pos.x = pointer.x;
       mouse_pos.y = pointer.y;
       wait (1);
    }
}

Main() ist die einzige Funktion aus morrowing.wdl; die Framerate wird auf 60 fps begrenzt und dann werden viele Funktionstasten deaktiviert (die hier nicht genannten brauchen wir später noch). Das Level wird geladen; wir warten bis dies geschehen ist und geben dem Mauszeiger eine Bitmap.

Wenn die Leertaste gedrückt und festgehalten wird, ist der Mauszeiger sichtbar; ansonsten wird er verborgen. Die letzten Codezeilen erlauben es uns, den Mauszeiger zu bewegen, wenn er sichtbar ist. Wir werden den Mauszeiger anzeigen, wenn wir ihn benötigen (z.B. wenn wir mit einem Charakter reden möchten), aber vergessen Sie nicht, dass Sie ihn mit der Leertaste jederzeit anzeigen können.

action player1
{
    player = my;
    my.x = -2700;
    my.y = -2500;
    my.z = -2000;
    my.pan = 160;
    my.transparent = on;
    my.alpha = 100;
    my.skill40 = camera_distance;
    my.skill41 = camera_height;
    camera_mode = 3;

Dies ist die Actioon für den Spieler; Sie finden sie in der Datei player.wdl. Wir geben dem Spieler eine geeignete Position und Ausrichtung; dem Spieler diese Position im Skript zuzuweisen ist nützlich, wenn Sie einen großen Level haben und der Spieler im WED nur schwer zu sehen ist. Der Spieler ist transparent, aber der Alphawert von 100 läßt ihn dennoch nicht durchsichtig erscheinen. Der Abstand zwischen Spieler und Kamera wird in skill41 festgehalten und dann wird camera_mode auf 3 gesetzt, weil das Spiel in der Außenansicht (3rd person) startet.

    while (1)
    {
       player.pan -= 10 * mouse_force.x * time - 1.5 * (key_a - key_d);
       camera.x = player.x - camera_distance * cos(player.pan);
       camera.y = player.y - camera_distance * sin(player.pan);
       camera.z = player.z + camera_height + 0.8 * sin(my.skill46 * 3.6);
       camera.pan = player.pan;
       camera.tilt += 7 * mouse_force.y * time;
       camera_distance = min (max (camera_distance, 5), 500);
       if (key_w + key_s > 0)
       {
          ent_cycle("walk", my.skill46);
          my.skill46 += 10 * (1 + key_shift * 0.5) * time;
          my.skill46 %= 100;
       }
       else
       {
          ent_cycle("stand", my.skill48);
          my.skill48 += 2 * time;
          my.skill48 %= 100;
       }

Der Spieler dreht sich, wenn wir die Maus bewegen oder die Tasten “A” oder “D” gedrückt werden. Die folgenden Zeilen sorgen dafür, dass die Kamera hinter dem Spieler bleibt und zwar in dem Abstand, der durch camera_distance gegeben ist, sowie ein bißchen nach oben versetzt. Mit einem kleinen Trick habe ich dafür gesorgt, dass der Kopf des Spielers sich etwas bewegt, wenn er läuft, damit er nicht wie ein Auto strikt geradeaus blickt. Wie das funktioniert? Ich addiere einfach 0.8*sin(my.skill46 * 3.6) zu camera.z; skill46 kontrolliert die “walk” Animation und ändert seine Werte zwischen 0 und 100. Also ändert sich skill46 * 3.6 von 0 bis 360 und sin(0 .. 360) nimmt Werte zwischen –1 und 1 an, daher ändert sich die Höhe der Kamera zwischen –0,8 und 0,8. Sie können diesen Wert von 0,8 vergrößern, damit der Effekt stärker wird, aber machen Sie den Wert nicht zu groß, sonst könnte Ihren Kunden beim Spielen schlecht werden und Sie müßten mit jedem Spiel ein neues Keyboard ausliefern. :)

Die Kamera übernimmt den Pan des Spielers und kann beliebig nach oben oder unten schwenken; die folgende Zeile begrenzt camera_distance auf 5 bis 500 Quants. Wenn die “W” oder “S” Taste gedrückt wird, wird die “walk”-Animation des Spielers gestartet, um 50% schneller falls zusätzlich die Shift-Taste gedrückt wird. Wenn der Spieler hingegen steht, dann kommt seine “stand”-Animation zum Einsatz.

       if (camera_mode == 3)
       {
          avoid_obstacles();
       }
       vec_set (temp, my.x);
       temp.z -= 10000;
       trace_mode = ignore_me + ignore_passable + use_box;
       temp.z = -trace (my.x, temp);
       temp.x = 10 * (key_w - key_s) * (1 + 1 * key_shift) * time;
       temp.y = 0;
       my.skill47 += ent_move (temp, nullvector);
       if (my.skill47 > 50)
       {
          snd_play(step_wav, 30, 0);
          my.skill47 = 0;
       }
       wait (1);
    }
}

Wenn wir in der Außenansicht spielen, wird die Funktion avoid_obstacles einmal pro Frame aufgerufen; über diese Funktion reden wir gleich. Wir tracen 10.000 Quants nach unten und gleichen seine Höhe entsprechend an, indem wir seine Füße auf den Boden setzen. Der Spieler kann sich mit den “W” und “S”-Tasten vor- und rückwärts bewegen, wieder mit doppelter Geschwindigkeit, wenn die Shift-Taste gedrückt wird.

Die zurückgelegte Distanz wird auf skill47 addiert; wenn dieser Skill 50 übersteigt, dann ist ein step_wav Sound zu hören und skill47 wird zurückgesetzt. Ändern Sie diesen Wert je nach Ihrer Animation, er kontrolliert die Anzahl der Schritte die pro Walk-Zyklus ertönen.

function avoid_obstacles()
{
    trace_mode = ignore_me + ignore_passable;
    vec_set (temporary_distance.x, camera.x);
    temporary_distance.z -= 50; // 50 = experimental value
    distance_traced = trace (player.x, temporary_distance.x); // trace between the player and temporary_distance
    if (distance_traced == 0) // no obstacles on the way?
    {
       my.alpha = min (100, my.alpha + 3 * time); // then increase player's alpha up to 100
       if (camera_distance < my.skill40) // if the camera got closer to the player
       {
          camera_distance += 1; // restore the initial camera_distance slowly
       }
    }
    else // obstacles encountered?
    {
       distance_traced -= 2; // then bring the camera 2 quants closer to the player!
       my.alpha = (distance_traced / (my.skill40 + 1)) * 100; // decrease player's alpha; don't allow a division by zero
       camera.x = player.x - distance_traced * cos(camera.pan); // place the camera behind the player
       camera.y = player.y - distance_traced * sin(camera.pan); // at the new distance given by distance_traced
    }
}

Diese Funktion führt einen trace vom Spieler zu einem Punkt aus, der 50 Quants unter der Kamera liegt; falls distance_traced 0 ist (keine Hindernisse), wird die Transparenz des Spielers verringert bzw. sein Alpha-Wert bis hin zu 100 erhöht, sollte er kleiner gewesen sein als 100. Falls camera_distance kleiner als der Wert in skill40 ist, dann erhöhen wir camera_distance um 1 pro frame bis der Anfangswert wieder erreicht wurde.

Falls distance_traced nicht 0 ist, dann haben wir ein Hindernis zwischen Spieler und Kamera; distance_traced wird verringert, wodurch die Kamera noch 2 Quants näher an den Spieler gerückt wird und die Transparenz des Spielers wird angepaßt, damit der Spieler durch das Spielermodel blicken kann, das sonst die Sicht versperren würde. Die Kamera bleibt an der neuen Position bis es keine Hindernisse mehr hinter dem Spieler gibt.

Wenn Sie sich immer noch am Kopf kratzen (nicht, dass ich da etwas gegen hätte!), schauen Sie sich das folgende Bild an. Ich habe einen Punkt 50 Quants unter der Kamera gewählt, damit die Kamera unter normalen Umständen (normales Terrain) nie im Terrain landet. Nie! Ja, genau und Windows hat auch keine Bugs!

function first_person_camera() // press "F1" to run this function
{
    camera_distance = 0; // place the camera at player's position
    camera_height = 20; // play with this value
    player.invisible = on; // make the player model invisible
    camera_mode = 1; // set the camera_mode variable to 1st person
}

function third_person_camera() // press "F3" to run this function
{
   camera_distance = player.skill40; // restore the distance from the player to the camera
   camera_height = player.skill41; // as well as its height
   player.invisible = off; // and show the player
   camera_mode = 3; // set the camera_mode variable to 3rd person
}

on_f1 = first_person_camera;
on_f3 = third_person_camera;

Das Spiel kann auch in der Egoperspektive gespielt werden (drücken Sie F1) – mit F3 kann in die Außenansicht zurückgeschaltet werden. Die entsprechende Funktion plaziert die Kamera an der Position des Spielers und macht das Model unsichtbar, bzw. stellt camera_distance und camera_height wieder her und zeigt das Model an. Eine einfache Definition für einen Sky Cube befindet sich in der environment.wdl und das beendet den Artikel:

sky level1_cube
{
    type = <free2+6.tga>;
    z = 100;
    layer = 20;
    flags = cube, visible;
}

Ändern Sie z = 100 wenn Sie möchten. Bis nächsten Monat dann!

 

Runde Linse

Im letzten Monat testete ich eine Engine, deren Preis im sechsstelligen Bereich lag, und eines der Testlevels beinhaltete eine solche Linse. Ich habe sie mir gut angesehen, weil mir die Idee gefiel und dann habe mich mich hingesetzt und den Effekt für A6 umgesetzt. Sie brauchen die A6 Professional dafür, aber selbst die ist 200 bis 300 mal günstiger!

entity lens_ent
{
    type = <sphere.mdl>;
    x = 700;
    y = 0;
    z = 0;
    layer = 40;
    flags = visible;
}

Als erstes definieren wir eine Entity namens lens_ent. Diese wird 700 Quants vor dem View plaziert (ein anderer Wert sorgt für eine andere Größe) und in der Bildschirmmitte (natürlich können diese Werte auch geändert werden, um sie woanders hinzuplazieren).

view lens_view
{
    layer = -10;
    flags = visible;
    arc = -20;
}

Schritt zwei: wir definieren einen View namens lens_view, der einen negativen layer erhält, damit wir diesen View niemals sehen. Er wird sichtbar gemacht und er enthält einen geeigneten negativen Zoomfaktor; das ist, damit das Bild auf dem Kugelmodel (der Linse) nicht spiegelverkehrt erscheint.

starter init_lens()
{
    lens_view.pos_x = 0;
    lens_view.pos_y = 0;
    lens_view.size_x = screen_size.x;
    lens_view.size_y = screen_size.y;
    lens_view.bmap = bmap_for_entity (lens_ent, 0);
    while (1)
    {
       lens_view.x = camera.x; // keep the lens view in sync with the camera
       lens_view.y = camera.y;
       lens_view.z = camera.z;
       lens_view.pan = camera.pan;
       lens_view.tilt = camera.tilt;
       lens_view.roll = camera.roll;
       wait (1);
    }
}

Schritt drei: die lens_view übernimmt die Position und Größe der camera View. Dann wird die View auf die Bitmap von lens_ent gerendert, also den Skin der Kugel. Die Schleife sorgt dafür, dass lens_view immer mit der Kamera synchron läuft. Das Resultat liefert das Gewünschte: was wir auf dem Bildschirm sehen, erscheint auch in der verborgenen View, die wiederum auf die Kugel gerendert wird. Jetzt sehen Sie, warum ich A6 benutze, die Engine ist leicht zu benutzen und wird Monat für Monat verbessert! Warten Sie, ich habe noch eine Idee!

Haben Sie gesehen, wie ich das gemacht habe? Die Linse meines Scharfschützengewehres zeigt das Level wie es ist. Für das Gewehr habe ich eine neue Entity definiert und die x, y und z-Werte der lens_ent Entity angeglichen. Und, ist das cool?