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