Fragen aus dem Forum

Top  Previous  Next

Q: Ich habe diesen netten Code für ein Physik-Auto gefunden. Leider gibt es aber keine Kamera, die dem Auto folgt. Können Sie mir helfen?

A: Hier eine verbesserte Version Ihres Codes.

 

#include <acknex.h>

#include <default.c>

 

//vars, vectors, entities and angles

var constr_front_left;

var constr_front_right;

 

var constr_back_left;

var constr_back_right;

 

VECTOR mom_speed;

var max_angle;

var stear_contr;

 

var max_speed = 1500;

var ang_force;

var speed_contr;

 

VECTOR* vec_gravity = {x = 0; y = 0; z = -1800;}

 

ENTITY* chassis;

 

function wheel_physics_init()

{

       set (my, SHADOW);

       phent_settype (my, PH_RIGID, PH_SPHERE);

       phent_setmass (my, 30, PH_SPHERE);

       phent_setgroup (my, 2);

 

       phent_setfriction (my, 100);

       phent_setdamping (my, 20, 20);

       phent_setelasticity (my, 0, 100);

}

 

// front wheels

function front_tyre_left()

{

       wheel_physics_init();

       constr_front_left = phcon_add (PH_WHEEL, chassis, my);

       phcon_setparams1 (constr_front_left, my.x, vector (0,0,1), nullvector);

       phcon_setparams2 (constr_front_left, nullvector, nullvector, nullvector);

}

 

function front_tyre_right()

{

       wheel_physics_init();

       constr_front_right = phcon_add (PH_WHEEL, chassis, my);

       phcon_setparams1 (constr_front_right, my.x, vector (0,0,1), nullvector);

       phcon_setparams2 (constr_front_right, nullvector, nullvector, nullvector);

}

 

 

// rear wheels

function back_tyre_left()

{

       wheel_physics_init();

       constr_back_left = phcon_add (PH_WHEEL, chassis, my);

       phcon_setparams1 (constr_back_left, my.x, nullvector, vector (0,1,0));

       phcon_setparams2 (constr_back_left, nullvector, nullvector, nullvector);

}

 

function back_tyre_right()

{

       wheel_physics_init();

       constr_back_right = phcon_add (PH_WHEEL, chassis, my);

       phcon_setparams1 (constr_back_right, my.x, nullvector, vector (0,1,0));

       phcon_setparams2 (constr_back_right, nullvector, nullvector, nullvector);

}

 

function chassis_init()

{

       chassis = my;

       set (chassis, SHADOW);

       //init physics variables

       phent_settype (chassis, PH_RIGID, PH_BOX); // rigid and box as collision hull

       phent_setmass (chassis, 15, PH_BOX); // lighter than wheels to avoid tilt

       phent_setgroup (chassis, 2); // all parts of the car in one group --> no collision

 

       phent_setfriction (chassis, 20);

       phent_setdamping (chassis, 5, 5);

       phent_setelasticity (chassis, 10, 100); // only little bouncing

 

       //init car wheels

       //back

       ent_create ("car_tyre_left.mdl", vector (chassis.x - 65, chassis.y - 45, chassis.z - 20), back_tyre_left);

       ent_create ("car_tyre_right.mdl", vector (chassis.x - 65, chassis.y + 45, chassis.z - 20), back_tyre_right);

       //front

       ent_create ("car_tyre_left.mdl", vector (chassis.x + 65, chassis.y - 45, chassis.z - 20), front_tyre_left);

       ent_create ("car_tyre_right.mdl", vector (chassis.x + 65, chassis.y + 45, chassis.z - 20), front_tyre_right);

       wait(1);

}

 

void main()

