Questions from the forum

Top  Previous  Next

Q: The code from Aum88's Player -> Car article has a small bug: the gun can fire even if the player doesn't pick it up.

A: Replace function fire_bullets( ) with the one below; I have updated the code inside aum88code.zip as well.

 

function fire_bullets()

{

       if (weapon1.roll != 0) {return;} // the player didn't pick up the weapon yet? Then don't allow the gun to fire!

       if (players_health <= 0) {return;} // can't fire bullets if the player is dead

       if (player_in_car == 1) {return;} // can't fire bullets while the player is driving

       VECTOR temp;

       proc_kill(4); // don't allow more than 1 copy of this function to run

       while (mouse_left) // this loop runs for as long as the left mouse button is pressed

       {

               front_offset -= 1; // move the weapon backwards a bit

               vec_for_vertex (temp.x, weapon1, 29); // get the coordinates for the bullets' origin

                // create the bullet at camera's position and attach it the "move_bullets" function

               ent_create (bullet_mdl, temp.x, move_bullets);

               ent_create (muzzle_tga, temp.x, display_muzzle); // create the gun muzzle

               snd_play (bullet_wav, 100, 0); // play the bullet sound at a volume of 100

               wait (-0.07); // fire 7 bullets per second (2 * 0.07 * 7 = 1 second)

               front_offset += 1; // restore the position of the weapon

               wait (-0.07);

       }

}

 

 

Q: I try to make a snippet that allows my enemies to respawn; when one of the enemies dies, another is created. Can you help?

A: Here's a snippet that generates enemies from a cave; you can use any other entity, of course. Click an enemy using the left mouse button to kill it.

 

ENTITY* enemy;

 

BMAP* pointer_tga = "pointer.tga";

 

function mouse_startup()

       mouse_mode = 2;

       mouse_map = pointer_tga;

       while (1)

       

               vec_set(mouse_pos, mouse_cursor);

               wait(1);

       }

}

 

function destroy_enemy() // this function runs if the player clicks the enemy using the left mouse button

{

       enemy = NULL; // free the enemy pointer

       ent_remove(my); // remove the enemy from the level; another one will be created

}

 

function move_enemy()

{

       var anim_percentage = 0;

       my.emask = ENABLE_CLICK; // the enemy is sensitive to mouse clicks

       my.event = destroy_enemy;

       my.z += 50; // create the enemy 50 quants above the origin of the cave model

       my.pan = random(360); // and give it a random pan angle

       while (1)

       {

               c_move (my, vector(5 * time_step, 0, 0), nullvector, GLIDE); // "5" controls the walking speed

     ent_animate(my, "walk", anim_percentage, ANM_CYCLE);

               anim_percentage += 3 * time_step; // "3" controls the animation speed

               anim_percentage %= 100;

               wait (1);

       }

}

 

action enemy_respawner() // attach this action to your cave model, etc

{

       set (my, PASSABLE);

       while (1)

       {

               while (enemy) {wait (1);} // don't do anything if the enemy exists already

               // create the enemy and attach it the function named move_enemy

               enemy = ent_create("guard.mdl", my.x, move_enemy);

       }        

}

 

 

Q: I use your function from Aum88's faq to detect the best resolution for my game; however, when I change the resolution by pressing the F5 key all the panels lose their positions. How can I make sure that my panels are properly aligned at any resolution?

A: Here's an example that uses four panels (one for each corner of the screen) and keeps them aligned at any resolution.

 

BMAP* upperleft_tga = "upperleft.tga";

BMAP* upperright_tga = "upperright.tga";

BMAP* lowerleft_tga = "lowerleft.tga";

BMAP* lowerright_tga = "lowerright.tga";

 

PANEL* upperleft_pan =

{

       bmap = upperleft_tga;

       layer = 15;

       flags = SHOW;

}

 

PANEL* upperright_pan =

{

       bmap = upperright_tga;

       layer = 15;

       flags = SHOW;

}

 

PANEL* lowerleft_pan =

{

       bmap = lowerleft_tga;

       layer = 15;

       flags = SHOW;

}

 

PANEL* lowerright_pan =

{

       bmap = lowerright_tga;

       layer = 15;

       flags = SHOW;

}

 

function align_startup()

{

       while (1)

       {

               // that's the easy part; the panel from the upper left corner will always be placed at x = 0 and y = 0;

               upperleft_pan.pos_x = 0;

               upperleft_pan.pos_y = 0;                

               // the panel from the upper right corner has x = horizontal resolution - panel bitmap width and y = 0;

               upperright_pan.pos_x = screen_size.x - bmap_width(upperright_tga);

               upperright_pan.pos_y = 0;                

               // the panel from the lower left corner has x = 0 and y = vertical resolution - panel bitmap height

               lowerleft_pan.pos_x = 0;

               lowerleft_pan.pos_y = screen_size.y - bmap_height(lowerleft_tga);

               // the panel from the lower right corner x = horizontal resolution - panel bitmap width

               // and y = vertical resolution - panel bitmap height

               lowerright_pan.pos_x = screen_size.x - bmap_width(lowerright_tga);

               lowerright_pan.pos_y = screen_size.y - bmap_height(lowerright_tga);

               wait (1);

       }

}

 

