KLAC logo KLAC Word puzzle Available for

Cocos-Js: Windows Phone script not loading issue

Our game Aedo Episodes is built upon Cocos2D-Js, a cross-platform game engine written in javascript. When deployed as HTML app, the game runs fine on all major mobile OSes but on Windows Phone.

On the Windows Phone the game doesn’t even start. After some challenging debug sessions, the Cocos2D-JS source code finally revealed the issue. A Cocos2D-JS game is composed by several javascript sources you include in the project.json file. The WebHost on the Windows Phone simply doesn’t load those scripts, thus the game doesn’t boot.

The issue is due to the loader, in the code below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
cc.loader = {
/* omitted code */

_createScript: function (jsPath, isAsync, cb) {
var d = document, self = this, s = cc.newElement('script');
s.async = isAsync;

/* omitted code */

cc._addEventListener(s, 'load', function () {
s.parentNode.removeChild(s);
this.removeEventListener('load', arguments.callee, false);
cb();
}, false);
}

The loader calls _creteScript for each source file. The function creates a script tag to load the js file, and when the loading ends, it completes by calling the callback cb. But on Windows Phone the script load event never fires, because WP doesn’t like when the async property of the script is false, that is when the script is loaded synchronously. And, the cocos loader uses false.

If we change the function to use true, scripts will be loaded fine and the game starts. Now, you shouldn’t welcome a change to an external library. In this way, you create your own version of the library and, hence, you have to track changes and propagate them to every update of the library. And the javascript world is fast, bugfixing is constantly done, changes happen all the time. Do this only as a last resort.

But luckily, in this case, there is a better solution. Because of the nature of javascript, we can overwrite functions, and so we can change the default logic of the _createScript function by forcing the async behavior.

1
2
3
4
5
6
//store the framework function
var loader_createScript = cc.loader._createScript;
//overwrite forcing isAsync
cc.loader._createScript = function (jsPath, isAsync, cb) {
loader_createScript.call(cc.loader, jsPath, true, cb);
};

The code stores the default function, overwrites it with a new function that calls it with isAsync always equals to true. This fixes the blocking issue of the Windows Phone.

Include the snippet above just after the cocos-js import.

The fix cames with a price, a slightly collateral effect. If your game includes 2+ javascript files (the common scenario), they are loaded in random order. That is, with the async behavior you lose the feature to load the sources in sequence, the standard behaviour of cocos-js. Therefore, if your code uses variables from the global scope defined in other source files, that can be a problem. With async loading you have no confidence to use variables defined elsewhere because it happens the source isn’t loaded yet.

To overcome this issue, bundling is your friend. Just bundle all your sources in a single javascript file. Be sure to preserve the include order as defined in the project.json.

Should we do it manually? Nah…automation first. Here is a gulp task, for you.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var fs = require('fs'),
gulp = require('gulp'),
concat = require('gulp-concat');

gulp.task('build:src', function () {
var project = require('./project.json');
var files = project.jsList;
return gulp.src(files)
.pipe(concat('game.js'))
.pipe(gulp.dest('dist'));
});

gulp.task('build', ['build:src'], function () {
var project = require('./project.json');
project.jsList = ['game.js'];
fs.writeFileSync('dist/project.json', JSON.stringify(project), 'utf-8');
});

Use gulp build from the prompt. Processed files will be into dist folder.

This website uses cookies to improve your experience