{

       level_load ("test.wmb");

       //initialize the physics sub-system:

       ph_setgravity (vec_gravity);

       ph_fps_max_lock = 300;

       //init chassis

       ent_create ("chassis.mdl", vector (0,0, 100), chassis_init);

       while (!chassis) {wait (1);} // wait until the chassis appears in the level

       // speed and steering control

       while (1)

       {

               // steering control

               stear_contr = (key_d - key_a);//d = 1, a = -1, a & d = 0

 

               max_angle += stear_contr * 3 * time_step;

               max_angle = clamp (max_angle, -30, 30);

 

               if (stear_contr != 0)

               {

                       phcon_setparams2 (constr_front_left, vector (max_angle, max_angle, 0), nullvector, vector (95000, 500, 0));

                       phcon_setparams2 (constr_front_right, vector (max_angle, max_angle, 0), nullvector, vector (95000, 500, 0));

               }

               else

               {

                       max_angle = max_angle * (1 - (0.25 * time_step));

                       if (abs(max_angle) < 1)

                               max_angle = 0;

                       phcon_setparams2 (constr_front_left, vector (max_angle, max_angle, 0), nullvector, vector (95000, 500, 0));

                       phcon_setparams2 (constr_front_right, vector (max_angle, max_angle, 0), nullvector, vector (95000, 500, 0));

               }

               // speed control

               speed_contr = (key_w - key_s);//w = 1, s = -1, w & s = 0

               ang_force = speed_contr * 1000 * time_step;

 

               phcon_setmotor (constr_back_left, nullvector, vector(-ang_force, max_speed, 0), nullvector);

               phcon_setmotor (constr_back_left, nullvector, vector(-ang_force, max_speed, 0), nullvector);

 

            camera.x = chassis.x - 250 * cos(chassis.pan);

               camera.y = chassis.y - 250 * sin(chassis.pan);

               camera.pan = chassis.pan; // the camera and the car have the same pan angle

               camera.z = chassis.z + 50; // place the camera above the car, play with this value

               camera.tilt = -15; // look downwards

               

               wait(1);

       }

}

 

 

Q: Ich bräuchte den Code für ein einfaches Bike, das sich bewegt und in die Kurven lehnt.

A: Hier ein Beispiel:

 

action my_bike() // attach this action to your bike model

{        

       var movement_speed = 0; // initial movement speed

       var rotation_speed = 3; // rotation speed

       VECTOR bike_speed, temp;

       player = my;

       while (1)

       {

               // change the pan angle of the bike if the player presses the left / right cursor keys

               my.pan += rotation_speed * (key_cul - key_cur) * time_step;

               // also change the roll angle of the bike if the player presses left / right

               if (key_cul) // the player has pressed the left cursor key?

               {

                       if (my.roll > -20)

                       {

                               my.roll -= 5 * time_step;

                       }        

               }

               else

               {

                       if (my.roll < 0)

                       {

                               my.roll += 5 * time_step;

                       }        

               }

               if (key_cur) // the player has pressed the right cursor key?

               {

                       if (my.roll < 20)

                       {

                               my.roll += 5 * time_step;

                       }        

               }

               else

               {

                       if (my.roll > 0)

                       {

                               my.roll -= 5 * time_step;

                       }        

               }

               // 15 gives the acceleration, 0.1 gives the friction

               vec_set(bike_speed.x, accelerate (movement_speed, 15 * (key_cuu - key_cud), 0.1));

               bike_speed.y = 0;

               vec_set (temp.x, my.x);

               temp.z -= 10000;

               // 12 gives the distance to the ground, play with it

               bike_speed.z = -c_trace (my.x, temp.x, IGNORE_ME | IGNORE_PASSABLE | USE_BOX) + 12;

               c_move (my, bike_speed.x, nullvector, IGNORE_PASSABLE | GLIDE);                

            camera.x = my.x - 250 * cos(my.pan); // put the camera 250 quants behind the bike

               camera.y = my.y - 250 * sin(my.pan);

               camera.pan = my.pan; // the camera and the bike have the same pan angle

               camera.z = my.z + 70; // place the camera above the bike, play with this value

               camera.tilt = -10; // look downwards                

               wait (1);

       }

}

 

aum92_faq1

 

Q: Ich würde gerne eine animierte Wassertextur in eine WAD-Datei einfügen. Wie mache ich das?

A: Sämtliche animierten Texturen aus einer WAD-Datei wechseln ihre Frames 8 mal pro Sekunde. Wollen Sie Ihre eigenen animierten Texturen erstellen, sorgen Sie dafür, dass deren Namen mit einem "+" beginnen und dann eine Zahl zwischen 0 und 9 folgt (Sie brauchen natürlich nicht alle benutzen). Wenn Sie beispielsweise die Texturen "+0water", "+1water" und "+2water" in Ihre WAD aufnehmen, wird eine animierte "water"-Textur mit 3 Animationsframes erstellt. 

 

 

Q: Ich möchte den Abstand zwischen einem Ball und dem Boden zurückliefern und ein kontinuierliches Update seines Abstands auf meinem Bildschirm angezeigt bekommen. Der Ball bewegt sich auf und nieder.

