Bemalen von Skins und Sprites

A6 hat eine Menge interessanter Features, unter anderem bietet es die Möglichkeit auf eine vorhandene Bitmap mit Hilfe von C-Skript etwas zu malen. In diesem Artikel malen wir ein rotes Kreuz auf die Skin des Guard Models:

bmap* heros_skin; // bitmap pointer, will be used for the skin

action my_hero
{
    heros_skin = bmap_for_entity(my, 0); // get a pointer to guard.mdl's skin
    temp.red = 255; // paint the skin using the red color
    temp.green = 0;
    temp.blue = 0;
    format = bmap_lock (heros_skin, 0); // lock the skin bitmap
    pixel = pixel_for_vec(temp, 100, format);
    draw_cross(31, 108);
    bmap_unlock (heros_skin); // unlock the bitmap
    // put the rest of the code for your hero here
}

Dies ist die Anfänger-Ecke, also sollte der Code nicht allzu kompliziert sein: wir weisen dem vorher definierten Bitmap Zeiger heros_skin die Skin des Guard Models zu (das ist, was die Anweisung bmap_for_entity leistet) und setzen dann temp.red auf 255. Alle Pixel, die wir jetzt malen wollen, werden also rot sein. Mit Hilfe von bmap_lock bereiten wir den Malprozeß vor und erzeugen dann einen roten Pixel mit einer Transparenz von 100 (nicht transparent) mit dem korrekten Format. Die letzten zwei Zeilen rufen die Funktion auf, die das Kreuz malt und gibt die Skin wieder frei.

Hier ist der Code für die furchteinflößende draw_cross Funktion:

function draw_cross(coords_x, coords_y)
{
    pixel_to_bmap(heros_skin, coords_x + 3, coords_y + 0, pixel);
    pixel_to_bmap(heros_skin, coords_x + 4, coords_y + 0, pixel);
    pixel_to_bmap(heros_skin, coords_x + 5, coords_y + 0, pixel);
    pixel_to_bmap(heros_skin, coords_x + 6, coords_y + 0, pixel);
    pixel_to_bmap(heros_skin, coords_x + 3, coords_y + 1, pixel);
    pixel_to_bmap(heros_skin, coords_x + 6, coords_y + 1, pixel);
    pixel_to_bmap(heros_skin, coords_x + 3, coords_y + 1, pixel);
    pixel_to_bmap(heros_skin, coords_x + 6, coords_y + 1, pixel);
    pixel_to_bmap(heros_skin, coords_x + 0, coords_y + 2, pixel);
    pixel_to_bmap(heros_skin, coords_x + 1, coords_y + 2, pixel);
    pixel_to_bmap(heros_skin, coords_x + 2, coords_y + 2, pixel);
    pixel_to_bmap(heros_skin, coords_x + 3, coords_y + 2, pixel);
    pixel_to_bmap(heros_skin, coords_x + 6, coords_y + 2, pixel);
    pixel_to_bmap(heros_skin, coords_x + 7, coords_y + 2, pixel);
    pixel_to_bmap(heros_skin, coords_x + 8, coords_y + 2, pixel);
    pixel_to_bmap(heros_skin, coords_x + 9, coords_y + 2, pixel);
    pixel_to_bmap(heros_skin, coords_x + 0, coords_y + 3, pixel);
    pixel_to_bmap(heros_skin, coords_x + 9, coords_y + 3, pixel);
    pixel_to_bmap(heros_skin, coords_x + 0, coords_y + 4, pixel);
    pixel_to_bmap(heros_skin, coords_x + 9, coords_y + 4, pixel);
    pixel_to_bmap(heros_skin, coords_x + 0, coords_y + 5, pixel);
    pixel_to_bmap(heros_skin, coords_x + 1, coords_y + 5, pixel);
    pixel_to_bmap(heros_skin, coords_x + 2, coords_y + 5, pixel);
    pixel_to_bmap(heros_skin, coords_x + 3, coords_y + 5, pixel);
    pixel_to_bmap(heros_skin, coords_x + 6, coords_y + 5, pixel);
    pixel_to_bmap(heros_skin, coords_x + 7, coords_y + 5, pixel);
    pixel_to_bmap(heros_skin, coords_x + 8, coords_y + 5, pixel);
    pixel_to_bmap(heros_skin, coords_x + 9, coords_y + 5, pixel);
    pixel_to_bmap(heros_skin, coords_x + 3, coords_y + 6, pixel);
    pixel_to_bmap(heros_skin, coords_x + 6, coords_y + 6, pixel);
    pixel_to_bmap(heros_skin, coords_x + 3, coords_y + 6, pixel);
    pixel_to_bmap(heros_skin, coords_x + 6, coords_y + 6, pixel);
    pixel_to_bmap(heros_skin, coords_x + 3, coords_y + 7, pixel);
    pixel_to_bmap(heros_skin, coords_x + 4, coords_y + 7, pixel);
    pixel_to_bmap(heros_skin, coords_x + 5, coords_y + 7, pixel);
    pixel_to_bmap(heros_skin, coords_x + 6, coords_y + 7, pixel);
}

