GameMaker: Loading bar + animated image in HTML5


Mouseover to view animation (note: mild color cycling)

A year and some ago, I published an extension that allows to use custom logos for HTML5 games made with GameMaker: Studio. Since some functionality has been added since then, I made an updated version with more options.

Setting up

The process of setting up the extension is much as with the original (see linked post), but with a few changes:

First, the extension' JS code is expanded a little,

var inst = { };
function ImageLoadBar_hook(ctx, width, height, total, current, image) {
    function getv(s) {
        if (window.gml_Script_gmcallback_imgloadbar) {
            return window.gml_Script_gmcallback_imgloadbar(inst, null,
                s, current, total,
                width, height, image ? image.width : 0, image ? image.height : 0)
        } else return undefined;
    }
    function getf(s, d) {
        var r = getv(s);
        return typeof(r) == "number" ? r : d;
    }
    function getw(s, d) {
        var r = getv(s);
        return r && r.constructor == Array ? r : d;
    }
    function getc(s, d) {
        var r = getv(s);
        if (typeof(r) == "number") {
            r = r.toString(16);
            while (r.length < 6) r = "0" + r;
            return "#" + r;
        } else if (typeof(r) == "string") {
            return r;
        } else return d;
    }
    // get parameters:
    var backgroundColor = getc("background_color", "#FFFFFF");
    var barBackgroundColor = getc("bar_background_color", "#FFFFFF");
    var barForegroundColor = getc("bar_foreground_color", "#242238");
    var barBorderColor = getc("bar_border_color", "#242238");
    var barWidth = getf("bar_width", Math.round(width * 0.6));
    var barHeight = getf("bar_height", 20);
    var barBorderWidth = getf("bar_border_width", 2);
    var barOffset = getf("bar_offset", 10);
    // background:
    ctx.fillStyle = backgroundColor;
    ctx.fillRect(0, 0, width, height);
    // image:
    var totalHeight, barTop;
    if (image != null) {
        var rect = getw("image_rect");
        if (!rect) rect = [0, 0, image.width, image.height];
        totalHeight = rect[3] + barOffset + barHeight;
        var image_y = (height - totalHeight) >> 1;
        ctx.drawImage(image, rect[0], rect[1], rect[2], rect[3],
            (width - rect[2]) >> 1, image_y, rect[2], rect[3]);
        barTop = image_y + rect[3] + barOffset;
    } else barTop = (height - barHeight) >> 1;
    // bar border:
    var barLeft = (width - barWidth) >> 1;
    ctx.fillStyle = barBorderColor;
    ctx.fillRect(barLeft, barTop, barWidth, barHeight);
    //
    var barInnerLeft = barLeft + barBorderWidth;
    var barInnerTop = barTop + barBorderWidth;
    var barInnerWidth = barWidth - barBorderWidth * 2;
    var barInnerHeight = barHeight - barBorderWidth * 2;
    // bar background:
    ctx.fillStyle = barBackgroundColor;
    ctx.fillRect(barInnerLeft, barInnerTop, barInnerWidth, barInnerHeight);
    // bar foreground:
    var barLoadedWidth = Math.round(barInnerWidth * current / total);
    ctx.fillStyle = barForegroundColor;
    ctx.fillRect(barInnerLeft, barInnerTop, barLoadedWidth, barInnerHeight);
}

The interesting part here is window.gml_Script_gmcallback_imgloadbar,

Scripts prefixed with gmcallback_ are not obfuscated when compiling to HTML5.
This is originally intended for use with clickable_add function, but is also handy for any other situations where you want a particular script exposed, including this one.

And, since the game' code is fully loaded before the loading bar extension fires, this allows to execute non-game-specific GML code before the game even starts.

So, the next step would be to define a gmcallback_imgloadbar script that would contain something like the following:

/// gmcallback_imgloadbar(context, current, total, width, height, img_width, img_height)
var r;
var pc = argument1; // progress current
var pt = argument2; // progress total
var cw = argument3; // canvas width
var ch = argument4; // canvas height
var iw = argument5; // image width
var ih = argument6; // image height
switch (argument0) {
    case "image_rect": // ([left, top, width, height] in pixels)
        r[0] = (current_time div 500) mod 4 * (iw div 4);
        r[1] = 0;
        r[2] = iw div 4;
        r[3] = ih;
        return r;
    case "background_color": return c_white;
    case "bar_background_color": return c_white;
    case "bar_foreground_color": return $242238;
    case "bar_border_color": return $242238;
    case "bar_width": return round(cw * 0.6); // (px)
    case "bar_height": return 20; // (px)
    case "bar_border_width": return 2; // (px)
    case "bar_offset": return 10; // (px from image)
}
return undefined;

The script receives the name of requested parameter as the first argument, and returns the value for it. This allows to both configure parameters without editing the extension, and to have them change dynamically.

Here, as an example, the image' sub-coordinates are adjusted based on current_time, as result animating the loading screen' logo at rate of 2 frames/second if you've imported a 4-frame strip for it.

Pre-assembled example and extension are available for download via itch.io:

Download

Alternatively, mirrors for both the extension and the example are hosted on this site too.

Have fun!

Related posts:

16 thoughts on “GameMaker: Loading bar + animated image in HTML5

    • For scrolling, you’ll want to give your body element an “overflow: hidden” style or keep the game at least one pixel smaller than width/height.

      Supposedly the best way to have a full-screen loading bar at the moment is to change size+position of the game canvas, you can get a reference to it using “ctx.canvas”

  1. I realize this post is nearly 7 years old, but this extension is still extremely useful in 2024.

    However, there’s one thing about it that needs attention. When loading HTML5 in a container that’s less than 100 percent of the full size, the loading bar and graphic logo appear off-center. This is because the initial size is the canvas size. But depending on the room size, this can create a very jarring display.

    The solution is to modify the width and height values passed to ImageLoadBar_hook() with the dimensions of the wrapper div by inserting the following snippet at line 28:

    if (parent.document.getElementById(‘html5’) != null) {
    // container width/height:
    width = parent.document.getElementById(“html5”).offsetWidth;
    height = parent.document.getElementById(“html5”).offsetHeight;
    }

    In my case, the wrapper div has an ID of “html5”. With this snippet in place, the logo graphic and loading bar are always centered within the given window.

    Here’s a live example:

    https://treeguardians.net/looking-ahead/lesson-5/woodlands-food-web

    In any event, I want to thank YellowAfterLife for his tremendous contributions to the GameMaker community. :-)

  2. Hello, it is a good extension, but is it possible to center the loading image? What I am trying to do is to put only an animated gif ( without a bar), but there is a problem. I use your other extension – HDPI support (btw. it’s great and you should be proud of it) and unfortunately, those two extensions aren’t compatible. The loading bar or image is not centered on the screen :( Any idea how to fix this bug?

  3. Hi,

    What does line 4 do exactly? What is it checking for? Is it only checking if the given function exists, and that’s it?

    Thanks

    • Indeed – it verifies that your project contains a “gmcallback_imgloadbar” script before calling it.

  4. “Scripts prefixed with gmcallback_ are not obfuscated when compiling to HTML5.” Wow. That’s a feature they could have done with documenting better, on the page about writing javascript extensions for example! I never thought I had any reason to look at ‘clickable_add’ – who would have thought that was the only page of the manual to document a vital GameMaker feature that isn’t exclusively relevant to that function?

  5. Pingback: A summary of my GameMaker assets

  6. Awesome extension, man, thank you very much for the time you spent working on it and for share it for free. You should publish it into YoYo’s marketplace.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.