A: Hier ein einfaches Beispiel, das zum Berechnen des Abstands c_trace benutzt.

 

var distance_to_ground = 0;

 

FONT* arial_font = "Arial#20b";

 

ENTITY* ball;

 

action my_ball() // simple ball action

{        

       ball = my; // for further use

       VECTOR temp;

       while (1)

       {

               my.z += 0.3 * sin(total_ticks);

               vec_set (temp.x, my.x);

               temp.z -= 10000;

               distance_to_ground = c_trace (my.x, temp.x, IGNORE_ME | IGNORE_PASSABLE | USE_BOX) - 2;

               wait (1);

       }

}

 

PANEL* info_pan =

{

       layer = 15;

       digits(30, 40, "Distance to ground: %.f", arial_font, 1, distance_to_ground);

       flags = SHOW;

}

 

aum92_faq2

 

 

Q: Kann mir jemand funktionierenden und getesteten Code zeigen, der ein an einem Modell angebrachtes Sprite zeigt?

A: Hier ein Beispiel, das einem patroullierenden Wachen-Modell ein Flare-Sprite anfügt.

 

var entity_speed = 3;

var movement_enabled = 0;

var dist_to_node;

var current_node = 1;

 

VECTOR temp_angle;

VECTOR pos_node; // stores the position of the node

VECTOR temp;

 

function attach_sprite() // attaches a sprite to guard's left thumb

{

       proc_mode = PROC_LATE; // this is the key instruction - it eliminates the lagging        

       // the sprite must be made passable; otherwise it would hinder the movement of the guard

       set (my, PASSABLE | BRIGHT); // no need to make the sprite bright if you don't want to

       my.scale_x = 0.2; // set the scale of the sprite according to your needs

       my.scale_y = my.scale_x;

       while (1)

       {

               vec_set(my.x, temp.x); // get the updated        coordinates of guard's left thumb each frame

               wait (1);

       }

}

 

function move_target()

{

       var stand_percentage, walk_percentage;

       ent_create("muzzle.tga", my.x, attach_sprite); // create the sprite at player's origin initially

       while(1)

       {

               if(movement_enabled)

               {

                       entity_speed = minv(5, entity_speed + 0.5 * time_step);

                       c_move(my, vector(entity_speed * time_step, 0, 0), nullvector, IGNORE_PASSABLE | GLIDE);

                       walk_percentage += 4 * time_step;

                       ent_animate(my, "walk", walk_percentage, ANM_CYCLE); // play the "walk" animation

                       vec_to_angle (my.pan, vec_diff (temp_angle, pos_node, my.x));

               }

               else // standing still? (no path could be found)

               {

                       stand_percentage += 2 * time_step;                        

                       ent_animate(my, "stand", stand_percentage, ANM_CYCLE); // play the "stand" animation

               }

               vec_for_vertex (temp, my, 500); // get the coordinates of guards 500th vertex (its left thumb)                                

               wait(1);

       }

}

 

action guard_with_sprite() // attach this action to the guard model

       move_target();

       result = path_scan(me, my.x, my.pan, vector(360, 0, 500)); // scan the area

       if (result) {movement_enabled = 1;}

       path_getnode (my, 1, pos_node, NULL);

       vec_to_angle (my.pan, vec_diff (temp_angle, pos_node, my.x)); // rotate towards the node

       while(1)

       {

               dist_to_node = vec_dist(my.x, pos_node);

               if(dist_to_node < 50) // close to the node?

               {

                       current_node = path_nextnode(my, current_node, 1);

                       if (!current_node) {current_node = 1;} // reached the end of the path? Then start over!

                       path_getnode (my, current_node, pos_node, NULL);

               }

               wait(1);

       }

}

 

aum92_faq3

 

 

Q: Ich finde nicht heraus wie man Dinge in Richtung spezifischer Entities hinbewegt, beispielsweise eine Rakete, die erscheint und sich in Richtung eines Feindes bewegt.

A: Hier ein voll funktionierendes Beispiel:

 

#include <acknex.h>

#include <default.c>

 

SOUND* rocket_wav = "rocket.wav";

SOUND* destroyed_wav = "destroyed.wav";

 

STRING* heatseek_wmb = "heatseek.wmb";

STRING* rocket_mdl = "rocket.mdl";

 

function fire_rocket();

function shoot_rocket();

function remove_rocket();