Ja, ich weiß, sie ist sehr lang, aber haben Sie bemerkt, dass die Zeilen fast identisch sind? Schauen wir uns die erste genauer an:

pixel_to_bmap(heros_skin, coords_x + 3, coords_y + 0, pixel);

Wir benutzen die patentierte pixel_to_bmap Anweisung um die Skin zu bemalen und zwar an den gegebenen Koordinaten (coords_x + 3, coords_y + 0) und zwar mit dem roten nicht durchsichtigen Pixel. Also malt unser Funktionsaufruf draw_cross:

draw_cross(31, 108);

einen Pixel für jede Zeile in der Funktion, wobei die erste Zeile einen Pixel bei x = 34 und y = 108 setzt (31 + 3, 108 + 0). Wo habe ich diese Werte her? Schauen Sie sich die Skin mal an:

Der Rest ist leicht: Sie entscheiden, was dargestellt werden soll (ein Stern, ein Symbol, etc.) und dann malen Sie es irgendwo, z.B. auf Papier. Hier habe ich mein Kreuz vorbereitet (die grauen Ränder sind zur Verdeutlichung):

Benutzen Sie für jeden Pixel die pixel_to_bmap Anweisung und vergessen Sie nicht, dass auch Sprites so bemalt werden können! Sie könnten also eine Digitaluhr, seltsame Zeichen und andere Dinge mit dieser Technik bewerkstelligen!


Einfacher Unterwassereffekt

Ok, wir wissen also, wie man eine schöne Wasseroberfläche erstellt. Aber was geschieht, wenn der Spieler untertaucht? Wird er oder sie dieselben schönen Wellen sehen? Dieser Artikel beschreibt einen einfachen Unterwassereffekt, der mit jeder Edition der Engine funktioniert. Die Idee ist einfach: wir überwachen die Höhe des Spielers in einer Endlosschleife und wenn diese unter einen gewissen Wert fällt, dann kommt der Kopf des Spielers in ein blaues und rotierendes Model einer Kugel.

starter create_water()
{
    sleep (1); // wait until the level is loaded
    ent_create (sphere_mdl, nullvector, move_sphere);
}

Den Anfang macht eine Starterfunktion, die wartet bis das Level geladen ist und dann eine Kugel erstellt, die mit der move_sphere Action versehen wird:

function move_sphere()
{
    while (player == null) {wait (1);}
    proc_late();
    my.passable = on;
    my.transparent = on;
    my.unlit = on;
    my.bright = on;
    my.ambient = -100;
    my.alpha = 90; // play with this value
    while (1)
    {
       my.x = player.x;
       my.y = player.y;
       my.z = player.z + 40; // 40 = experimental value
       my.pan = sin(total_frames * water_speed) * amplitude;
       my.tilt = sin(total_frames * water_speed) * amplitude;
       my.roll = cos(total_frames * water_speed) * amplitude;
       if (my.z < -38) // experimental value, depends on the height of the water surface
       {
          my.invisible = off;
       }
       else
       {
          my.invisible = on;
       }
       wait(1);
    }
}

Diese Funktion wartet bis der Spieler existiert und macht das Model dann passierbar, transparent, unlit und bright. Ich habe einen negativen Wert für Ambient gewählt, aber Sie könnten auch einfach die Skin dunkler wählen. Sie sollten auch verschiedene Werte für den Transparenzfaktor (alpha) testen. Die Kugel ist 40 Quants über dem Spieler – das ist ein experimenteller Wert, der davon abhängt, wie groß das Model Ihres Players ist.

Ich benutze Sinus und Cosinus Funktionen, um die Kugel zu drehen; benutzen Sie beliebige Kombinationen für die Winkel und vergessen Sie nicht, auch water_speed und amplitude zu ändern. Wie gesagt prüft der Code, ob der z-Wert des Spielers unterhalb von 38 Quants ist oder nicht und je nachdem wird die Kugel sichtbar oder nicht. Dieser Wert hängt davon ab, wo sich die Wasseroberfläche in Ihrem Level befindet und muß je nach Bedarf angepaßt werden.

Ich habe für die Oberfläche selbst ein simples transparentes Sprite, aber Sie können auch eine andere Methode wählen, z.B. das Terrain-basierte Wassersystem aus einer früheren AUM Ausgabe.