Steroidz

Mike Bravo 23 an Basis! Es kommt ein riesiger Asteroid auf uns zu! Nein... es sind hunderte von Asteroiden, die auf unser Schiff zukommen!

Wenn dies mal keine gruselige Vorstellung ist, dann ist es Steroidz! Werfen wir einen Blick auf die main Funktion:

function main()
{
    fps_max = 40;
    level_load (steroidz_wmb); // dummy level
    wait (2);
    score = 0; // reset score
    asteroid_generator();
    display_lives();
}

Die Framerate wird auf 40 begrenzt. Wir laden das Level (eine riesige Mdl Kugel und ein winziger Würfel, sonst würde das Level nicht kompiliert werden). Sobald das Level geladen wurde, setzen wir die Punktzahl zurück und fangen an, die Leben anzuzeigen und kontinuierlich Asteroiden zu generieren. Wir werden über diese beiden Funktionen etwas später sprechen, sehen wir uns zuerst die Funktion für das Raumschiff des Spielers an:

action players_ship
{
    my.dead = 0;
    my.metal = on;
    my.skill22 = 0; // reset the number of hits
    my.enable_impact = on;
    my.event = player_dies;
    player = me;
    while (camera == null) {wait (1);}
    while (my.dead == 0) // as long as the player isn't dead
    {
        player.tilt += 0.5 * (key_cuu - key_cud) * time;
        player.pan += 2 * (key_cul - key_cur) * time;
        ship_speed.x = 0.6 * key_space * time + max ((1 - time * 0.05), 0) * ship_speed.x;
        ship_speed.y = 0;
        ship_speed.z = 0;
        ent_move(ship_speed, nullvector);

        vec_set (temp, nullvector);
        vec_sub (temp, camera_distance);
        vec_to_angle (camera_angle, temp);
        camera_angle.roll = 0;

        vec_set (camera.x, camera_distance);
        vec_rotate (camera.x, my.pan);
        vec_add (camera.x, my.x);

        vec_set (camera.pan, my.pan);
        ang_rotate (camera.pan, camera_angle);

        vec_for_vertex(particles_left, my, 89);
        vec_for_vertex(particles_right, my, 83);

        if (key_space == 1)
        {
            effect (ship_particles, 2, particles_left, normal);
            effect (ship_particles, 2, particles_right, normal);
            if (shipsound_handle == 0)
            {
                snd_loop (ship_snd, 50, 0);
                shipsound_handle = result;
            }
        }
        else
        {
            snd_stop (shipsound_handle);
            shipsound_handle = 0;
        }

        if (key_ctrl == 1) // press ctrl to fire
        {
            if (my.skill35 == 0)
            {
                my.skill35 = 1; // disable autofire
                ent_create (rocket_mdl, player.pos, ship_rocket);
                snd_play (firerocket_snd, 80, 0);
            }
        }
        else
        {
            my.skill35 = 0; // enable fire again
        }

        wait (1);
    }
}

Ich habe “dead” als Skill20 definiert, daher bedeutet my.dead = 0 dasselbe wie my.skill20 = 0. Skill20 wird auf 1 gesetzt, wenn der Spieler tot ist. Der Spieler reagiert auf Zusammenstöße (impact) und stirbt nach fünf Treffern, die Anzahl der Treffer wird in Skill22 gespeichert. Ich benutze die Pfeiltasten um das Schiff zu drehen (rauf und runter für Tilt, links und rechts für Pan), die Leertaste für eine Beschleunigungsstoß (beinhaltet Reibung) und Strg zum feuern.

Wir drehen die Kamera zum Schiff, fügen eine anständige Distanz hinzu (wird in camera_distance festgelegt) und drehen anschließend das Schiff mit ang_rotate. Diese Anweisung ermöglicht es, die Kamera (oder ein anderes Objekt) relativ zur Richtung des Raumschiffs zu drehen.

Wenn wir die Leertaste drücken, generieren die Motoren Partikel und spielen einen kontinuierlichen Ton ab. Die entsprechenden Vertices der Motoren haben die Nummern 89 und 83 (holen Sie sich diese Werte aus MED). Die beiden Zeilen mit vec_for_vertex setzen particle_left und particle_right auf die Vertex Koordinaten.

Sobald wir Strg drücken, erstellen wir eine Rakete an der Position des Spielers, die Funktion für die Rakete sieht wie folgt aus:

function ship_rocket()
{
    wait (1);
    my.ambient = 100;
    my.skill30 = 0;
    my.enable_entity = on;
    my.enable_block = on;
    my.event = remove_me;
    my.passable = on;
    my.pan = player.pan;
    my.tilt = player.tilt;

    my.skill12 = 100 * time;
    my.skill13 = 0;
    my.skill14 = 0;

    while (my != null) // as long as the rocket hasn't exploded
    {
        my.skill12 = 100 * time;
        my.skill30 += 0.1 * time;
        if (vec_dist (my.x, player.x) < 100) {my.passable = on;}
        // don't collide with the player
        else {my.passable = off;}
        if (my.skill30 < 100)
        {
            ent_move (my.skill12, nullvector);
        }
        else
        {
            ent_remove (me);
        }
        wait (1);
    }
}