function enemy_event();

 

function main()

{

       fps_max = 70;

       camera.ambient = 100;

       video_mode = 7; // run in 800x600 pixels

       video_depth = 32; // 32 bit mode

       video_screen = 1; // start in full screen mode

       level_load (heatseek_wmb);

       wait (3);

       on_mouse_left = fire_rocket;

}

 

action player_moves() // attach this action to your player

{        

       var movement_speed = 20;

       VECTOR temp;

       set (my, INVISIBLE);

       player = my;

       while (1)

       {

               my.pan -= 7 * mouse_force.x * time_step;

               camera.x = my.x;

               camera.y = my.y;

               camera.z = my.z + 50 + 1.1 * sin(my.skill44);

               camera.pan = my.pan;

               camera.tilt += 5 * mouse_force.y * time_step;

               vec_set (temp.x, my.x);

               temp.z -= 10000;

               temp.z = -c_trace (my.x, temp.x, IGNORE_ME | IGNORE_PASSABLE | USE_BOX) - 2;

               temp.x = movement_speed * (key_w - key_s) * time_step;

               temp.y = movement_speed * (key_a - key_d) * 0.6 * time_step;

               c_move (my, temp.x, nullvector, IGNORE_PASSABLE | GLIDE);

               wait (1);

       }

}

 

action enemy() // attach this action to your enemies (they need to be sensitive to scanning)

{

       my.emask |= (ENABLE_SCAN);

       my.event = enemy_event;

}

 

function fire_rocket()

{

       ent_create (rocket_mdl, player.x, shoot_rocket);

       snd_play (rocket_wav, 50, 0);

}

 

function shoot_rocket()

{

       VECTOR rocket_speed, temp;

       my.emask |= (ENABLE_ENTITY | ENABLE_BLOCK);

       my.event = remove_rocket;

       set (my, PASSABLE);

       my.pan = camera.pan;

       my.tilt = camera.tilt;

       my.skill20 = 0;

       vec_set(rocket_speed.x, nullvector);

       rocket_speed.x = 10 * time_step;

       my.skill10 = 0;

       while (my.skill20 < 500)

       {

               if (vec_dist (player.x, my.x) > 200)

                       reset (my, PASSABLE);

               if ((my.skill10 == 0) && (vec_dist (my.x, player.x) > 100))

               {

                       c_scan(my.x, my.pan, vector(40, 60, 500), IGNORE_ME | SCAN_ENTS); // play with 500

               }

               my.skill20 += 1 * time_step;

               c_move (my, rocket_speed.x, nullvector, IGNORE_FLAG2 | IGNORE_PASSABLE);

               wait (1);

       }

       remove_rocket();

}

 

function enemy_event()

{

       VECTOR temp;

       if (event_type == EVENT_SCAN)

       {

               you.skill10 = 1; // stop scanning

               while (you != NULL) // as long as the rocket exists

               {

                       vec_set (temp.x, my.x);

                       vec_sub (temp.x, you.x);

                       vec_to_angle (you.pan, temp.x); // rotate it towards the target

                       wait (1);

               }

       }

}

 

function remove_rocket()

{

       wait (1);

       my.event = NULL;

       ent_playsound (my, destroyed_wav, 1000); // play the explosion sound

       set (my, INVISIBLE); // hide the rocket, keep ent_playsound playing

       wait (-1.5); // wait for 1.5 seconds

       ent_remove(me); // now remove it

}

 

 

Q: Mit einer "if (key_1)"-Anweisung muß der Player zum Weiterkommen auf 1 drücken. Dieses "key_1" funktioniert aber nur mit der "1" über dem "A" oder (bei QWERTY-Tastaturen) dem "Q". Mit der "1" im Nummernblock rechts auf der Tastatur funktioniert es nicht. Gibt es eine Lösung, daß das auch mit der "1" über der "0" funktioniert?

A: Sicher, nehmen Sie das folgende Schnipselchen als Basis für Ihren Code. Prüfen Sie den Wert, der vom key_pan-Panel angezeigt wird und finden Sie so den Scan-Code-Wert für jedwede andere Taste auf Ihrer Tastatur heraus (hier ist es 79 für "1").

 

SOUND* alarm_wav = "alarm.wav";

 

function alarm_startup()

