Previous: Entity movement

Using the physics engine

In this workshop we'll learn a bit about the third movement method that's described in the last lesson: physics-based movement. For this workshop you'll need Gamestudio/A8 or better (if you are still using A7, please read work19o.htm for working with ODE physics). Make sure that the nVidia PhysX engine is installed on your PC - this is normally the case through installing Gamestudio/A8. Start SED, then open the script19x.c file. Run the project and you'll find yourself in a game level, where you can control a ball using the arrow keys.

The goal of this game (yes, it's a game!) is to move the ball until it reaches the small platform at the top of the level. Press the arrow keys and you will see that the ball moves as if an invisible force was applied to it. Well, in fact, an invisible force is indeed applied to it!

#include <acknex.h>
#include <default.c>
#include <ackphysx.h>

VECTOR ball_force;
ENTITY* ball;
function main()
  level_load("roller.wmb"); // load the level
  ball = ent_create ("ball.mdl", vector(-400, 0, 100), NULL); // create the ball
  pXent_settype (ball, PH_RIGID, PH_SPHERE); // set the physics entity type
  pXent_setfriction (ball,50); // set the friction on the ground
  pXent_setdamping (ball,10,10); // set the damping
  pXent_setelasticity (ball,50); // set the elasticity
  while (1)
    ball_force.x = (key_cur-key_cul)*10*time_step; // rotate the ball about the global x axis
    ball_force.y = (key_cuu-key_cud)*10*time_step; // rotate the ball about the global y axis
    ball_force.z = 0; // no need to rotate about the vertical axis
    pXent_addtorqueglobal (ball, ball_force); // add a torque (an angular force) to the ball
    camera.x = ball.x - 300; // keep the camera 300 quants behind the ball
    camera.y = ball.y; // using the same y with the ball
    camera.z = 1000; // and place it at z = 1000 quants
    camera.tilt = -60; // make it look downwards

Can you believe it? We're only using about 20 lines of code and everything happens inside function main! Now that's what I call the 'lite' in lite-C :)

You can see that we have included another file: ackphysX.h. This file contains all the physics commands, so we need to include it whenever we want to use physics.

Also, we've defined a VECTOR named ball_force. A VECTOR is similar to a var, but consisting of three values: x, y, and z. We will store the rotation force of the ball on the x, y and z axis inside it. Then, we have defined an ENTITY pointer named ball, which will be used for our ball.

In the main function we're first "switching on" the physics engine: physX_open(). Including ackphysX.h and calling physX_open() are the two things required for using physics. A roller.wmb level, which was created in WED, is loaded next. Then, we create the ball, at x = -400, y = 0, z = 100 in the level, using the ball.mdl file. We have defined an entity pointer named ball, and this is the name that's given to our newly created entity.

The following lines of code register our ball entity with the physics system and set its properties. Don't worry, we well discuss them one by one.

- pXent_settype (ball, PH_RIGID, PH_SPHERE) tells our ball to become a physics entity. It is allowed to move (PH_RIGID) with a shape like a sphere (PH_SPHERE). You see, the shape of our model doesn't set its proper physics behavior automatically. If I'd simply replace my ball.mdl sphere with a cube, the brand new cube would continue to rotate and roll as if it were a sphere because pXent_settype has told it to behave like one, get it?

- pXent_setfriction (ball, 50) sets the ball's friction coefficient to 50. The value can range from 0 (ice-like friction) to 100 (rubber-like friction).

- pXent_setdamping (ball, 10, 10) sets the linear damping to 10 and the angular damping to 10. It's a simple and yet effective method to simulate airdrag and friction; without damping, our ball would continue to rotate ad infinitum.

- pXent_setelasticity (ball, 50) sets the bounciness factor for our ball to 50. If it were 0, the ball would not bounce at all but just stick to the floor.

I know that we've got a lot of fresh information here, but play with all the numerical values and you'll learn what they're about quickly. Let's step right into the while loop:

while (1)

       ball_force.x = (key_cur-key_cul)*10*time_step;
       ball_force.y = (key_cuu-key_cud)*10*time_step;
       ball_force.z = 0;

These lines of code set the proper ball_force.x and ball_force.y values, depending on the status of the cursor (aka arrow) keys on the keyboard; key_cur is the predefined variable that tracks the status of the right arrow key, key_cul tracks the status of the left arrow key, and so on. Every key has such a variable; when the key is pressed, the variable has a value of 1, otherwise 0. As an example, let's see the possible values for ball_force.x, depending on the status of key_cur and key_cul:

a) ball_force.x = 0 when none of the keys is pressed because key_cur = 0, key_cul = 0, so 10 * time_step * (0 - 0) = 0; the ball gets no force;
b) ball_force.x = 10 * time_step when the player presses the right arrow key because key_cur = 1, key_cul = 0, so 10 * time_step is multiplied with (1 - 0) and the ball gets a positive force;
c) ball_force.x = -10 * time_step when the player presses the left arrow key because key_cur = 0, key_cul = 1, so 10 * time_step is multiplied with (0 - 1) and the ball gets a negative force;
d) ball_force.x = 0 when the player presses the left arrow key and the right arrow key at the same time because key_cur = 1, key_cul = 1, so the multiplication factor is (1 - 1) = 0, the ball gets no force.

The same things happen with the next line of code, which takes care of the y force; by combining the x and y forces of the ball, we can roll it to any spot in the level. This (key_a - key_b) type of thing is common, being used in lots of game in order to control player's movement, and so on - you'll see many similar samples in further workshops and in the AUM.

You can see that the ball doesn't move at all about the z axis; you can use a similar line of code here and make the ball rotate clockwise and counterclockwise about the z axis depending on the status of another two keys on the keyboard if you want to. Let's look at the following line of code inside the loop:

       pXent_addtorqueglobal (ball, ball_force);

The pXent_addtorqueglobal instruction applies a torque (an angular force) to an entity. In our case, the force given by the vector named ball_force is applied  to the entity named ball, making it rotate about the x or y axis when the player presses the arrow keys.

If you followed our time_step explanation in workshop 10, you might wonder why we multiply the angular force with time_step. This makes the force the smaller, the faster the frame rate is. We have understood that the covered distance per frame depends on the frame rate, but a force should not - or should it? In this case, yes. You can see that we apply the force every frame inside the while loop. So, every frame our ball gets a little kick. If we would kick it with the same force any time, it would roll faster when the frame rate is high because it gets more kicks then. We eliminate this effect by using time_step for reducing the kick force dependent on the frame rate.

       camera.x = ball.x - 300;
       camera.y = ball.y;
       camera.z = 1000;
       camera.tilt = -60;

The last few lines of code inside the loop set a proper position for the camera (our "eye" inside the level). They place the camera 300 quants behind the ball on the x axis, while keeping the same y with it at all times. Finally, the camera is placed at 1000 quants in the level on the z axis and looks downwards because its tilt angle is set to -60 degrees.

I hope that you are now encouraged to learn even more about the physics engine. Read the reference manual to see the other functions that are available, and above all - make experiments!


Next: Sprites. Animated sprites

Further reading: Gamestudio Manual, Physics.