Questions from the forum

Top  Previous  Next

Q: I found this nice code for a physics car, but it doesn't have a camera that follows the car. Can you help me?

A: Here's an improved version of your code.

 

#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: I would need the code for a simple bike that moves and lean as it turns.

A: Here's an example.

 

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: I would like to put an animated water texture inside a WAD file. How can I do that?

A: All the animated textures from a WAD file will change their frames 8 times per second. If you want to create your own animated textures, make sure that their names starts with a "+" followed by a number between 0 and 9 (you don't need to use them all, of course). As  example, adding the "+0water", "+1water" and "+2water" textures to your wad will create an animated "water" texture with 3 animation frames.

 

 

Q: I want to retrieve the distance between a ball and the floor and have a continuous update of its distance displayed on my screen. The ball is moving up and down.

A: Here's a simple example that uses c_trace to compute the distance.

 

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: can someone show me working, tested code sample showing a sprite attached to a model?

A: Here's an example that attaches a flare sprite to a patrolling guard model.

 

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: I can't figure out how to have things move towards specific entities, for example a rocket that appears and moves towards an enemy.

A: Here's a fully functional example.

 

#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: With a "if (key_1)" instruction the player has to press 1 in order to continue. But this "key_1" only works with the "1" above the "A" (on AZERTY keyboards, or "Q" on  QWERTY keyboards) and not with the 1 from the numerical keys at the right side of the keyboard. Is there a solution to make it work with the 1 above the 0 key as well?

A: Sure, use the snippet below as a base for your code. Check the value displayed by the key_pan panel to see the scan code value for any other key on your keyboard (it's 79 for "1" here).

 

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: I'd like to know if the zero key was pressed and I can move the camera using the WSAD keys. Of course that I can test that by pushing the keys, but I'd like to know that just by looking at the monitor.

A: There you go.

 

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: I am not able to trigger the EVENT_SHOOT event. I'd like to have a text displayed when the moving "ent1" hits "ent2".

A: You are using the wrong event; EVENT_SHOOT is triggered by a c_trace instruction, not by the collision with a moving entity. Here's an example that uses the proper event - 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: How do I write a snippet which makes sure that a dead player doesn't react to mouse button clicks, pressed keys, etc?

A: The key here is to use player's health value to control all the needed functions; here's an example that disables the movement keys, the zoom-in key and the left mouse button as soon as player's health reaches zero.

 

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);                

       }

}