GameMaker: Debug Logger

Hi everyone,

I went round and around with people on Twitter today. We were tossing around a bunch of ideas for convenient helper scripts and things that we did to make our lives easier in GameMaker. I thought that I would take the opportunity to write about something that can make your life a hell of a lot easier! A debug logger! In short, the debug logger will allow you to print information to the screen and each entry will allow you to specify different properties like how long it is drawn on the screen, whether it is pushed to the top or drawn in-line, its draw color, maybe whether or not it is indented, and so on (anything that you’d like, really).

For starters, you can probably tell by that last bit that this feature is very specific to your project, so your debug logger might not have the same set of properties as mine does. I will do my best to explain things in a way where the code remains modular and manipulable without becoming overly complex. As usual, contact me if you are in need of extra help!

Storing Data
There are plenty of ways to store data in GameMaker. For our purposes today, I would say that our best bet would likely be a standard 2D array. Note that you can pass arrays, 2D or otherwise, to scripts, but they are passed by reference and that the script will create a copy of that array to manipulate within itself. From there, you will need to return that new array and store it at the address of your initial array. This is both inefficient and can also get rather messy. Luckily, GameMaker allows you to access and manipulate the original array using the accessor ‘@’. I will cover this when it is necessary. For now, we just need to initialize a 2D array for storing our debug log data.

/// DebugLogCreate(properties, size);

var dlp, dls, dla;

dlp = argument0;
dls = argument1;

for (var j = 0; j < dls; j++) {
    for (var i = 0; i < dlp; i++) {
        dla[i, j] = 0;
    }
}

return (dla);

Alright, so here we have a simple script that expects two parameters: the number of properties per entry in your log (text, draw color, etc) and the maximum number of entries at any given time (just be reasonable about how much text can/will be drawn along the left-hand side of the view port, simultaneously). I decided to let my game controller object store and initialize the debug logger. I did this by entering the following into the create event of my object, ‘oGame’.

_debugLogProperties = 5; // KeyStr, TextStr, frames, indent, color
_debugLogSize       = 16;

_debugLog = DebugLogCreate(_debugLogProperties, _debugLogSize);

It may be good to note that arrays need to be freed just like any other data structure or manually allocated memory in GameMaker. You do this by setting the variable to zero (_debugLog = (0);). Simple, but important!

Updating Functionality
We now have a way to store all of our data. What exactly are we trying to do with it? Well, for my example (this is what I am currently using to debug some functionality in Fara) I am using a debug entry that includes five properties: a key string (to prevent duplicate entries), the printed text string, how many frames to print the text ((-1) to permanently add it to the list), whether or not to indent the entry, and the draw color. I am treating the ‘frames’ property as a draw timer; almost like how GameMaker treats alarms. I will use this to determine which entries are being drawn, and I will decrement the ‘frames’ property of each entry via an update script.

EDIT: I went back and pushed all of the entries up towards the front of the list each time an entry dies out. This way, there will never be needless space between entries when one ends before another that follows.

/// DebugLogUpdate(array, size);

var dla, dls;

dla  = argument0;
dls  = argument1;

for (var i = 0; i < dls; i++) {
    if (dla[@ 2, i] > 0) {
        dla[@ 2, i]--;
        
        // If entry dies
        if (dla[@ 2, i] == 0) {
            // Shift all remaining entries forward
            for (var j = (i + 1); j < dls; j++) {
                if (dla[@ 2, j] &gt 0) {
                    for (var k = 0; k &lt 5; k++)
                        dla[@ k, (j - 1)] = dla[@ k, j];
                    dla[@ 2, j] = 0; 
                }
            }
            
            //i--;
        }
    }
}

Ok, so this is where things get a tad ‘hack-y’ and your implementation may begin to differ from mine. This script updates the draw state of my debug log entries by decrementing the ‘frames’ property of each entry. Also, note that I am using the ‘@’ accessor that I mentioned earlier! This allows me to read from and/or modify the true array rather than the copy of the referenced array. It is both simpler to use and more efficient. Again I will show how and where I call this script for ease of use if you are simply copying my debug log structure. The following is placed within the step event of my game controller.

/// DebugLog Update

if (!_isReleaseBuild) {
    DebugLogUpdate(_debugLog, 2, _debugLogSize);
}

Actually drawing the debug log information shouldn’t be too difficult. I added an option indenting entries, horizontally, to show one tweak that you can do with these entries, stylistically. For the most part, this should be pretty straight-forward. A few notes:

1) Using the ‘@’ accessor again.
2) I am using a few helper scripts that return the position of the view.
3) You could just decrement ‘frames’ here, but I like to keep logic out of the draw event
4) Personally, I choose to reset the draw color to white after any object or script changes it to anything other than white.

/// DebugLogDraw(array, size);

// Param(s)
var dla, dlp, dls;