aum89_questions1

 

 

Q: What event type do I have to choose (impact, push, etc) if I want a ball to trigger an event when it passes through a passable sprite?

A: The easiest (and more accurate) method is to use vec_dist, checking the distance between the center of the ball and the center of the sprite - here's an example.

 

var movement_speed = 10;

 

ENTITY* ball;

 

action simple_ball() // attach this action to your ball entity

{

       VECTOR temp;

       ball = my;

       while (1)

       {

               temp.x = movement_speed * (key_cuu - key_cud) * time_step;

               temp.y = movement_speed * (key_cul - key_cur) * 0.6 * time_step;

               temp.z = 0; // use gravity, etc here

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

               wait (1);

       }

}

 

action passable_sprite() // attach this action to your passable sprite

{

       set (my, PASSABLE);

       while (!ball) {wait (1);} // wait until the player model is loaded

       while (1)

       {

               if (vec_dist (ball.x, my.x) < 50) // play with 50

               {

                       beep(); // do what you need here

                       // get rid of the "break" line if you want to trigger the event several times

                       break; // get out of the while loop

               }

               wait (1);

       }

}

 

 

Q: How can I split the big bitmap used as a heightmap for my terrain in several tiles?

A: Most graphic editors come with tools that allow you to slice images for the web; you can use the same tools to split your heightmap in several smaller bitmaps. Here's how it works with Paint Shop Pro 9.

 

aum89_questions2

 

aum89_questions3

 

 

Q: I've got two spheres and I would like to create a 3D line that connects them. How can I do that?

A: Here's an example.

 

ENTITY* s1;

ENTITY* s2;

 

action sphere1() // attach this line to the first sphere

{

       s1 = my; // I'm the first sphere

}

 

action sphere2() // attach this line to the second sphere

{

       s2 = my; // I'm the second sphere

}

 

function line_startup()

{

       while (!s1 || !s2) {wait (1);}

       while (1)

       {

               // set the starting point for the 3D line

               draw_line3d(vector(s1.x, s1.y, s1.z), NULL, 100);

               // draw a blue line that connects s1 and s2

               draw_line3d(vector(s2.x, s2.y, s2.z), vector(255, 0, 0), 100);

               wait (1);

       }        

}

 

aum89_questions4

 

 

Q: How would you detect if the player is entering a certain room?

A: You can use c_scan to do that.

 

STRING* detected_str = "#50";

 

TEXT* detected_txt =

{

       pos_x = 200;

       pos_y = 20;

       string(detected_str);

       flags = SHOW;

}

 

// attach this action to an entity that is placed inside the room, near the door

action detect_player()

{

       set (my, PASSABLE);

       // set (my, INVISIBLE); // remove this comment after you have set the proper scanning value

       while (!player) {wait (1);}

       VECTOR temp[3];

       while (1)

       {

               // scan for the player every 5 frames to save a bit of cpu power

               // the player will be detected if it comes closer than 200 quants to this entity

               c_scan(my.x, my.pan, vector(360, 90, 200), IGNORE_ME | SCAN_ENTS); // play with 200

               if (you == player) // detected the player?

               {

                       beep(); beep(); // do what you need here (a level_load instruction, etc)

                       str_cpy((detected_txt.pstring)[0], "The player has entered the room");

                       break; // get out of the loop, the player has entered the room

               }

               else

               {

                       str_cpy((detected_txt.pstring)[0], "The player is outside the room");

               }

               wait (5);

       }

}

 

 

Q: How can I disable the camera movement when the user presses the zero key? And how can I enable the camera movement without having to press the zero key?

A: The zero key works only while you are developing the game; it won't work in the published version of your game, so you don't have to do anything about that. Place any entity in your level and attach it the following action to create a freely moving camera that works without having to press the zero key; don't forget to build the level again.

 

// place a model in your level using Wed and then attach it this action

action my_camera()