Der Code ähnelt dem aus dem Pfeil und Bogen Beispiel. Die Rakete wird sich solange bewegen, bis sie etwas trifft, falls sie aber nichts trifft, wird sie nach einem bestimmten Zeitintervall entfernt.

Ich habe einen einfachen Trick verwendet, um genügend Spielraum für unser Raumschiff zu bieten: Ich habe eine riesige Kugel ins Level plaziert und benutze eine einfache Aktion um den Spieler in der Mitte der Kugel zu halten (wenn sich der Spieler bewegt, wird sich die Kugel mit ihm in die selbe Richtung bewegen):

action sky_follows_player
{
    my.passable = on;
    while (player == null) {wait (1);}
    while (1)
    {
        vec_set (my.pos, player.pos);
        wait (1);
    }
}

Der “Himmel” muss passabel sein, da die Asteroiden durch ihn hindurch kommen und in Richtung des Spielers bewegen müssen. Sehen wir uns jetzt die Funktion asteroid_generator an:

function asteroid_generator()
{
    while (1)
    {
        if (random(1) > 0.97)
        {
            asteroid_angle = player.pan + 30 - random(60); // -30...+30 degrees
            asteroid_pos.x = player.x + (5000 + random(5000)) * cos (asteroid_angle);
            asteroid_pos.y = player.y + (5000 + random(5000)) * sin (asteroid_angle);
            asteroid_pos.z = player.z + 300 - random(600);
            ent_create (asteroid_mdl, asteroid_pos, move_asteroid);
        }
        waitt (3);
    }
}

function move_asteroid()
{
    my.ambient = -50;
    my.metal = on;
    my.enable_entity = on;
    my.event = destroy_asteroid;
    vec_set (temp.x, player.x);
    vec_sub (temp.x, my.x);
    vec_to_angle (my.pan, temp); // turn towards the player
    my.pan += 3 - random(6); // miss the player from time to time
    my.tilt += 1 - random(2);
    while (my != null)
    {
        my.roll += 5 * time;
        my.skill3 = 20 * time;
        my.skill4 = 0;
        my.skill5 = 0;
        ent_move(my.skill3, nullvector);
        wait (1);
    }
}
 

Die Asteroiden werden von Zeit zu Zeit, falls random(1) > 0.97 ist, auf einem Ring, der den Spieler umgibt, kreiert, wie in folgendem Bild zu sehen ist:

Es wäre nicht fair, den Spieler von hinten zu treffen, daher werden die Asteroiden nur vor dem Spieler (player.pan + 30 – random(60)) generiert. Die Funktion move_asteroid dreht die Asteroiden zum Spieler. Wir verändern die Richtung ein wenig, indem wir kleine zufällige Werte zu Pan und Tilt hinzu addieren.

function destroy_asteroid()
{
    my.ambient = 100;
    my.passable = on;
    waitt (2);
    ent_remove (me);
    snd_play (explode_snd, 70, 0);
}

Sobald der Asteroid von einer Rakete oder dem Spieler getroffen wird, wird sein Ambient für 0.125 Sekunden kurz erhöht und danach wird der Asteroid entfernt. Wird der Spieler von einem Asteroiden getroffen, verliert er ein Leben. Die Funktion zur Anzeige der Leben setzt entsprechend dem Inhalt von Skill22 (Anzahl der Treffer) das Visible Flag auf on (oder off).

function display_lives()
{
    while (1)
    {
        if (player.skill22 == 0)
        {
            first_pan.visible = on;
            second_pan.visible = on;
            third_pan.visible = on;
            fourth_pan.visible = on;
            fifth_pan.visible = on;
        }
        if (player.skill22 == 1)
        {
            first_pan.visible = on;
            second_pan.visible = on;
            third_pan.visible = on;
            fourth_pan.visible = on;
            fifth_pan.visible = off;
        }
        if (player.skill22 == 2)
        {
            first_pan.visible = on;
            second_pan.visible = on;
            third_pan.visible = on;
            fourth_pan.visible = off;
            fifth_pan.visible = off;
        }
        if (player.skill22 == 3)
        {
            first_pan.visible = on;
            second_pan.visible = on;
            third_pan.visible = off;
            fourth_pan.visible = off;
            fifth_pan.visible = off;
        }
        if (player.skill22 == 4)
        {
            first_pan.visible = on;
            second_pan.visible = off;
            third_pan.visible = off;
            fourth_pan.visible = off;
            fifth_pan.visible = off;
        }
        if (player.skill22 == 5)
        {
            first_pan.visible = off;
            second_pan.visible = off;
            third_pan.visible = off;
            fourth_pan.visible = off;
            fifth_pan.visible = off;
        }
        wait (1);
    }
}
 
