Physics demo

A6 commercial and A6 pro include a good physics engine and this article will show you how easy it is to set it up.

Let's start with function main:

function main()
{
level_load (phdemo1_wmb);
camera.x = 0;
camera.y = -600;
camera.z = 40;
camera.pan = 90;
ball_pos.x = -95; // initial value, ranges from -95 to 105
ball_pos.y = 32;
ball_pos.z = 220;
ent_create (arrow_wmb, ball_pos, move_arrow);
}

We load the level and then we set the camera position and pan angle, to make it look at the board:

Before I forget, I have used a simple, oriented bitmap for the score.

We set ball_pos, and then we create the green arrow (arrow_wmb). The arrow moves all the time because of its associated function:

function move_arrow()
{
my.passable = on;
while (balls > 0)
{
while (my.x < 105)
{
my.x += 5 * time;
ball_pos.x = my.x;
wait (1);
}
while (my.x > -95)
{
my.x -= 5 * time;
ball_pos.x = my.x;
wait (1);
}
wait (1);
}
}

The arrow is passable and it will continue to move as long as we haven't used all the five balls (coming right on); the arrow will change its x coordinate from -95 to 105 using 2 simple while loops. Please note that ball_pos.x = arrow_wmb.x every frame.

Ok, so we've got a green wmb arrow that moves from left to right and back. Let's see what happens when we press the "Space" key:

entity* ball_ptr;

on_space = create_ball;

function create_ball()
{
if ((balls < 1) || (ball_ptr != null)) {return;}
ball_ptr = ent_create(ball_mdl, ball_pos, init_sphere);
balls -= 1;
}

We have defined a pointer named ball_ptr. We are going to create 5 balls and this pointer will be associated to every ball at its creation time. If we run out of balls or if the pointer to the ball isn't null (which means that the ball is still bouncing around in our level) the function returns. If the number of ball is bigger than zero we create a ball, at the position given by ball_pos, running the function init_sphere. This function is the only part of the demo that uses physics; don't worry, I will comment it line by line:

function init_sphere()
{
phent_settype(my, ph_rigid, ph_sphere);
Enable physics for this ball; it will behave like a sphere when it comes to collisions

phent_setmass(my, 1, ph_sphere);
The ball has 1Kg (~2 lbs) and behaves like a sphere when it comes to rotations

phent_setfriction(my, 30);
Friction = 30 (could be 0...100)

phent_setelasticity(my, 30, 0);
Bounce a little (value = 30, could be 0...100)

temp.x = 0;
temp.y = 0;
temp.z = -380;
ph_SetGravity(temp);
Set gravity for the sphere (temp.z = -380 corresponds to the earth gravity factor, g = 9.81 m/s2)

while (my.z > -150) {wait (1);}
Wait until the ball has approached the floor level

sleep (3);
Then give it three more seconds to bounce around

phent_setelasticity(my, 0, 0);
Now stop bouncing

phent_setdamping(my, 0, 100);
Apply a big damping factor to prevent the ball from rotating too much

sleep (2);
Roll on the floor for two more seconds

phent_settype(my, 0, 0);
Now stop the ball; A6 commercial allows us to use one object (ball) at a time

ball_ptr = null;
Free the pointer because we need to assign it to a new ball
}

This is all the code that makes the ball jump and bounce and finally find its way into one of the baskets. If you don't own A6 commercial or A6 pro yet, take a look at the movie and see what you could do with only a few lines of code. It would be almost impossible to code this realistic behavior without a physics engine.

Physics movie (divx 5.01)

Typing tutor

It is no secret that any Acknex user can develop all kinds of useful applications using the engine; this month I want to show you how to create a typing tutor "game". The idea behind this typing tutor is simple: you see a letter falling from the sky (ok, from the ceiling) and then you try to press the corresponding letter as fast as you can. If you aren't quick, the letter falls on the ground and you loose a point. There are several attack "waves", with letters that fall with bigger and bigger speeds.

When we run the level we see this image on the screen:

