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:
//////////////////////////////////// var plane_pan; //////////////////////////////////// function main() { level_load ("work17.wmb"); } action rotate_plane() { my.ambient = 100; while (1) { my.pan = my.pan + 2; plane_pan = my.pan; if (my.pan == 360) { sys_exit(NULL); } wait (1); } }
We have used a similar script in the 10th workshop, but I have made a few small changes to the code. I have set the ambient of the plane to 100 and I have defined a variable (plane_pan) that has the same value with the plane's pan angle all the time. Why did I do that? Well, I want to "watch" the pan angle of the plane, and with SED versions prior to 6.50 we could only "watch" variables or vectors, not my.pan or other entity related stuff. SED versions 6.50 and above (you can recognize them from the two black and red Run buttons) can watch entities directly and we'll practice that later. Read the previous workshop to get more information about watches if you haven't done that already.
But what's with the plane? It uses a while loop
that adds 2 degrees to its pan angle every frame and after a full rotation
(pan = 360) it shuts down the engine. Let's see if the code does that indeed!
The plane rotates as expected, but the engine 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 variable named plane_pan.
Now go to Debug / Add Watch:
Add a watch named "plane_pan" (without the quotes):
Be sure to use the same name for the watch and for the variable, otherwise your watch is useless! Now click the Watch tab as shown below to display the newly created watch:
Time to run the script file again! Aren't you curious to see the values for plane_pan?
I have tried to catch the moment when plane_pan was close to 360 degrees but the steps are too big because the watches are updated every 500 ms! Let's try to update the watches more often! Exit the engine and switch to SED; choose Options / Preferences / Environment:
Choose 200 ms instead of 500 ms, press
OK and then Debug Run 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 this while loop 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 in SED with the Stop Debugging button. Move the cursor to the first line in the while loop, then press [F9]. The line should now highlight in red:
I have 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 SED you can also type "breakpoint;" in the script before 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 upper left corner of the engine window.
<= my.pan = my.pan + 2
Now press the Single Step key once. This is normally the F10 key (tip: SED and engine versions prior to 6.50 had different keys for single stepping: Space in the engine and F4 in SED. Check which version you're using!).
Some things have changed! The plane has rotated a little and now we can see two lines of code in the engine window:
3.000 <= my.pan = my.pan + 2
<= plane_pan = my.pan
Also in SED this line in the script is now highlighted, indicating the current instruction. Pressing the Single Step key causes the debugger to execute the code line by line, and to display the result of the operation on the left side in the engine window. It is obvious that the plane has a pan angle of 3 degrees, so its initial pan value must have been 1 degree. The watch displays zero at the moment because the line of code that sets plane_pan = my.pan hasn't been executed yet. Let's press Single Step again!
3.000 <= plane_pan = my.pan + 2
<= (my.pan == 360)
The value stored in my.pan was copied to plane_pan (both of them are set to 3 now) and guess what? Our watch displays the same value! Let's press Single Step again!
0.000 <= (my.pan == 360)
<= wait (1)
The result of the operation (my.pan == 360) is 0 (false); that's how it is supposed to be, because my.pan has only 3 degrees now! If my.pan would have had a value of 360 degrees, the result of the operation would have been 1 (true) and the first line would have looked like this:
1.000 <= (my.pan == 360)
We are coming close to the end of the while loop because I see that wait (1) instruction in the text displayed by the debugger. Let's press Single Step one more time:
1.000 <= wait (1)
<=
)
You can see that wait (1) was executed so the while loop will be soon over. Press Single Step again:
0.000 <= )
<=
(1)
The engine will check if the (1) from while (1) is true. I have told you that "while (1)" is the same thing with "while (1 == 1)" which is always true. Let's press Single Step again:
1.000 <= (1)
<=
my.pan = my.pan + 2
We've got the correct result for "while (1)" - that's the 1.000 on the left, which means "true". I'm really curious to see how the next line of code works (press Single Step):
5.000 <= my.pan = my.pan + 2
<=
plane_pan = my.pan
The plane has again rotated a little! Oh, I see... its pan angle has 5 degrees now. I'm sure that the value will be copied in my watch as soon as I press Single Step again!
5.000 <= plane_pan = my.pan
<= (my.pan == 360)
Ok, so now the things will repeat over and over and my.pan will change its value this way: 1, 3, 5, 7, ... You can press Ctrl-F10 (or click the Debug Run button again) to deactivate the debugger until it detects a new breakpoint; this time it will find a new one pretty soon because we've put the breakpoint in a while loop. What could be wrong with this code? Why doesn't it shut down the engine when the pan angle reaches 360 degrees? Where is the bug? Uncle Tom?
(Two weeks later) I know! I have set a wrong initial pan angle for the plane in WED! If the plane starts with an initial odd value for its pan angle, the values will look exactly like the ones that were displayed by the watch: 1, 3, 5, 7, ... 357, 359, 361, 363... Now I see why the engine won't shut down: pan jumps from 359 to 361 so it never reaches 360 degrees! Let me check this thing right away in WED.
I'm really sorry! The default pan angle in WED is 0 degrees so I must have rotated the plane a little by mistake ;) because its pan angle is -1 degrees! Change the pan angle back to90 degrees, build the level again and everything will work as expected.
What if I don't believe you? I want to see that the pan angle jumping from 359 to 361 degrees! And I don't want to press that Single Step key a thousand times to get there! Couldn't we activate the debugger only when a certain condition is met?
There is no function for that, but a little trick will do:
////////////////////////////////////
var plane_pan;
////////////////////////////////////
function main()
{
level_load ("work17.wmb");
}
action rotate_plane()
{
my.ambient = 100;
while (1)
{
my.pan = my.pan + 2;
plane_pan = my.pan;
if (my.pan > 358) {
my.pan = my.pan;
}
if (my.pan == 360)
{
sys_exit(NULL);
}
wait (1);
}
}
Insert the red lines into the code, and place the the breakpoint straight at the "my.pan = my.pan;" line! That remarkable piece of code will do nothing, but it is only executed when my.pan exceeds 358 degrees, and then 'catches' the breakpoint. Run the script and you will now see that the plane stops when plane_pan == 359 degrees:
Now press the Single Step key several times and you will see that the next value for plane_pan is 361 degrees. The plane jumps from 359 to 361 degrees, so the branch that shuts down the engine can't ever run.
There is another debugging feature that can make your life easier and its name is "watched". It is an ENTITY* pointer, a predefined name for an entity that will be permanently watched on the engine screen. You could set this pointer in the script, but you can even 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 Debug Run or Test Run. Now press [Shift-F11]. The spinning plane suddenly stops as if hit by a breakpoint! Now move the mouse pointer over the plane and click it. Some strange values 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, PRV lighting values, ambient, color, animation, flags, action and many more! 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 stay on the screen and allow you to observe the fate 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 skill17 value of a certain watched entity during the whole game, just add "watched.skill17" to SED'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 plane_pan can have values that are bigger than 360 degrees. The picture above shows a pan value of 641.0 degrees! Could this be an 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 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! And if your angles need to be 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
Put that line right after adding 2 to the pan value.
But... believe it or not, this version
of the code has a bug! Run the modified script file and you will see for
yourself: the engine refuses to shut down, even if we set the correct pan angle
for the plane in WED. Your homework is to find and kill this nasty bug.
Solution: my.pan %= 360 limits the pan angle to 0...359 degrees, so it will never be equal to 360 degrees. Change the "if" branch this way:
if (my.pan >= 358)
{
sys_exit(NULL);
}