{

       var alarm_handle;

       while (1)

       {

               if (key_1 || key_pressed(79)) // the player can press the regular "1" key or the one on the numerical keyboard

               {

                       if (!snd_playing(alarm_handle)) // the alarm sound isn't playing already?

                       {

                               alarm_handle = snd_play(alarm_wav, 70, 0); // then let's play it!

                       }

               }

               wait (1);

       }

}

 

PANEL* key_pan = // displays the code of the last key that was pressed

{

       layer = 15;

       digits(600, 20, 3, *, 1, key_lastpressed);

       flags = SHOW;

}

 

 

Q: Ich würde gerne wissen, ob die Nulltaste gedrückt wurde und ich die Kamera mithilfe der WSAD-Tasten bewegen kann. Klar kann ich das testen, indem ich die Tasten drücke, ich würde das aber gerne einfach durch einen Blick auf den Monitor wissen.

A: Hier haben Sie was Sie brauchen:

 

TEXT* camera_txt =

{

       pos_x = 20;

       pos_y = 20;

       flags = SHOW;

}

 

function camera_startup()

{

       var camera_activated = 0;

       while (1)

       {

               if (key_0)

               {

                       while (key_0) {wait (1);} // wait until the zero key is released

                       camera_activated += 1;

                       camera_activated %= 2;                

               }

               if (camera_activated == 0)

                       str_cpy ((camera_txt.pstring)[0], "Camera Inactive");

               else

                       str_cpy ((camera_txt.pstring)[0], "Camera Active");

               wait (1);

       }

}

 

 

Q: Ich kann den EVENT_SHOOT-Event nicht auslösen. Ich hätte gerne, daß ein Text angezeigt wird wenn die sich bewegende "ent1" auf "ent2" trifft.

A: Sie verwenden den falschen Event. EVENT_SHOOT wird durch eine c_trace-Anweisung ausgelöst und nicht durch die Kollision mit einer sich bewegenden Entity. Hier ein Beispiel, das den geeigneten Event benutzt - EVENT_IMPACT:

 

#include <acknex.h>

#include <default.c>

 

SOUND* rocket_wav = "rocket.wav";

SOUND* destroyed_wav = "destroyed.wav";

 

STRING* rocket_mdl = "rocket.mdl";

 

function fire_rocket();

function shoot_rocket();

function remove_rocket();

function enemy_event();

 

function main()

{

       fps_max = 70;

       camera.ambient = 100;

       video_mode = 7; // run in 800x600 pixels

       video_depth = 32; // 32 bit mode

       video_screen = 1; // start in full screen mode

       level_load ("test.wmb");

       wait (3);

       on_mouse_left = fire_rocket;

}

 

TEXT* gotme_txt =

{

       pos_x = 20;

       pos_y = 20;

       flags = SHOW;

}

 

action player_moves() // attach this action to your player

{        

       var movement_speed = 20;

       VECTOR temp;

       set (my, INVISIBLE);

       player = my;

       while (1)

       {

               my.pan -= 7 * mouse_force.x * time_step;

               camera.x = my.x;

               camera.y = my.y;

               camera.z = my.z + 50 + 1.1 * sin(my.skill44);

               camera.pan = my.pan;

               camera.tilt += 5 * mouse_force.y * time_step;

               vec_set (temp.x, my.x);

               temp.z -= 10000;

               temp.z = -c_trace (my.x, temp.x, IGNORE_ME | IGNORE_PASSABLE | USE_BOX) - 2;

               temp.x = movement_speed * (key_w - key_s) * time_step;

               temp.y = movement_speed * (key_a - key_d) * 0.6 * time_step;

               c_move (my, temp.x, nullvector, IGNORE_PASSABLE | GLIDE);

               wait (1);

       }

}

 

function fire_rocket()

{

       ent_create (rocket_mdl, player.x, shoot_rocket);

       snd_play (rocket_wav, 50, 0);

}

 

function shoot_rocket()

{

       VECTOR rocket_speed, temp;

       my.emask |= (ENABLE_ENTITY | ENABLE_BLOCK);

       my.event = remove_rocket;

       set (my, PASSABLE);

       my.pan = camera.pan;

       my.tilt = camera.tilt;

       my.skill20 = 0;

       vec_set(rocket_speed.x, nullvector);

       rocket_speed.x = 10 * time_step;

       my.skill10 = 0;

       while (my.skill20 < 500)

       {

               if (vec_dist (player.x, my.x) > 100)

                       reset (my, PASSABLE);

               if ((my.skill10 == 0) && (vec_dist (my.x, player.x) > 100))

               {

                       c_scan(my.x, my.pan, vector(40, 60, 500), IGNORE_ME | SCAN_ENTS); // play with 500

               }

               my.skill20 += 1 * time_step;

               c_move (my, rocket_speed.x, nullvector, IGNORE_FLAG2 | IGNORE_PASSABLE);

               wait (1);

       }

       remove_rocket();

}

 