There are quite a few elements displayed on the screen so let's discuss them right away:

text start_txt
{
font = comic_font;
pos_x = 0;
pos_y = 200;
string = " Get ready, captain!\n Press any key to begin!";
flags = d3d, visible;
}

The first text displays the text in the center of the screen. Please note that I'm using newline (\n) to make the text appear on two rows. I will use this text to display other messages during the game.

text score_txt
{
font = comic_font;
pos_x = 10;
pos_y = 10;
string = "Total:\nCorrect:\nWave:";
flags = d3d, visible;
}

Score_txt displays the three lines of text in the upper left corner.

panel score_pan
{
pos_x = 0;
pos_y = 0;
digits = 250, 10, 3, comic_font, 1, total_chars;
digits = 250, 50, 3, comic_font, 1, correct_chars;
digits = 250, 90, 3, comic_font, 1, wave;
flags = refresh, d3d, visible;
}

This panel displays the three numerical values that appear in the upper left corner (0, 0, 1 in the picture).

I will start with the function that creates the letters:

function create_letters(number, interval)
{
while (number > 0)
{
letter = 1 + int(random(26));
temp.x = 0;
temp.y = 350;
temp.z = 450;
str_for_asc(temp_str, letter + 64);
str_cat(temp_str, ".wmb");
str_cpy(letter_str, temp_str);
str_trunc(letter_str, (str_len(letter_str) - 1));
ent_create(temp_str, temp, falling_letters);
letter_falls = 1;
while (letter_falls) {wait (1);}
str_cpy(temp_str, "");
number -= 1;
sleep(interval);
}
}

You can see that this function takes two parameters: the number of letters that will be created and the interval (in seconds) between two consecutive letters. If I run the function like this:

create_letters(4, 2);

it will create four letters that will appear every two seconds. Let's see the function one more time:

function create_letters(number, interval)
{
while (number > 0)
As long as the number of letters that need to be created is bigger than zero
{
letter = 1 + int(random(26));
generate an integer from 1 to 26
temp.x = 0;
temp.y = 350;
temp.z = 450;
set the coordinates for the letter in front and above player's head
str_for_asc(temp_str, letter + 64);
convert the number to the ascii char associated to the number (65..90)
str_cat(temp_str, ".wmb");
add ".wmb" to create something like this: p.wmb
str_cpy(letter_str, temp_str);
copy the letter.wmb string in letter_str
str_trunc(letter_str, (str_len(letter_str) - 1));
now keep only the first character of the string = the letter
ent_create(temp_str, temp, falling_letters);
now create the letter
letter_falls = 1;
set this var to 1 -> it will be used in another function
while (letter_falls) {wait (1);}
wait until the other function sets letter_falls to zero
str_cpy(temp_str, "");
reset temp_str
number -= 1;
decrease the number of letters
sleep(interval);
wait the number of seconds given by interval and repeat the steps above.
}
}

When we create a letter, it will run its associated function - falling_letters():

function falling_letters()
{
total_chars += 1;
my.enable_block = on;
my.event = destroy_letter;
falling_speed.x = 0;
falling_speed.y = 0;
falling_speed.z = -7;
falling_speed.z *= wave;
falling_speed.z *= time;

We increase total_chars, the figure associated to "Total:" in the game. The letter will be sensitive to level blocks, which will trigger its destroy_letter event. Falling_speed is negative on z so the letter will fall down. I have told you that the program uses "waves"; in fact, we use a variable named wave that is set to 1 for the first level, 2 for the second level and so on. Finally, falling_speed is multiplied by time to make sure that we have the same frame rate on any pc.

while ((my != null) && (my.event != null))
{
ent_move (falling_speed, nullvector);
str_for_key(temp_str, key_lastpressed);
if (str_cmpi(letter_str, temp_str) && key_any)
{
correct_chars += 1;
snd_play(key_wav, 30, 0);
letter_fades();
str_cpy(letter_str, "");
while (key_any) {wait (1);}
}
else
{
if (key_any)
{
destroy_letter();
}
}
wait (1);
}
}

