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?