Diese Funktionen stellen die Partikel für die Motoren dar:
 
function ship_particles()
{
    temp.x = random(2) - 1;
    temp.y = random(10) + 2;
    temp.z = random(2) - 1;
    vec_add (my.vel_x, temp);
    my.alpha = 30 + random(50);
    my.bmap = particle_map;
    my.size = 7 + random(4);
    my.flare = on;
    my.bright = on;
    my.lifespan = 20;
    my.function = fade_particle;
}

function fade_particle()
{
    my.alpha -= 20 * time;
    if (my.alpha < 0) {my.lifespan = 0;}
}
 
Diese Funktionen sind nichts besonderes, temp.y gibt die korrekte Richtung für den Schweif an, und my.alpha -= 20 * time legt die Länge fest.

Nach fünf Treffern hat der Spieler alle Leben verloren und muss das Spiel mit R neu starten, wobei einfach die main Funktion neu aufgerufen wird.
 

Neues Auto

Ich weiß nicht, wie viele von Ihnen im Forum an diesem Skript interessiert waren, aber es funktioniert – Ich habe mich entschlossen den Code dafür zu entwickeln. Was ich hier versuchte war folgendes: Spieler in Egoperspektive steuern -> Auto Modell berühren -> in das Auto einsteigen und in Egoperspektive herumfahren -> Leertaste drücken, um aus dem Auto auszusteigen und wieder herumzugehen. Wenn ich mich dem Auto erneut nähere, kann ich wieder losfahren, usw.

Dies mag zunächst bestimmt als schwierige Aufgabe erscheinen – Mitnichten. Alles was ich tun muss, besteht im gegenseitigen Wechseln der Funktionen player_to_car und car_to_player. Wir plazieren ein Automodell ins Level und weisen ihm folgende Aktion zu:

action my_car
{
    my.enable_impact = on;
    my.event = player_to_car;
}

Das Auto wird auf meine Berührung reagieren, sehen wir uns an, was in diesem Fall passiert:

function player_to_car()
{
    wait (1);
    ent_remove (me);
    player.shadow = off;
    player._MOVEMODE = _MODE_DRIVING;
    player._FORCE = 1.5;
    player._BANKING = 0.1;
    player.__SLOPES = on;
    player.__WHEELS = on; // rotate only when moving
    player.__JUMP = off; // the car can't jump
    player.__STRAFE = off; // can't strafe
    player.__TRIGGER = on;
    astrength.pan = 1.5; // decrease rotation speed (pan)
    car_entity.visible = on;
    while (key_space == 0) {wait (1);} // press space to switch from car to player
    temp.x = player.x - 100 * sin(player.pan); // check to see if there is an empty space (near the car door) to deploy the player
    temp.y = player.y + 100 * cos(player.pan) ;
    temp.z = player.z;
    ent_create (car_mdl, player.pos, create_car);
    vec_set (player.pos, temp);
    car_entity.visible = off;
    car_to_player();
}
 
entity car_entity
{
    type = <rallycar.mdl>;
    layer = 10;
    view = camera;
    x = 20;
    y = 0;
    z = -60;
}
 
Das Auto wird entfernt, sobald es mit dem Spieler kollidiert. Der Spieler wird in ein Auto verwandelt (Sie werden die veränderten Parameter in der Aktion player_drive bemerken). Ich habe ein car_entity definiert, das exakt wie das eben entfernte Auto aussieht, und schalte es auf visible. Jetzt kann der Spieler solange herumfahren, wie er will. Sobald wir die Leertaste drücken, wird der Spieler in die Nähe der Tür gesetzt, und das Auto Modell wird wieder generiert:

function create_car()
{
    wait (1);
    vec_set(temp, my.x);
    my.pan = player.pan;
    temp.z -= 2000; // trace 2000 quants below the car
    trace_mode = ignore_me + ignore_passable + ignore_models + ignore_sprites;
    my.z -= trace (my.x, temp); // place the car at ground level
    my_car(); // start the action again
}

Ich benutze “trace” um sicherzustellen, dass das Auto auf den Boden plaziert wird. Die Aktion my_car wird erneut gestartet und so fängt alles von vorne an.

Ein kleines Detail: Wir lehren dem Spieler, sich wie ein Mensch zu verhalten, und nicht wie ein Auto. Dies geschieht hier:

function car_to_player()
{
    player._MOVEMODE = _MODE_WALKING;
    player._FORCE = 0.75;
    player._BANKING = -0.1;
    player.__SLOPES = off;
    player.__WHEELS = off;
    player.__JUMP = on;
    player.__DUCK = on;
    player.__STRAFE = on;
    player.__BOB = on;
    player.__TRIGGER = on;
    astrength.pan = 7; // restore the original value in move.wdl
}
 
Dieses Skript erledigt die Sache, kann aber natürlich noch verbessert werden. Überprüfen Sie doch den Platz neben der Tür, um sicherzustellen, dass der Spieler aussteigen kann, ohne in einer Wand stecken zu bleiben?