function remove_rocket()

{

       wait (1);

       my.event = NULL;

       ent_playsound (my, destroyed_wav, 1000); // play the explosion sound

       set (my, INVISIBLE); // hide the rocket, keep ent_playsound playing

       wait (-1.5); // wait for 1.5 seconds

       ent_remove(me); // now remove it

}

 

action enemy() // attach this action to your sensitive entities

{

       my.emask |= (ENABLE_IMPACT); // they need to be sensitive to impact

       my.event = enemy_event;

}

 

function enemy_event()

{

       if (event_type == EVENT_IMPACT) // collided with another entity?

       {

               my.event = NULL; // don't react to other events for a while

               str_cpy ((gotme_txt.pstring)[0], "Ouch! That hurt, man!");

               wait (-3); // display the "Ouch" text for 3 seconds

               str_cpy ((gotme_txt.pstring)[0], ""); // reset the string (hides the message)

               my.event = enemy_event; // the enemy is sensitive to events again

       }

}

 

aum92_faq4

 

 

Q: Wie schreibe ich einen Code, der dafür sorgt, daß ein toter Player nicht auf Klicks mit den Maustasten, gedrückte Tasten usw. reagiert?

A: Der Schlüssel hierzu ist der Gesundheitswert des Players, der wird zum Kontrollieren sämtlicher gebrauchter Funktionen benutzt. Hier ist ein Beispiel, das die Bewegungstasten, die Zoom-Taste und die linke Maustaste abschaltet sobald die Playergesundheit Null erreicht hat:

 

var players_health = 100;

 

action dead_player() // attach this action to your player

{

       var movement_speed = 20;

       VECTOR temp;

       my.skill40 = 0;

       while (players_health > 0)

       {

               // decrease player's health slowly - this should be done by the enemy hits, of course

               players_health -= 0.4 * time_step;

               if (key_w || key_s || key_a || key_d)

               {

                       ent_animate(my, "walk", my.skill40, ANM_CYCLE); // play the "walk" animation

                       my.skill40 += 5 * time_step; // "walk" animation speed

               }

               else

               {

                       ent_animate(my, "stand", my.skill40, ANM_CYCLE); // play the "stand" animation

                       my.skill40 += 3 * time_step; // "stand" animation speed

               }

               vec_set (temp.x, my.x);

               temp.z -= 10000;

               temp.z = -c_trace (my.x, temp.x, IGNORE_ME | IGNORE_PASSABLE | USE_BOX) - 2;

               temp.x = movement_speed * (key_w - key_s) * time_step;

               temp.y = movement_speed * (key_a - key_d) * 0.6 * time_step;

               c_move (my, temp.x, nullvector, IGNORE_PASSABLE | GLIDE);

               if (mouse_left)

                       beep(); // just a beep here, you would have a firing function in real-life situations

               wait (1);

       }

       // the player is dead here, so the movement keys and the left mouse button won't work anymore

       my.skill40 = 0; // skill40 controls the "death" animation

       while (my.skill40 < 95) // don't play all the animation frames because the result doesn't always look good

       {

               ent_animate(my, "death", my.skill40, NULL); // play the "death" animation

               my.skill40 += 2 * time_step; // "death" animation speed

               wait (1);

       }

       set (my, PASSABLE); // the corpse will be passable from now on

}

 

function zoom_startup() // allows the player to zoom in by pressing the "Z" key only if the player is alive

{

       while (1)

       {

               // if (key_z) // this line would keep the zooming code active even after player's death, so don't use it

               if ((key_z) && (players_health > 0)) // this line takes into account player's health as well

               {

                       camera.arc -= 10 * time_step; // 10 gives the zoom-in speed

                       camera.arc = maxv(15, camera.arc); // make sure that camera.arc's value doesn't go below 15

               }

               else

               {

                       if (camera.arc < 60) // camera.arc is below the default value?

                               camera.arc += 12 * time_step;        // 12 gives the zoom-out speed

               }

               wait (1);                

       }

}