As long as the letter exists and its event isn't set to null, we move the letter downwards, we convert the scan code to its associated key and then we compare the falling letter with the key that was pressed by the player. If these two strings match, we increase correct_chars, "Correct:" on our display and then we play a small sound. We run a small function that makes the letter fade away and then we reset the string named letter_str, otherwise we could increase correct_chars several times for a single letter, which wouldn't be correct, isn't it? We wait until the key that was pressed (any key) is released.

If the match isn't true, we have typed the wrong letter so if our finger is pressing the wrong key we call the function named destroy_letter:

function destroy_letter()
{
my.event = null;
ent_remove (me);
letter_falls = 0; // the letter has stopped
snd_play(error_wav, 50, 0);
}

This function sets event to null, removes the letter, tells the function create_letters that the current letter has disappeared so it can create a new letter and then plays an error sound. The function that fades the letters is simple:

function letter_fades()
{
my.alpha = 100;
my.transparent = on;
while (my.alpha > 5)
{
my.alpha -= 10 * time;
wait (1);
}
ent_remove (me);
letter_falls = 0; // the letter has disappeared, create a new one
}

The letter is made transparent but if its alpha is set to 100 it looks opaque; its alpha will be decreased until it is smaller than 5. When this happens, the letter is removed and letter_falls is reset so a new letter will be created.

The last function is main(); it looks quite complicated this time but all the code inside it is simple:

function main()
{
on_d = null;
randomize();
fps_max = 30;
level_load (tutor_wmb);
sleep (1);
camera.x = 0;
camera.y = -350;
camera.z = 150;
camera.pan = 90;
camera.ambient = 100;

We disable the debug key because we wouldn't want to show the debug panel when letter "D" is falling. We make sure that we generate random numbers (random letters) every time we run the engine, we limit the frame rate to 30 fps and then we load the level. We wait for a second and then we set the camera position and pan angle. I thought that the textures I've used are a bit too dark so I have set camera.ambient to 100 in order to make the level look brighter.

while (key_any == 0) {wait (1);}
start_txt.string = " First wave - prepare!";
sleep (2);
start_txt.visible = off;
create_letters(10, 2);
while(total_chars < 10) {wait (1);}

Do you remember the text definitions above? These texts are visible at game start; check their flags if you don't believe me. This means that the text "Get ready, captain!..." is visible on the screen at game start. We wait until the player presses any key and then we change the string used by the text so now we will see "First wave - prepare" on the screen. We wait two more seconds, we hide the text and then we create 10 letters, every 2 seconds, using our beloved create_letters function. We wait until all the letters are created (total_chars = 10).

The things are similar from now on but don't worry, I will comment the code anyway:

sleep (2); // wait for 2 more seconds
start_txt.string = " Second wave - prepare!";
start_txt.visible = on;
sleep (3); // for 3 seconds
start_txt.visible = off;
wave = 2;
create_letters (10, 1);
while(total_chars < 20) {wait (1);}

We wait for two seconds, we change the string for the text and then we make the text visible. We wait for three seconds and then we hide the text, we set wave to 2; this will increase the falling speed. We create 19 letters that fall every second and then we wait until the second wave is finished (total_chars = 20).

sleep (2);
start_txt.string = " Third wave - prepare!";
start_txt.visible = on;
sleep (3);
start_txt.visible = off;
wave = 3;
create_letters (10, 0.5);
while(total_chars < 30) {wait (1);}
sleep (5); // wait for 5 more seconds
start_txt.string = " Impressive result!";
start_txt.visible = on;
sleep (5);
exit;
}

We wait for two more seconds, we change the string, we make the text visible for three seconds, we set wave to 3 and we create 10 more letters; a letter could fall every 0.5 seconds if you have a big typing speed. We wait until the third wave is finished (total_chars = 30) and then we display "Impressive result!" which isn't quite true because the text will appear even if you miss all the letters. This text will appear for five seconds and then the engine will shut down. And this article will do the same thing.