Previous: Using the debugging tools

Advanced debugging

If your game has more than a few lines of code, it may (and probably will) have bugs. You know how to use watches now, but what can you do when the values of the variables change way too quickly? And what if you need to "watch" a specific entity and not the value of a variable?

Let's open the Script17 file right away

///////////////////////////////
 #include <acknex.h>
 #include <default.c>
///////////////////////////////
ENTITY* plane;
   
action rotate_plane()
{
   plane = me;
   my.ambient = 100;
   while (1)
   {
       my.pan += 3.5 * time_step;
       if (my.pan == 360)
           sys_exit(NULL);
       wait (1);
   }
}
   
function main()
{
   level_load ("work17.wmb");
   vec_set(camera.x, vector(-500, 0, 100));
   ent_create("mig.mdl", vector(0, 0, 0), rotate_plane);
}   

We have used a similar script in workshop 10, but I have made a few small changes to the code. I have added an entity pointer named "plane", I have set the ambient of the plane to 100 and I have added a line that shuts down the game as soon as the plane has done a full revolution (pan == 360). Let's see if the code does that indeed!

The plane rotates as expected, but Lite-C doesn't shut down after a complete rotation; in fact, it doesn't shut down at all! What could be wrong with these few lines of script? Press the [Esc] key to exit the program and let's "watch" the entity pointer named plane.

You already know the procedure from the last workshop: right click into the Watch area (or go to Debug / Add Watch), and enter "plane" (without the quotes) in the Variable name field. If the old "energy" watch from our previous workshop is visible, you can safely delete it by right clicking it, and then choosing "Delete watch". Back to our plane: be sure to name the watch using the same name with the variable or entity that you are tracking; otherwise, the watch will be useless! The Watch tab as shown below displays the newly created watch.

Time to run the script file again! Aren't you curious to see the values for plane? Don't forget to use DebugRun debugrun

As you can see, we are now watching several values: position, angles, scale, as well as some other parameters of the entity pointer. I have tried to catch the moment when the pan angle was close to 360 degrees, but the pan angle variation steps are too big because the watches are updated only twice a second (every 500 ms). Let's try to update the watches more often! Exit the engine and go to Lite-C's Options / Preferences / Environment:

Choose 200 ms instead of 500 ms, press OK and then Debug Run debugrun the script17 file again.

It's the same thing, if not worse! The figures change way too fast now! What should we do then? It would be nice if we could stop the while loop where we want it while we are debugging the code, isn't it? Well... we can do that!

Exit the engine. In a Debug Run you can also do that using Lite-C's Stop Debugging button. Move the cursor to the first line of code inside the while loop, click it, and then press [F9]. That line should now be highlight in red:

We have just added a breakpoint to the script file. The engine will stop every time it encounters a breakpoint in debug mode, and the debugger will be activated for single stepping through our script. Please be patient, you'll see what "single stepping" means pretty soon. By the way, besides entering a breakpoint in Lite-C you can also type a "//!" comment in the script after the line you want the engine to stop at. This will place a permanent breakpoint into the code.

Now, let's run script17 again:

The plane has stopped and you can see a line of code in the title bar of the engine window.

my.pan += 3.5 * time_step;

Now press the [Single Step] key once. This is normally the [F10] key; check out or set the debugging keys the way you want them in Lite-C's Options -> Customize Hotkeys. If you have pressed [F10], you have noticed that some things are changed; the plane has rotated a little and now we can see a different line in the title bar:

if (my.pan == 360)

Also in Lite-C's window the cursor has changed, indicating the current instruction. Pressing the [Single Step] key causes the debugger to execute the code line by line. You can see in the Watch area that the plane has now a pan angle of 10 degrees. Let's press [Single Step] ([F10] by default) again:

wait (1);

The result of the comparison (my.pan == 360) was false; that's how it is supposed to be, because my.pan has only 10 degrees. Consequently, the next line in the code is skipped and we're now at the "wait (1);" instruction. Press [Single Step] again until the cursor has done a full while loop and is at its initial line:

my.pan += 3.5 * time_step;

Ten degrees are added once again to the pan angle, who is now set to 24. You might see different numerical values on your computer, but this doesn't matter at all. When single stepping through code, time_step does not reflect the time taken by the last frame cycle - this would be a very long time because the engine has to wait until we press a key! Instead, time_step is set to a fixed value while we are debugging the code.

Now we could press [Single Step] over and over until we would reach the needed 360 degrees; however, this would take quite a bit of time! An easier way of doing this is by clicking the Debug Run debugrun button. This causes the program to run until the next breakpoint is hit, which happens at the beginning of the while, loop after a full loop cycle is being executed. Click Debug Run debugrun repeatedly and observe the pan angle:

10 - 24 - 38 - 52 - 66 - 80 - 94 - ... - 304 - 317 - 331 - 345 - 359 - 373 !

Ah, I know! I have tried to shut down the application when the plane's pan angle was equal to 360 degrees, but this was never happening! Now I see why the engine won't shut down: pan jumps from 359 to 373 so it never reaches 360 degrees! And since time_step normally has an arbitrary value any frame, it's even more unlikely that pan will ever grow to be exactly 360. We now know for sure how to fix our bug:

if (my.pan >= 360)
    sys_exit(NULL);

Watching entities

There is another debugging feature that can make your life easier and its name is "watched". It is a predefined ENTITY* pointer that Lite-C knows already, a pointer for an entity to be permanently watched on the engine screen. You could set this pointer in the script, but it's a lot cooler to set it by just a mouse click while the engine is running. Let's try that straight away.

Remove the breakpoints and run the script again. It does not matter this time whether you run the code using DebugRun or TestRun. Now press [Shift-F11]. The spinning plane suddenly stops as if it was hit by a breakpoint! Now move the mouse pointer over the plane and click it. Some strange values will appear at the top of the screen:

You can see the type of the entity (MDL), its internal name used by WED (mig_mdl_000), the name of the file (MIG.MDL), the position (x, y, z), the angles (pan, tilt, roll), its scale, lighting values, its ambient, color, animation, flags, action and many more, including even the name of the texture that's placed below the plane entity! You can find a detailed description of all those values in the reference manual.

Now press [Shift-F11] again and the plane will go on spinning, but its values will stay on the screen, allowing you to observe the state of the plane even when it's not visible on the screen anymore. This is a very useful feature for checking what's happening with your entities. For instance, if you want to observe the values of a certain watched entity during the entire game, just add that entity to Lite-C's Watch list.

I bet you're feeling a bit tired... Hold on, we'll end this workshop soon. Some of you might have noticed that pan can have values that are bigger than 360 degrees. Could this bean engine bug? No, the values of the angles increase / decrease all the time if the entity continues to rotate in the same direction. The engine has no problem with angles above 360 or less than zero; this feature is useful if you want to determine the number of rotations for an entity. Want to know when the plane has rotated two times around its pan angle? Then check if its pan angle is over 2 * 360 = 720 degrees! If you need to keep your angles in the 0...359 degrees range, you can place a single line of code inside the while loop to solve the problem:

my.pan %= 360; // limit pan to 0...359 degrees

Place this line in the script17.c file, right after the instruction that adds 3.5 * time_step to the pan value. But... believe it or not, this version of the code has a bug as well! Run the modified script file and you will see for yourself: the engine will (once again) refuse to shut down! Your homework is to find and kill this nasty bug.

Solution: my.pan %= 360 limits the pan angle to 0...359.999 degrees, so my.pan will never be equal to 360 degrees. Change the "if" branch this way:

if (my.pan >= 359)
    sys_exit(NULL);

Next: Entity movement