{

       var speed_factor = 5;

       VECTOR camera_speed;

       set (my, INVISIBLE); // make the camera entity invisible

       while (1)

       {

               // use the left and right mouse buttons to move forward and backward; 5 gives the movement speed

               camera_speed.x = speed_factor * (mouse_left - mouse_right);

               // press and hold the shift key to increase the movement speed 6 times, useful for large areas

               if (key_shift)

               {

                       speed_factor = 30; // 5 * 6

               }

               else

               {

                       speed_factor = 5; // restore the default movement speed if the shift key isn't pressed

               }

               // no need to change the y and z components of the camera_speed vector

               camera_speed.y = 0;

               camera_speed.z = 0;

               my.pan -= 5 * mouse_force.x * time_step; // 5 = horizontal rotation speed

               my.tilt -= 3 * mouse_force.y * time_step; // 3 = vertical rotation speed

               // now move the entity in the direction given by its pan and tilt angles

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

               // the camera will inherit the same position and angles with the entity

               vec_set (camera.x, my.x);

               camera.pan = my.pan;

               camera.tilt = my.tilt;

               wait(1);

       }

}

 

 

Q: Is there any way to scan all the entities and check their skills rather then stopping at the closest one?

A: c_scan only detects the closest entity indeed, but it can trigger the EVENT_SCAN event for all the entities that enter the scanning cone - here's an example.

 

var entity_id = -1;

 

var value[100];

 

function show_info() // runs if the entities are scanned by the scanner

{

       while (event_type == EVENT_SCAN)

       {

               my.skill1 = 123; // set skill1 to 123 while the entity is being scanned

               value[my.skill2] = my.skill1; // and copy it to the "value" array

               my.scale_z = 4; // also increase the z scale of the entity that is being scanned

               wait (1);

       }

       my.scale_z = 1; // restore the original z scale of the entity if it isn't being scanned anymore

       my.skill1 = 0; // set skill1 to zero if the entity isn't scanned

       value[my.skill2] = 0; // reset "value"

}

 

action scanner() // attach this action to the entity that will scan the area

{

       while (1)

         {

               // scan using a pan angle of 120 degrees, a tilt of 60 degrees and a range of 500 quants

                 c_scan(my.x, my.pan, vector (120, 60, 500), IGNORE_ME | SCAN_ENTS | SCAN_LIMIT);

            c_move(my, vector(5 * time_step, 0, 0), nullvector, IGNORE_PASSABLE | GLIDE); // 5 = movement speed

            my.skill22 += 5 * time_step; // 5 gives the "walk" animation speed

            my.pan += 1 * time_step;

            ent_animate(my, "walk", my.skill22, ANM_CYCLE);

            wait (1);

       }

}

 

action scanned_objects() // attach this action to several entities

{

       entity_id += 1;

       my.skill2 = entity_id;

       my.emask |= ENABLE_SCAN; // the scanned entities are sensitive to c_scan instructions

       my.event = show_info;

}

 

PANEL* values_pan = // displays the first 10 "value" values

{

       layer = 15;

       digits(20, 20, 4 ,* , 1, value[0]);

       digits(20, 40, 4 ,* , 1, value[1]);

       digits(20, 60, 4 ,* , 1, value[2]);

       digits(20, 80, 4 ,* , 1, value[3]);

       digits(20, 100, 4 ,* , 1, value[4]);

       digits(20, 120, 4 ,* , 1, value[5]);

       digits(20, 140, 4 ,* , 1, value[6]);

       digits(20, 160, 4 ,* , 1, value[7]);

       digits(20, 180, 4 ,* , 1, value[8]);

       digits(20, 200, 4 ,* , 1, value[9]);

       flags = SHOW;

}

 

 

Q: Does anyone have any suggestions for a simple gravity system that works effectively? I need the objects to fall but also be able to slide along the floor and down the slopes under the gravity's effect.

A: There are no physics like the ones offered by a physics engine.

 

action physics_box() // this should be a function, but it can be used as a standalone action

{

       VECTOR kick_speed;

       ph_setgravity (vector(0, 0, -386));

       set (my, SHADOW);

       phent_settype (my, PH_RIGID, PH_BOX);

       phent_setmass (my, 20, PH_BOX);

       phent_setfriction (my, 50);

       phent_setdamping (my, 50, 50);

       phent_setelasticity (my, 30, 30);

       kick_speed.x = 250; // kick speed

       kick_speed.y = 0;

       kick_speed.z = 50; // make it move a bit upwards as well

       vec_rotate(kick_speed, camera.pan); // kick it depending on the angle of the camera

       phent_addvelcentral(my, kick_speed); // kick the box, comment this line to turn the box into a regular one

}

 

function create_boxes()

{

       VECTOR box_pos;

       vec_set(box_pos.x, vector(100, 0, 0));

       vec_rotate (box_pos.x, camera.pan);

       vec_add (box_pos.x, camera.x);

       ent_create ("box.mdl", box_pos.x, physics_box);

}

 

function init_startup()

{

       on_c = create_boxes; // creates a box when the player presses the "c" key

}

 

aum89_questions5