dla = argument0;
dls = argument1;

// Property(ies)
var pKey, pText, pFrames, pIndent, pColor;

pKey    = 0;
pText   = 1;
pFrames = 2;
pIndent = 3;
pColor  = 4;

// Draw offsets
var ox, oy, th, iw;

ox = (ViewX() + 8); // Starting draw x-coord 
oy = (ViewY() + 8); // Starting draw y-coord
th = 20;            // Height of a text string (vertical distance between entries)
iw = 8;             // Additional offset for indented entries

for (var i = 0; i < dls; i++) {          
    if ((dla[@ pFrames, i] > 0) || (dla[@ pFrames, i] == (-1)) {
        draw_set_color(dla[@ pColor, i]);
        draw_text((ox + (dla[@ pIndent, i] * iw)), (oy + (th * i)), dla[@ pText, i]);
    }    
}

// Reset draw color
draw_set_color(c_white);

Our final step is the ability to add entries to our debug log. This is another situation where my scripts and my choice of implementation (debug log is held by the game controller) are coupled. Either the DebugLogAdd() script or the objects calling this script will need to be aware which object holds the logger. This isn’t an issue, more so just something that you need to be aware of.

/// DebugLogAdd(array, size, key, text, frames, indent, color);

var dla, dls, ek, et, ef, ei, ec, perm, found;

dla   = argument0;
dls   = argument1;
ek    = argument2;
et    = argument3;
ef    = argument4;
ei    = argument5;
ec    = argument6;
perm  = (ef == (-1)); // Entry is permanent and should update rather than duplicating
found = false;

if (perm) {
    for (var i = 0; i < dls; i++) {
        // Find permanent entries
        if (dla[@ 2, i] == -1) {
            // Compare against key
            if (dla[@ 0, i] == ek) {
                dla[@ 0, i] = ek;
                dla[@ 1, i] = et;
                dla[@ 2, i] = ef;
                dla[@ 3, i] = ei;
                dla[@ 4, i] = ec;
                
                found = true;
                break;
            }
        }
    }
}

if (!found) {
    for (var i = 0; i &lt dls; i++) {
        // Find an entry that is not in use
        if (dla[@ 2, i] == 0) {
            dla[@ 0, i] = ek;
            dla[@ 1, i] = et;
            dla[@ 2, i] = ef;
            dla[@ 3, i] = ei;
            dla[@ 4, i] = ec;

            break;
        }
    }
}

Iterate across the array and find the first entry that isn’t in use. BOOM, pass some parameters into that bad boy and you are good to go! I decided not to pass the ‘frames’ property to this script (like I did for DebugLogUpdate()). They are already somewhat coupled, so I said screw it. Feel free to drop it from one, the other, or both (I went back and removed it from both).

You should be good to go! Have a few objects test out the code. I will slap together a quick GIF that shows a permanent entry or two, along with a few dynamic entries.

EXAMPLE:
1) The player (x, y) position is permanent and is black.
2) Horizontal input shows up for 20 frames and is blue.
3) Vertical input shows up for 30 frames, is red, and is indented.

/// DEBUG LOG TEST

// Pos
DebugLogAdd(oGame._debugLog, oGame._debugLogSize, "Position", string("(x, y) : (" + string(x) + ", " + string(y) + ")"), (-1), false, c_black);

// L
if (keyboard_check_pressed(vk_left))
    DebugLogAdd(oGame._debugLog, oGame._debugLogSize, "LEFT", string("KEY_LEFT_PRESSED"), 20, false, c_blue);

// R
if (keyboard_check_pressed(vk_right))
    DebugLogAdd(oGame._debugLog, oGame._debugLogSize, "RIGHT", string("KEY_RIGHT_PRESSED"), 20, false, c_blue);

// U
if (keyboard_check_pressed(vk_up))
    DebugLogAdd(oGame._debugLog, oGame._debugLogSize, "UP", string("KEY_UP_PRESSED"), 30, true, c_red);

// D
if (keyboard_check_pressed(vk_down))
    DebugLogAdd(oGame._debugLog, oGame._debugLogSize, "DOWN", string("KEY_DOWN_PRESSED"), 30, true, c_red);

debuglogtut

That’ll do it for tonight! I hope this helps some of you.

Have a good one,
Z

GameMaker: Debug Logger

3 thoughts on “GameMaker: Debug Logger

  1. Hi Zack, thank you for another great tutorial!. I was wondering if there is any chance you could make a tutorial on how to make the wave effect from hacky zacky whenver you hit the ball please? or maybe just mail me a few pointers on how to do it?

    Like

  2. rokero9911 says:

    Hi Zack, thank you for another great tutorial!. I was wondering if there is any chance you could make a tutorial on how to make the wave effect from hacky zacky whenver you hit the ball please? or maybe just mail me a few pointers on how to do it?

    Liked by 1 person

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s