13 November 2009

Code of the Ninja: Watchers

If you haven't already, read the Code of the Ninja: Introduction

Welcome, Code Ninjas!

Today I have a simple fix for something that bugged me about Game Maker. First, let me tell you about the problem, and then I will move on to the solution.

Many times I would be running my game, and realise that there was some variable I needed to know the value of. Game Maker allows you to list the values of variables that you specify in a readout, but only when running in Debug Mode.

The trouble is, once you are already running your game, there is no way to dynamically switch to Debug Mode. You have to close the game, and run it again. This is kind of an annoyance, because compile time can get quite lengthy.

So, I thought, is there any way to have a list of "watchers" built into the game, that I can invoke and dismiss at will?

At first I used a simple method that worked much like any HUD (heads up display) in a video game. The values I needed to keep an eye on were just printed on the screen. This was fine and all, but there was no good way to add to it while the game was running. Game Maker's built in Debug Mode allows you to add watchers at any time.

Then I got the idea to - instead of printing out the values of a fixed set of variables - read the variables from a ds_list.

This ds_list would contain a series of strings, each of which would contain the name of the variable to watch (mouse_x, image_speed, etc). By right-clicking the list, I would bring up an input box that would let me add a new variable name to the ds_list.

How can you read the value of a variable by its name? Game Maker helpfully includes the functions variable_local_get(), and variable_global_get(), which take as their only argument the variable's name in string format, and return the value of the variable.

However, there are still issues with this method. A) You can only return the contents of variables, not expressions. This means you can print out the value of mouse_x and things like that, but never the value of instance_nearest(PlayerObj), and other such useful functions. B) You can't tell whether a variable is global or local, a constant, or part of an array, so you're pretty much screwed.

But then I had the brainwave. Instead of using variable_local_get() and its ilk, I'd use the execute_string() command!

Using this method, the ds_list could be filled with strings containing variable names, expressions, anything - using the identical syntax as the Game Maker Debug Mode watchers use. In fact, I could even save and load the strings to TXT documents in the same format, making them fully compatible with Game Maker's normal Debug Mode.

When drawing the watchers onto the screen, all I have to do is read the strings from the list. Then, I use the execute_string() command to perform the string as if it were code. By prepending "return" and a space before the string when I do this, execute_string() will return the value, which can be then drawn on the screen.

Code will demonstrate this better:


//a for loop that steps through the whole list
for (t=0;t<ds_list_size(watchlist);t+=1)
  //the code string in the list to be executed
  r = ds_list_find_value(watchlist,t);
  //align the text to the left
  //print the code - this is how you'll identify the watcher
  //return the result of the watcher
  r = execute_string("return "+r);
  //align the text to the right
  //print the result of the watcher

Now we have a watcher display that's just as nice as the Game Maker one. With a little bit of polish, you can add functions for adding, removing, replacing, and editing watchers from the list, as well as saving and loading lists to TXT documents.

For Code Ninjas who may need a working example to make sense of all this, here is a zip file which contains a GMK example and a sample TXT document of watchers.

Happy coding!

If you use my code or scripts in your game or engine, no credit is necessary. But I'd love to hear about your project if you do! Just drop me a comment below, or e-mail me at us.mercurysilver@gmail.com

No comments:

Post a Comment