Starting/restarting an ImpactJS game without reloading the page

For the JIPPI game, we had to modify ImpactJS so that we could restart the game without reloading the page.

Create()

Most of the create function has been explained in the blog post Dynamic loading of assets, entities and levels in ImpactJS. Aside from what's explained in that article, you could do other kinds of pre-initialization stuff here. Just make sure you end your create function with ig.main();

Destroy()

Input handlers

We made changes to impact/input.js so that the event handlers for input were not defined by anonymous functions. So instead of doing this:

ig.system.canvas.addEventListener('mousedown', this.keydown.bind(this), false );

We did this:

ig.keydownBound = this.keydown.bind(this);
ig.system.canvas.addEventListener('mousedown', ig.keydownBound, false );

That way we could later remove the event handlers by doing:

ig.system.canvas.removeEventListener('mousedown', ig.keydownBound, false );
window.removeEventListener('keydown', ig.keydownBound, false );

Game loop and other event handlers

The main game loop can be stopped by calling:

ig.system.stopRunLoop();

There are also two event handlers added for module loading that needs to be removed:

document.removeEventListener( 'DOMContentLoaded', ig._DOMReady, false );
window.removeEventListener( 'load', ig._DOMReady, false );

In order to make the next create() execute as expected, reset ig._current by doing:

ig._current = null;

Removing modules and levels

Next up, we removed all modules and levels that might be loaded the next time we run create(). When you try to create a module that already exists, ImpactJS will trigger a "Module has already been defined" error message. We do this by traversing ig.modules, like this:

var deleteables = [];

for (var m in ig.modules) {
    var d = false;
    if (m.substr(0,11) === 'game.levels') {
        d = true;
    }
    if (m.substr(0,13) === 'game.entities') {
        var mod = m.split('.');
        if (mod.length >= 4) d = true;
    }
    if (d) deleteables.push(m);
}

for (var i=0, l=deleteables.length; i < l; i++) {
    delete ig.modules[deleteables[i]];
}

As you can see here, I delete modules that has a name which begins with game.levels. But for entities, I make sure to only remove those who belong to a subfolder since I don't want to remove the shared entities that are used in all levels, and resides in the root entities folder.

Next step is to clean up the ig.resources array.

deleteables = [];

if (ig.resources.length > 0) {
    for (var res in ig.resources) {

        var r = ig.resources[res].path,
        d = false;

        if (typeof r !== 'undefined') {
            if (r.substr(0,13) === 'media/images/') {
                r = r.substr(13);
            }
            if (r.substr(0,6) === 'char1_' ||
                r.substr(0,6) === 'char2_' || ... ) {
                d = true;
            }
            if (d) {
                deleteables.push(res);
            }
        }
    }
}

for (var i=0, l=deleteables.length; i < l; i++) {
    var key = deleteables[i],
    r = ig.resources[key];
    if (!r.hasOwnProperty(key)) {
        return;
    }
    if (isNaN(parseInt(key)) || !(r instanceof Array)) {
        delete r[key];
    } else {
        r.splice(key, 1);
    }
}

This one is a bit more elaborate. I make sure to remove resources that only belongs to the game's characters. That way I don't delete the shared assets.

Finally I delete the levels by doing this for each level that might be loaded with the next create():

delete ig.LevelA;
ig.LevelA = null;

It took me quite a few hours to figure out this one, but it turned out to be quite simple. I just had to read and understand almost every line of code in the Impact engine to get this working. I hope this will be useful for you. Feel free to ask if anything is unclear.

Latest articles