Thursday, February 28, 2013

Collection Plugins in Three.js

‹prev | My Chain | next›

I was pleased to discover the power of Three.js plugins for myself last night. I do not think that I have them quite right. Or more precisely, I hope that I can improve upon the way in which I am using them.

The particular problem that I am trying to solve is a way to label three dimensional Three.js objects using two dimensional DOM elements. I have the plugin approach working to the point where I have to add each object/label pair to the renderer:
  renderer.addPostPlugin(new Label(earth, "Earth"));
  renderer.addPostPlugin(new Label(mars, "Mars"));
This gives me nice labels as the planets make their way around in their orbits:



There are two related problems with this approach. First, adding a new label is a two step process: the programmer has to construct a label object and add with the addPostPlugin() method. Second, there is no way to remove a plugin—at least there is no easy way to do so.

It occurs to me that Three.js plugins need simply respond to init() and render(). In other words, I can create a global object to hold all labels:
  var LabelPlugin = {
    labels: [],
    add: function(l) {this.labels.push(l);},
    init: function() {},
    render: function() {
      for (var i=0; i<this.labels.length; i++) {
        var args = Array.prototype.slice.call(arguments);
        this.labels[i].render.apply(this.labels[i], args);
      } 
    }
  };
The benefit of this approach is that I can add the LabelPlugin as a Three.js plugin immediately after constructing the renderer and then forget about it:
  // This will draw what the camera sees onto the screen:
  var renderer = new THREE.WebGLRenderer({antialias: true});
  renderer.addPostPlugin(LabelPlugin);
It is still a bit of a bummer that there is no way to auto-register the plugin. Even after I extract this out into a separate library, users of this library will still have to manually renderer.addPostPlugin(LabelPlugin).

Anyhow, none of this is of any use unless I add some labels to the LabelPlugin.labels property. I can update yesterday's Label constructor to do just that:
  function Label(object, content) {
    this.object = object;
    this.content = content;

    this.el = this.buildElement();
    LabelPlugin.add(this);
  }
Each Label already has a render() method. So, when these are added to LabelPlugin.labels, LabelPlugin.render() will in turn call each Label's render() method. In other words, to add a label to an element, all that I need do is construct a Label object:
  new Label(earth, "Earth");
  new Label(mars, "Mars");
The main benefit of this approach, aside from being a little cleaner, is that I can now remove labels. I add a remove() method to the LabelPlugin object to prevent the plugin from attempting to render removed labels:
  var LabelPlugin = {
    labels: [],
    init: function() {},
    add: function(l) {this.labels.push(l);},
    remove: function(l) {
     this.labels = this.labels.filter(function (label) {
       return label != l;
     });
    },
    render: function() { /* ... */ }
  };
Similarly, I define a remove() method on the Label class so that the DOM label is removed as well:
  Label.prototype.remove = function() {
    this.el.style.display = 'none';
    LabelPlugin.remove(this);
  };
So, when my space simulation first starts, I can have labels on planets. I can then remove them 5 seconds later:
  new Label(earth, "Earth");
  var m_label = new Label(mars, "Mars");
  setTimeout(function() {m_label.remove();}, 5000);
That is of limited use with this particular simulation. I suppose that I could add a keyboard listener to toggle the display of labels—that might be of some benefit. But if these labels were used as speech bubbles for game characters, then the ability to remove them would be quite handy.

(live code demo)


Day #676

Wednesday, February 27, 2013

Getting Started with Three.js Plugins for Labels

‹prev | My Chain | next›

I am not one to get all crazy when methods take more than a single argument. Except that I kind of am.

The Label class that I am trying to build for Three.js objects currently requires three arguments:
  function Label(object, camera, content) {
    this.object = object;
    this.camera = camera;
    this.content = content;

    this.el = this.buildElement();
    this.track();
  }
I can live with the object and the content parameters. After all, the entire point of the Label class is to create a relationship between a Three.js object (the object) and a text label (the content). But that camera argument really bugs me.

There is no way to avoid the necessity of the camera. Without it there is no way to project the 3D coordinates of the Three.js object into 2D screen space. And, without that projection, there is no way to accurately place the label. Still, it seems like an awkward thing to require the programmer to supply.

My first inclination is to reach under the renderer covers to redefine the render() method on the THREE.WebGLRender.prototype. Unfortunately, there is no THREE.WebGLRenderer.prototype. Well, there is a prototype—this is, after all JavaScript. But the prototype does not contain any of the methods. All of the THREE.WebGLRenderer methods are defined inside the constructor. In other words, I am out of luck if I want to redefine render() for all instances of WebGLRenderer (not to mention CanvasRenderer).

But I am not entirely out of luck. Three.js does this for a reason. In this case, the reason is that Three.js supports rendering plugins for doing this kind of thing. To convert my Label class into a plugin, I need an init() method, which I make empty. I also need a render() method, which I assign to yesterday's track():
  Label.prototype.track = function() {
    // ...
  };
  Label.prototype.render = Label.prototype.track;
One of the arguments that is supplied to the plugin's render() method just so happens to be the camera, which allows me to ditch the instance variable camera and use this instead:
  Label.prototype.track = function(scene, cam) {
    if (!cam) return;
    // ...
    var projector = new THREE.Projector(),
        pos = projector.projectVector(p3d, cam),
    // ...
  };
  };
  Label.prototype.render = Label.prototype.track;
That is a promising start. I am not sold on requiring the programmer to add each label to the render as a plugin:
  scene.add(mars);
  var mars_label = new Label(mars, camera, "Mars");
  renderer.addPostPlugin(mars_label);
Perhaps this is something that I can improve upon tomorrow. If nothing else, I am glad that Three.js prevented me from trying to reach under the render() covers. I would have been sure to cause problems doing that. Of course, it is even nicer that Three.js supports plugins for this express purpose.

(live code of the plugin)

Day #675

Tuesday, February 26, 2013

Movable, Centered Labels for Three.js Objects

‹prev | My Chain | next›

Up today, I hope to build on the Three.js label maker library from yesterday. In other words, I hope to make it into a library.

So far, I can label a single object on the scene:



I need to be able to easily label multiple objects and the location of the label should be above the object, not to the side.

Multiple object labels means that I need to take yesterday's procedural code and turn it into a class. I start with a constructor that requires the object being labeled, the camera, and the text content:
  function Label(object, camera, content) {
    this.object = object;
    this.camera = camera;
    this.content = content;

    this.el = this.buildElement();
    this.track();
  }
The buildElement() method does the usual DOM building:
  Label.prototype.buildElement = function() {
    var el = document.createElement('div');
    el.textContent = this.content;
    el.style.backgroundColor = 'white';
    el.style.position = 'absolute';
    el.style.padding = '1px 4px';
    el.style.borderRadius = '2px';
    el.style.maxWidth = (window.innerWidth * 0.25) + 'px';
    el.style.maxHeight = (window.innerHeight * 0.25) + 'px';
    el.style.overflowY = 'auto';
    document.body.appendChild(el);
    return el;
  };
The track() method is where the projection from 3D space into web page 2D takes place:
  Label.prototype.track = function() {
    var p3d = this.object.position.clone();

    var projector = new THREE.Projector(),
        pos = projector.projectVector(p3d, camera),
        width = window.innerWidth,
        height = window.innerHeight,
    this.el.style.top = '' + (height/2 - height/2 * pos.y) + 'px';
    this.el.style.left = '' + (width/2 * pos.x + width/2) + 'px';
    var that = this;
    setTimeout(function(){that.track();}, 1000/60);
  };
All of that is from yesterday, with the exception of that last two lines. I update the label tracking 60 times a second. I can likely get away with a slower update rate, but that does not tax the CPU and makes for a smooth animation around the screen.

It is in this track() method that I can improve on the positioning of the label. Firstly, I can move the 3D point up a bit. Since I want the label above the object, I increase the 3D position by the Three.js boundRadius. While I am at it, I offset the web page positioning by the DOM element's width and height:
  Label.prototype.track = function() {
    var p3d = this.object.position.clone();
    p3d.y = p3d.y + this.object.boundRadius;    

    var projector = new THREE.Projector(),
        pos = projector.projectVector(p3d, camera),
        width = window.innerWidth,
        height = window.innerHeight,
        w = this.el.offsetWidth,
        h = this.el.offsetHeight;
    this.el.style.top = '' + (height/2 - height/2 * pos.y - 1.5*h) + 'px';
    this.el.style.left = '' + (width/2 * pos.x + width/2 - w/2) + 'px';

    var that = this;
    setTimeout(function(){that.track();}, 1000/60);
  };
That should do it. I can then create multiple labels:
  new Label(earth, camera, "Earth");
  new Label(mars, camera, "Mars");
With that, I have multiple labels that are centered above the corresponding Three.js object:



The labels move with the planets as they orbit the Sun. I am not quite sure that this always works with all camera orientations. The API is a bit unwieldy as well as it requires the camera as the third wheel constructor argument. There is no easy way to avoid it, however, since the camera is necessary in order to make the projection in to 2D space.

I will sleep on the current approach. Perhaps tomorrow I will pick back up by applying this library as speech bubbles in the game player simulation.


Day #674

Monday, February 25, 2013

Labeling Three.js Elements with DOM Elements

‹prev | My Chain | next›

My son continues to be obsessed with the idea of chatting within the games that we write in 3D Game Programming for Kids. I have yet to figure out a means to make a faye service publicly available without incurring problems of a publicly available faye service. Quite aside from that, I realize that I do not know how to draw speech bubbles in Three.js 3D. So, I set aside my concerns over the larger problem for a day to see if I can solve this other problem.

My understanding of text in Three.js is that it is possible, but complex. In my experience, DOM overlays have been much easier. It then becomes an exercise of positioning the two dimensional DOM elements at the proper location for the 3D elements.

To test this out, I switch back to my old Earth-Mars orbit simulator:



I should probably make that an orthographic projection, but I am, in fact, using a perspective camera in there, so this ought to be a realistic experiment.

I add a DOM label for the Earth:
  var label = document.createElement('div');
  label.textContent = 'Earth';
  label.style.backgroundColor = 'white';
  label.style.position = 'absolute';
  label.style.padding = '1px 4px';
  label.style.borderRadius = '2px';
  
  document.body.appendChild(label);
Now, in my animate() function, I add a call to labelPlanets() which will be responsible for updating the label's screen position to mirror the location on the screen:
  function animate() {
    requestAnimationFrame(animate);
    //  update the planets' positions...    

    labelPlanets();
        
    // Now, show what the camera sees on the screen:
    renderer.render(scene, camera);
  }
Thankfully, Three.js includes the Projector for converting between projected and non-projected coordinate systems. I still need to translate between XY with with origin at the center to the DOM's origin at the top left with Y increasing down the screen. But I know how to do that:
  function labelPlanets() {
    var projector = new THREE.Projector();
    var pos = projector.projectVector(earth.position.clone(), camera);
    label.style.top = '' + (heightHalf - heightHalf * pos.y ) + 'px';
    label.style.left = '' + (widthHalf * pos.x + widthHalf) + 'px';    
  }
I find that, if I do not clone() the Earth's position, then the Earth disappears for some reason. That strangeness aside, I have my label:



That works surprisingly well. I still need to ruminate over this Faye question so I may take a day or two to build this out into a speech balloon / label library for the book. Unless inspiration strikes.


Day #673

Sunday, February 24, 2013

Playing, Pausing and Restarting Sounds in Three.js

‹prev | My Chain | next›

I had a good time with HTML5 sounds last night. I really had no idea that it was so easy. I am still unsure if I will end up including sound in 3D Game Programming for Kids, but it seems worth building up a simple Sounds.js library.

I spend a good amount of time trolling through the freesound.org for a selection of sounds. I end up with the following list (sound name, attribution, freesound filename):
bubble.mp3: CGEffex // 89534__cgeffex__very-fast-bubble-pop1.mp3
buzz.mp3: Sikoses // 82756__sikoses__stm1-upbass-5.mp3
click.mp3: orginaljun.deviantart.com // 157871__orginaljun__arcade-button-1-click-sound.mp3
donk.mp3: orginaljun.deviantart.com // 153063__orginaljun__1-bell-sound.mp3
drip.mp3: orginaljun.deviantart.com // 151840__orginaljun__pie-poi-water-dripping-sound.mp3
guitar.mp3: johnnypanic // 158650__johnnypanic__quickpowerchord2.mp3
knock.mp3: blimp66 // 88658__blimp66__knock.mp3
scratch.mp3: orginaljun.deviantart.com // 152844__orginaljun__1-rub-scratch-sound.mp3
snick.mp3: orginaljun.deviantart.com // 151482__orginaljun__weechuuu-high-pitch-sound-version-02.mp3
spring.mp3: orginaljun.deviantart.com // 153490__orginaljun__rusty-spring.mp3
swish.mp3: orginaljun.deviantart.com // 153489__orginaljun__friction-moving-object.mp3
It is not an exhaustive list, but hopefully a good start for kids / beginners building their first games.

I push each of those sounds under the http://gamingjs.com/sounds urlspace. With that, I need a Sound class that can play any of them:
  function Sound(name) {
    this.name = name;
    this.audio = document.createElement('audio');
    var source = document.createElement('source');
    source.src = '/sounds/' + name + '.mp3';
    this.audio.appendChild(source);
  }
  
  Sound.prototype.play = function() {
    this.audio.play();
  };
This is just an object representation of last night's work. Also in the Sound prototype, I add the ability to repeat the sound, as well as stopping it:
  Sound.prototype.repeat = function() {
    this.audio.loop = true;
    this.audio.play();
  };

  Sound.prototype.stop = function() {
    this.audio.repeat = false;
    this.audio.pause();
  };
Since the constructor takes the sound name, which is cleverly the basename of the MP3 file containing the sound, I can define the list of all sounds as:
  var Sounds = {
    all: [
      'bubble','buzz','click','donk',
      'drip','guitar','knock','scratch',
      'snick','spring','swish'
    ]
  };
And then add each of these sounds to the Sounds list:
  Sounds.all.forEach(function(sound) {
    Sounds[sound] = new Sound(sound);
  });
With that, I can redefine my Three.js / Physijs collision event handler to play a spring noise as:
  player.addEventListener('collision', function(object) {
    Sounds.spring.play();
  });
I rather like that. Except that it does not always play on collision. In particular, when a collision occurs immediately after another collision, the previous sound is still playing, blocking the new collision sound. To get around this, I stop the sound whenever I start it:
  Sound.prototype.play = function() {
    this.stop();
    this.audio.play();
  };
Furtermore, in the stop() method, I need to reset the current playback time to zero:
  Sound.prototype.stop = function() {
    this.audio.repeat = false;
    this.audio.currentTime = 0;
    this.audio.pause();
  };
And that seems to do the trick.

I close by verifying that repeat mode works:
  Sounds.swish.repeat();
  setTimeout(function(){Sounds.swish.stop();}, 8000);
And work it does.

If nothing else, this seems worth including in an appendix. I have to think that kids are going to want to include sounds in their games. I must say that I really am surprised at how easy this sound stuff is—I had avoided it so far assuming that it would be much tougher.

(a simple live code sound demo)

Day #672

Saturday, February 23, 2013

Adding Sound to Three.js Games

‹prev | My Chain | next›

One thing that I do not cover in 3D Game Programming for Kids is sound. I am concentrating more on the 3D gaming aspects than overall multimedia experience. Also, I am really trying to teach JavaScript, but don't tell anyone—I'd rather people think game programming is my main goal (better marketing). Anyhow, I would be remiss if I did not at least explore game sound.

The book has the distinct advantage of requiring Chrome. I may try to add Firefox as well, but the bottom line is that I do not have to worry about certain backwater browsers due to the need for WebGL. This means that I do not have to worry my pretty little head over backward compatibility in sound—HTML5 will work just fine.

Of course, I have never played with HTML5 sound before...

I grab a single sound from Freesound. I am not looking for game soundtracks, just ~1 second collision sounds. In case I actually do end up using these, I opt for an attribution-only (all Freedsound files are creative commons) sound from "orginaljun.deviantart.com". I upload it to gamingjs.com as "sounds/donk.mp3".

I will add this sound to the stripped-down version of a recent Three.js game in which arrow controls move a little ball around the screen:



I add the "donk" sound as:
  var audio = document.createElement('audio');
  var source = document.createElement('source');
  source.src = '/sounds/donk.mp3';
  audio.appendChild(source);
  audio.play();
And that does the trick. When the game loads, I get hear the desired "donk".

To hear it when the player runs into the wall, I move the audio.play() call into the collision event (from Physijs) of the player:
  player.addEventListener('collision', function(object) {
    audio.play();
  });
That is all there is to it. Now when the player runs into one of the walls, I hear a surprisingly reassuring "donk" noise.

I add a timeout before adding the sound:
  setTimeout(function() {
    player.addEventListener('collision', function(object) {
      audio.play();
    });
  }, 500);
This prevents a "donk" when the player is first added to the screen.

I must admit that even a simple sound like "donk" does add something to the experience. It might be worthwhile creating a simple library with a few sounds that can be played like: Sounds.donk.play(). I will have to think about that as the book is already getting nearing capacity for concepts. Simple sounds like this seem simple enough, but I need to figure out how or if I would like to support continuous sounds (loop=true) like those that a player might make while walking. That is something for another day.

The sound-enabled simulation.

Day #671

Friday, February 22, 2013

Git Submodules: ICE ICE Beta

‹prev | My Chain | next›

I think that my recent changes in my fork of Mr Doob's code editor are ready for 3D Game Programming for Kids. Before I push these changes out there, I would like to make it available in a beta location as a trial.

The "ICE Code Editor" that is used throughout the book is actually just a GitHub pages site that resides at http://gamingjs.com (currently the index page redirects to the Pragmatic landing page). Inside this gamingjs repository is a submodule that points to my fork of the code-editor:



I keep all of the ICE Code Editor changes that are specific to the book and http://gamingjs.com in a branch, aptly named gamingjs. That is, I have a gamingjs GitHub pages repository and a matching gamingjs branch in the ICE Code Editor repository. All of which gives me a nice URL: http://gamingjs.com/ice.

I believe that I can create a new submodule at /ice-beta, pointing to the latest commit in my code-editor. Instead of jumping right to the latest and greatest commit, however, I will temporarily point the /ice-beta submodule to the same commit currently used by /ice. This will give me a chance to verify the upgrade process.

I start by adding the submodule:
➜  gamingjs git:(gh-pages) ✗ git submodule add -b gamingjs https://github.com/eee-c/code-editor.git ice-beta
Cloning into 'ice-beta'...
remote: Counting objects: 327, done.
remote: Compressing objects: 100% (139/139), done.
remote: Total 327 (delta 211), reused 297 (delta 185)
Receiving objects: 100% (327/327), 379.79 KiB | 157 KiB/s, done.
Resolving deltas: 100% (211/211), done.
Branch gamingjs set up to track remote branch gamingjs from origin.
That seems promising. By virtue of the -b option, this points to the gamingjs branch of code-editor (which is where I keep my changes). But it currently points to the latest commit in that branch:
➜  gamingjs git:(gh-pages) ✗ git submodule status ice
 bf110d41a15d25c6ce07defc775b5db439be12c6 ice (heads/gamingjs)
➜  gamingjs git:(gh-pages) ✗ git submodule status ice-beta
 4ef9699d7566a842b7fd677a16cc0ca7d88da057 ice-beta (heads/gamingjs)
That is ultimately where I want to go, but I switch back to the same commit used by /ice:
gamingjs git:(gh-pages) ✗ cd ice-beta
➜  ice-beta git:(gamingjs) git co bf110d41a15d25c6ce07defc775b5db439be12c6
Note: checking out 'bf110d41a15d25c6ce07defc775b5db439be12c6'.

# The usual detached HEAD note...

HEAD is now at bf110d4... Revert "Try to cache /ice (in addition to /ice/)"
I then change directory back into the gamingjs repository and commit the new submodule:
➜  ice-beta git:(bf110d4) cd ..
➜  gamingjs git:(gh-pages) ✗ git ci -m "Add /ice-beta path
dquote>
dquote> Still pointing to the same SHA1 at /ice" -a

[gh-pages a3631f6] Add /ice-beta path
 2 files changed, 4 insertions(+)
 create mode 160000 ice-beta 
After pushing it to the GitHubs, I give the beta URL, http://gamingjs.com/ice-beta/, a try:



Perfect! It loads the code data from localStorage designated to the http://gamingjs.com domain, which was all created by the original ICE Code Editor. But now I can try it out with the beta version of the code editor. Well, at least I will be able to once I update my submodule to point to the last commit instead of this old one.

So, back in my local git repository, I checkout the last commit on the gamingjs branch for the /ice-beta submodule:
➜  ice-beta git:(master) git co gamingjs 
Switched to branch 'gamingjs'
After checking the submodule's new SHA1 into the gamingjs repository and pushing the change, I load the /ice-beta URL one more time:



Yay! That is the application cache code borrowed from html5rocks. It does just what the confirmation dialog says: manages the new version process. I click OK and see all of the recent changes. Up tomorrow, I need to start a new project so that I can put the beta version through its paces.



Day #670

Thursday, February 21, 2013

Game Sharing

‹prev | My Chain | next›

Last night I improved the code sharing ability in my fork of the venerable Mr.doob's code editor. As with everything else that I seem to do, I borrowed heavily from Mr.doob's implementation, tweaking it only for more suitability for kids. Tonight I would like to tweak a bit more to see if I can't come up with a decent game sharing experience.

Currently, shared code opens in a live code editor:



It would be pretty cool if kids could share the results of their work (especially if they use the Scoreboard to sign their work), with the code hidden initially:



I already have an edit-only mode (for when things go very, very wrong) that is specified with a ?e query string parameter. Perhaps a ?g for game mode?
var EDIT_ONLY = window.location.search.indexOf('?e') > -1;
var GAME_MODE = window.location.search.indexOf('?g') > -1;
After a bit of code rearranging, I think I need only hide the code if in GAME_MODE:
codeToolbar();
update();
if (GAME_MODE) hideCode();
That does not quite work, however. When I first load up the page, I am in "game mode", but when I click the "Show Code" button, there does not seem to be any code:



That is really weird. There is only a single line in the ACE code editor and it is empty. Eventually, I find that I need to ensure that ACE has resized itself at least once:
var showCode = function() {
  codeToolbar();
  editor.style.display = '';
  ace.renderer.onResize();
  ace.focus();
};
Somehow hiding it immediately after setting the initial value confused ACE to no end. Invoking the method that is supposed to be called when a resize event occurs seems to resolve the situation.

To enable this in the sharing popup, I do a little hand DOM coding:
    // ...
    var toggle_game_mode = document.createElement('input');
    toggle_game_mode.type = 'checkbox';
    var toggle_label = document.createElement('label');
    toggle_label.appendChild(toggle_game_mode);
    toggle_label.appendChild(document.createTextNode("start in game mode"));

    var game_mode = document.createElement('div');
    game_mode.appendChild(toggle_label);
    // ...

    var share = document.createElement( 'div' );
    share.appendChild(title);
    share.appendChild(input);
    share.appendChild(game_mode);
    share.appendChild(shortener);
    // ...
There is nothing quite like pure DOM coding in JavaScript to make one appreciate jQuery. Or Dart.

Thankfully, since this is just for Chrome (and maybe Firefox eventually), adding the checkbox event listener is a little nicer:
    toggle_game_mode.addEventListener('change', function() {
      if (this.checked) {
        input.value = 'http://gamingjs.com/ice/?g#B/' + encode( ace.getValue() );
      }
      else {
        input.value = 'http://gamingjs.com/ice/#B/' + encode( ace.getValue() );
      }
      input.focus();
      input.select();
    });
With that, I believe that I have the share-in-game feature working:



That seems a fine stopping point tonight. I think that I will pick up tomorrow with pushing this to the editor website, possibly in a beta URL location so that I don't affect any existing readers just yet.


Day #669

Wednesday, February 20, 2013

Is Good Simple URL Shortening

‹prev | My Chain | next›

I copied the nifty code sharing feature in Mr.doob's code editor into the fork that I am using for 3D Game Programming for Kids. It is nice and slick looking:



But I am not sure that it enough for kids and beginners. For one, it would benefit a bit from instructional text. Second, it would be nice if there were an easy way to submit the very long URLs to a URL shortener. The latter is the more difficult problem so I will start with that first.

To date, the in-browser code-editor that I have kids/beginners use in the book is entirely self-contained inside an application cache. I could see a need for a significant backend if I were trying to build up a community around this. And a community might be nice—a bunch of beginners showing off new projects and encouraging each other. But community with kids is a tricky thing for numerous reasons. So if I can avoid backend infrastructure, then I will do so.

Unfortunately, most URL shortening services seem to require an API key which they use to help throttle usage from particular applications. That is not exactly conducive to an open source editor that anyone can use. I could run a simple node.js proxy service (or even roll my own URL shortener), but I hope to avoid that.

And the is.gd URL shortener seems like it will allow me to do just that. The sharing popup already encodes the contents of the editor (I'm using the ACE code editor) in the share-code popup:
var menuShare = function() {
  var el = document.createElement( 'li' );
  el.textContent = 'share';
  el.addEventListener( 'click', function ( event ) {
    var dom = document.createElement( 'input' );
    dom.value = 'http://gamingjs.com/ice/#B/' + encode( ace.getValue() );
    // ...
  });
};
While I am building up the DOM elements for the sharing popup, I can also add a link to is.gd:
    // ...
    var link = document.createElement( 'a' );
    link.href = 'http://is.gd/create.php?url=' + encodeURIComponent(dom.value);
    link.textContent = 'shorten';
    var shortener = document.createElement( 'div' );
    shortener.appendChild(link);
    // ...
Happily, that just works:



I am slowly internalizing the lesson from last night that the best code is that code never written. So, unless a significant reason comes along that might require that I have my own URL shortening service, I will be content to live with this solution.

I take a bit more time to make the popup as pretty and as informative as possible:



And then call it a night. Up tomorrow I think that I will explore sharing links with the code hidden by default or with a parameter. Hopefully most of my readers will be eager to show off their work without all that pesky code getting in the way.


Day #668

Tuesday, February 19, 2013

ICE Code Editor on Resize

‹prev | My Chain | next›

I had a look through some of the changes Mr Doob's code editor (which is now called htmleditor). Naturally, they're much nicer than mine. I hate that guy. But not really. He's awesome.

Anyhow, many of the changes involved the CodeMirror editor and the esprima JavaScript parser. I like both of those, but I think it best to stick with ACE code editor for 3D Game Programming for Kids—the error feedback (at least the default) is slightly more helpful for beginner coders.

One of the changes in which I am keenly interested is the refresh on resize approach. Mr.doob no longer updates the visualization layer when resizing. This seemingly obvious change has two advantages: fewer screen updates make for fewer screen artifacts and debug output remains in place with the JavaScript console is shown. I have suffered through both problems sporadically for months now without a good solution. This is why Mr.doob maintains awesome projects and I write books, I suppose.

Anyhow, I delete the resize handler from my fork:
window.addEventListener( 'resize', function ( event ) {
  clearTimeout( interval );
  interval = setTimeout( update, 300 );
} );
And, sure enough, I can open and close the JavaScript console without restarting the visualization:



I cannot believe that did not occur to me. Sigh.

Actual window resizing will not cause a visualization update. That is a relatively rare event and there is that big “Update” button on the screen that programmers can use to force a visualization update if needed.

There is another nifty addition to upstream—the sharing experience is much improved. Instead of putting a sharable URL in the address bar and hoping the user notices, Mr.doob has a popup that displays the URL. In the hopes that I can cherry pick most of this commit, I add the original repository to my local copy as remote “upstream”:
➜  code-editor git:(gamingjs) ✗ git remote add upstream git://github.com/mrdoob/htmleditor.git
➜  code-editor git:(gamingjs) ✗ git fetch upstream
remote: Counting objects: 85, done.
remote: Compressing objects: 100% (34/34), done.
remote: Total 72 (delta 44), reused 65 (delta 38)
Unpacking objects: 100% (72/72), done.
From git://github.com/mrdoob/htmleditor
 * [new branch]      master     -> upstream/master
But after cherry picking the appropriate commit, I find that I am out of luck. I moved all of my JavaScript into a library file whereas the original repository has everything, JavaScript and all, in index.html. Bother.

So I manually copy the code from the commit into my editor.js and eventually get:



That is actually pretty slick. I can Ctrl+C to copy that URL immediately and it works. I will have to add some instructions to make it more beginner friendly, but that is nice. I end tonight by removing some resize handling code in the popup. Ironically, it includes code to reposition the popup on resize. I replace it with code that simply removes the popup on resize. That seems reasonable behavior for something that is unlikely to happen often.

Tomorrow, I will pick back up incorporating this into a URL shortener. No one should have to share a link as long as: http://gamingjs.com/ice/#B/jZKxTsMwEIZ3nuKUpU6HuAWKEE0ZisSAxELzAo5zioMSX2QbWt4e4zRpWlWlWZy7//sy/HGaU/HznPJw3KRWmqp1YI1cRcq59onzUjSVLt82iaSGZ8ogJp828krHXiG9KEMNvlY7tJdUvpFkMCdhihOs5/wbwLcwYAcSVqBxCweVxUsPHYDETwCuatCwuBskfWlX0Faz+Wy/Cjxb9KPCumWTNQLuJNY1ageOAIVUQE6hSSYe5NM/VLQt6uIdrRUlsihD62Ae7b9zLry9FN71YXO0vh+c4kRY+ASm/IzzEMVLCHvOwaIbvDXlURyC46K6Dh5noUBJ2lKNSU0lGzElulA1i/+hMl/4VdAHNqLS/rIEevTPR88v—especially kids.


Day #667

Monday, February 18, 2013

Doing Good on Day 666

‹prev | My Chain | next›

I had threatened to do something truly evil on day 666, but I lack maniacal imagination. Well, that and I think that I have exhausted my evilness quotient for the week with the now complete draft of a chapter on object oriented programming in 3D Game Programming for Kids. Instead, I think it time to get back to some hacking on my fork of Mr Doob's code editor, which I call the ICE Code Editor in the book.

There are a couple of annoyances that have been cropping up as I use it. Those annoyances are only going to compound as more readers get involved. Also, I know that Mr.doob has made improvements on the original so revisiting the code can get me ready for evaluating those changes.

First up, I would like to delay updates to the Three.js visualization after making updates in the code layer. I think that the delay is sub-second currently and it feels sometimes as though I am racing to type continuously or quickly so that nothing breaks.

This turns out to be quite easy. The ACE code editor, which serves as the actual in-browser editor in my code-editor fork, exposes an on-change callback. In there, I simply up the duration between checks to be one and a half seconds instead of 300 milliseconds:
ace.getSession().on( "change", function () {
  save();

  if ( documents[ 0 ].autoupdate === false ) return;

  clearTimeout( interval );
  interval = setTimeout( update, 1.5 * 1000 );
});
Lest I forget, since this is an application cache application (for offline usage), I need to ensure that I remove the manifest attribute from the HTML document:
<!DOCTYPE html>
<html manifest="editor.appcache">
  <head>
    <title>code editor</title>
...
If I leave that in place, then I will not actually see any of the changes that I make—instead my browser will continue to use the old, application cache version. With that, I do indeed see a more leisurely 1.5 second delay in between the last change that I make and the Three.js layer updating itself. I wonder if that should be even higher for kids who may be significantly slower at typing. I will table that until I run this version through another couple of hackathons.

Anyhow, with that change in place, I am ready for another. I would like the various dialogs that I use in the editor to support enter-to-submit. I find it very annoying to have to hit the Save button. Thankfully, this is just DOM event coding:
  var newProjectField = document.createElement( 'input' );
  // ...
  newProjectField.addEventListener('keypress', function(event) {
    if (event.keyCode != 13) return;
    createProject(newProjectField.value, templateField.value);
    closeNewDialog();
  }, false);
After doing the same elsewhere, I am ready to tackle an annoyance with the ACE Code Editor. The default tab size seems to be 4. I do not recall why I left it this way, but I am constantly re-editing text to get back to the normal 2 spaces per tab in the code.

Thanks to the very excellent API documentation for ACE, I find that I simple need the setTabSize() method, which is part of the editor session:
var ace = ace.edit("editor");
ace.setTheme("ace/theme/chrome");
ace.getSession().setMode("ace/mode/javascript");
ace.getSession().setUseWrapMode(true);
ace.getSession().setUseSoftTabs(true);
ace.getSession().setTabSize(2);
// ...
With that, I have cleared three fairly annoying behaviors from my TODO list. I am somewhat hesitant to push the change into production now that I have a bunch of active users. I will probably give it a few days of local use before pushing these changes into general use.


Day #666

Sunday, February 17, 2013

Boundary Conditions for Kids

‹prev | My Chain | next›

In the most recent candidate game for 3D Game Programming for Kids, the player positions ramps strategically so that the in-game player can perform sweet jumps to reach the green goal:



That works works quite well thanks to some simple Three.js and Physijs coding. It worked so well, in fact, that I was inspired to add the very excellent scoreboard, which, as of last night, seems to be in decent shape.

I was further inspired to add multiple levels to the game. If this makes it into the book, it would be a separate chapter because the game has gotten quite large—I would prefer under 150 lines of code for any game and this has now hit 300.

Anyhow, the game and the various levels are working quite well, but resetting the game does not. Thanks also to last night's work, reset works for the first level and after the game is over. But if I reset in between levels, then things break down. Obstacles from the previously active level remain on the screen in addition to the first level objects that should be there.

Since this works on the first level and after the game is over, I must have an error in my logic, but where? More importantly, how do I troubleshoot? Am I going to have to work to get to the second level each time I want to try something new?

Happily, I am able to reproduce my problem by starting the game on level 2 (well 1, but it is zero-indexed):
  var current_level = 1;
  function drawCurrentLevel() {
    scoreboard.resetCountdown(50 - current_level * 10);
    var obstacles = levels[current_level];
    obstacles.forEach(function(obstacle) {
      scene.add(obstacle);
    });      
  }
Immediately after the game loads, I can reset. Now, not only do I have the two platform obstacles from level 1, but I also have the single obstacle from level 0:



Being able to reproduce quickly will certainly help. With that, I start looking closer at my code in the hopes of figuring out where I went wrong. The most likely culprit in gameReset() would seem to be eraseOldLevel() since the old level is not actually being erased:
  function gameReset() {
    pause = false;
    eraseOldLevel();
    current_level = -1;
    scoreboard.resetTimer();
    scoreboard.setMessage('');
    levelUp();
  }
And this does turn out to be the problem area. If, as in this case of manually setting the current level, the game is at level one, then eraseOldLevel() will try to erase the previous level (zero in this case):
  function eraseOldLevel() {
    if (current_level === 0) return;
    var obstacles = levels[current_level-1];
    obstacles.forEach(function(obstacle) {
      scene.remove(obstacle);
    });      
  }
Well that's a bit of a bummer. I got myself confused by a boundary condition and I'm trying to teach this to beginners & kids?

The most obvious thing to do at this point is to avoid the topic in the book by not allowing reset. A straight progression through levels will avoid the boundary nicely. Then again, this might be a nice teaching moment. A mistake so common that we give it fancy names like boundary conditions / off-by-one / fencepost problems might give comfort to beginners. I will defer that decision until I write that chapter. The ultimate decision will likely rest on how long the chapter is without jamming this in.

For now, I solve the problem by removing the dependence on external state (current_level) and renaming the function from eraseOldLevel() to just eraseLevel():
  function eraseLevel(level) {
    if (level < 0) return;
    if (level > levels.length-1) return;
    var obstacles = levels[level];
    obstacles.forEach(function(obstacle) {
      scene.remove(obstacle);
    });      
  }
Now, it becomes the calling context's responsibility to know which level to erase. In the case of gameReset(), this is the current_level:
  function gameReset() {
    pause = false;
    eraseLevel(current_level);
    scoreboard.resetTimer();
    scoreboard.setMessage('');
    current_level = 0;
    drawCurrentLevel();
    moveGoal();
  }
I can then reset the current_level back to the starting point and allow the game to proceed.

With that, I believe that I am finally done with this game. This leaves me free to pursue any of the myriad of other obstacles in my way before I can get the book out of beta. Tomorrow.


Day #665

Saturday, February 16, 2013

Game Reset

‹prev | My Chain | next›

With a reasonable Scoreboard implementation in place for 3D Game Programming for Kids, tonight I hope to put it through its paces. It should be able to display "Game Over" messages when the game is lost. It should display "Win!" when the game is won. It should support resetting the game at any point.

I already have "Game Over" working:



The Scoreboard work from last night makes this straight-forward:
  scoreboard.onTimeExpired(function() {
    scoreboard.setMessage("Game Over!");
    gameOver();
  });
Actually, winning the game is similarly easy to code up with last night's scoreboard work. A completed level with no more levels remaining results in a call to gameOver():
  function levelUp() {
    current_level++;
    if (current_level >= levels.length) return gameOver();
    eraseOldLevel();
    drawCurrentLevel();
    moveGoal();
  }
If there is still time remaining on the scoreboard, then the scoreboard will display "Win!":
  function gameOver() {
    if (scoreboard.getTimeRemaining() > 0) scoreboard.setMessage('Win!');
    scoreboard.stopCountdown();
    scoreboard.stopTimer();
    // ...
  }
Even if the game is over for less desirable reasons, both the countdown and elapsed time timers are halted as well.

It takes some effort, but it is possible to win the game:



That leaves reset. The scoreboard does not support reset yet, nor does the elapsed timer that is used in the scoreboard. The countdown timer does already support reset so that it could be reset between levels. Adding the same ability to the elapsed timer is a matter of changing the start time and starting the clock anew:
__Timer.prototype.reset = function() {
  this._start = (new Date).getTime();
  this.start();
};
I also add a convenience method directly to the Scoreboard class to save on dots:
Scoreboard.prototype.resetTimer = function() {
  this._timer.reset();
};
With that, I can add a key handler so that "R" resets the game and the clocks on the scoreboard:
  document.addEventListener("keydown", function(event) {
    var code = event.keyCode;
    // ...
    if (code == 82) gameReset(); // R
  });
  // ...

  function gameReset() {
    scoreboard.resetTimer();
    scoreboard.setMessage('');
    pause = false;
    eraseOldLevel();
    current_level = -1;
    levelUp();
  }
There is no need to reset the countdown—the levelUp() function already does that.

I think that may do it for the Scoreboard class. I even have nice help, accessible with a press of the question mark:



Which is thanks to a simple call to the Scoreboard's help() method:
  var scoreboard = new Scoreboard();
  scoreboard.timer();
  scoreboard.countdown();
  scoreboard.help(
    "Get the green ring. " +
    "Click and drag blue ramps. " +
    "Click and S to spin. " +
    "Regular R to restart. " +
    "Left and right arrows to move player. " +
    "Be quick!"
  );
I will poke around a bit more tomorrow, but assuming all is good, it could be time to move on to other outstanding issues in the book, of which there are many.

(live code of the game)


Day #664

Friday, February 15, 2013

Scrolling Messages in the Scoreboard

‹prev | My Chain | next›

I am in a bit of a quandary with the scoreboard that I am building for 3D Game Programming for Kids. On the one hand, it allows me to not inflict DOM programming on unsuspecting beginners. On the other hand, DOM coding makes for an easy way to write ugly code that can be juxtaposed with cleaner code.

For instance, I introduce functions by having the reader add text to a log DIV element:
message = document.createElement('div');
message.textContent = 'My name is Chris.';
log.appendChild(message);

message = document.createElement('div');
message.textContent = 'I like popcorn.';
log.appendChild(message);
// ...
Eventually, I have readers define a message() function that performs the createElement() and appendChild() nonsense, leaving just the message being logged as the argument. Even a beginner can appreciate the beauty of a function when compared to DOM coding.

I do not know how I will replace DOM coding's ugliness in the narrative. But the remainder of the discussion relies on being able to do something that the Scoreboard cannot—appending messages. So I add an appendMessage() method:
Scoreboard.prototype.appendMessage = function(message) {
  function div(s) { return '<div>' + s + '</div>'; }
  this.setMessage((this._message || '') + div(message));
  return this;
};
Actually, I am not convinced that "append" is a useful term for beginners, so I create a couple of aliases:
Scoreboard.prototype.addMessage = Scoreboard.prototype.appendMessage;
Scoreboard.prototype.message = Scoreboard.prototype.appendMessage;
With that, I can append any number of messages to the board:
var scoreboard = new Scoreboard();
  scoreboard.
    appendMessage("Test 1").
    appendMessage("Test 2").
    appendMessage("Test 3").
    message("Test 4").
    addMessage("Test 5").
    message("Test 6");
I do not think it a good idea for the Scoreboard to grow vertically indefinitely. I introduce scrolling by specifying a maxHeight:
  var message_el = this.message_el = document.createElement('div');
  message_el.style.fontWeight = 'normal';
  message_el.style.color = 'white';
  message_el.style.maxHeight = (window.innerHeight * 0.2) + "px";
  message_el.style.overflowY = 'auto';
  el.appendChild(message_el);
It is so nice coding for a single, modern browser (the book only supports Chrome).

To ensure that the most recent message is always visible, I scroll then message element whenever it is updated:
Scoreboard.prototype.setMessage = function(message) {
  this.showMessage();
  this._message = message;
  this.message_el.innerHTML = this._message;

  this.message_el.scrollTop = this.message_el.scrollHeight;

  return this;
};
That seems to work quite well:



I make each of the scoreboard methods chainable because I appreciate being able to chain everything:
  var scoreboard = new Scoreboard();
  scoreboard.
    timer().
    countdown(50).
    help('Be excellent to each other.').
    appendMessage("Test 1").
    appendMessage("Test 2").
    appendMessage("Test 3").
    message("Test 4").
    addMessage("Test 5").
    message("Test 6");
I doubt that I will tell readers to code like that since it could distract from the topic at hand. Still, it is nice to have the option.

Regardless of the style of invoking those methods, I do think that I prefer using methods like this rather than setting these values in the constructor as I had been doing. The various constructor options were sure to confuse readers and the resulting logic was confusing me. These individual methods are much easier on both accounts.

I think that I am nearly done with this scoreboard class. Tomorrow, I will hook it back into the most recent game from the book to ensure that I am not overlooking anything.



Day #663

Thursday, February 14, 2013

Countdown to Awesome Games

‹prev | My Chain | next›

I had me a good old time building a scoreboard for kids to use in some of the 3D Game Programming for Kids chapters. I am doing this as a possible way to avoid teaching kids about DOM programming (and thus scaring them off from programming forever). But I have to admit—I had a good time whipping the scoreboard up. I also think that it could prove pretty useful for kids. Having slept on it, I think that the scoreboard could use a countdown timer in addition to the regular elapsed time timer.

Specifically, I can see it being pretty darn handy in the multi-level game that I whipped up recently. Each of the levels can be timed and I can use callbacks to decide what happens when time expires on any given level. For this to work, the countdown will need to support that time-expired callback as well as a way to reset the countdown when new levels of the game are reached.

My attempt at doing this involves subclassing last night's Timer class:
function __Countdown(options) {
  this.start = (new Date).getTime();
  this.length = options.length || 10;
  this.waitForTimeExpired();
}

__Countdown.prototype = new __Timer();
This gives me access to the methods that covert time to a printable string. In addition to needing a new waitForTimeExpired() method that will call the necessary callbacks, I have to override the diff() method from the sequential timer:
__Countdown.prototype.diff = function() {
  var now = this.last(),
      diff = now - this.start;

  if (diff > this.length * 1000) return 0;
  return this.length * 1000 - diff;
};
In this case, I do not want to print the different between the start time and the current time. Rather I want to print the difference between the elapsed time and the duration of this timer.

The new waitForTimeExpired() method is just a simple timeout:
__Timer.prototype.waitForTimeExpired = function() {
  var that = this;

  function _wait() {
    if (that.diff() > 0) return setTimeout(_wait, 500);
    that.stop();
    return that.onTimeExpired(that.toString());
  }
  _wait();
};
I add the countdown timer to yesterday's Scoreboard class so that I can display it with a boolean option:
  var scoreboard = new Scoreboard({
    showTimer: true,
    showCountdown: true,
    message: "Get the green ring. " +
             "Click and drag blue ramps. " +
             "Click and R to rotate. " +
             "Left and right arrows to move player. " +
             "Be quick!"
  });
  scoreboard.show();
Lastly, I add a reset method so that I can reset the time in between levels:
  var current_level = 0;
  function drawCurrentLevel() {
    scoreboard.resetCountdown(50 - current_level * 10);
    var obstacles = levels[current_level];
    obstacles.forEach(function(obstacle) {
      scene.add(obstacle);
    });      
  }
That seems to do the trick.



I still have a few kinks to work out—mostly in the API. With an elapsed timer, a countdown timer, a message, and a score tracker, the scoreboard class has gotten complicated. Especially the constructor. Still, this seems a promising thing to be able to include in the book.


Day #662

Wednesday, February 13, 2013

Pretty Simple Scoreboards

‹prev | My Chain | next›

My recent efforts have likely provided me with enough material to write the next two (possibly three) chapters in 3D Game Programming for Kids. Rather than pushing ahead to new material, now seems a good time to shore up the infrastructure.

There are a few improvements that I need to make in my fork of Mr Doob's code editor. It may be good to add a new template or two. I think that I will need a URL shortener of some kind to facilitate sharing of games. I still think it would be cool if I could get Faye in the book for multi-player gaming.

Before tackling that laundry list, I am going to stick closer to the code on which I have been working. Specifically, I would like to enable simple scoring and timing for games. That is, I would like to provide a simple scoreboard that kids can use.

At about the midway point in the book, I have kids type in the following:
  function addScoreboard() {
    var text = document.createElement('div');
    text.id = 'scoreboard';
    text.style.position = 'absolute';
    text.style.backgroundColor = 'white';
    text.style.borderRadius = "5px";
    text.style.padding = "0px 20px";
    text.style.left = "50px";
    text.style.top = "75px";
    document.body.appendChild(text);

    writeScoreBoard(
      'arrows to move;
space to jump'
    );
  }
Earlier incarnations of the book introduced the basics of the DOM. I am wavering on the matter somewhat at this point. If I remove it, then I have more time to introduce saner topics. But not touching on DOM coding at all seems a disservice to that which gave rise to the language that we all know and love.

For now, I am going to write the library so that I cannot use the lack of one as an excuse for keeping DOM coding in the book. If it warrants staying of its own merits, then so be it.

I spend a fair amount of time in the Scoreboard constructor deciding when to display different sections of the scoreboard (a text message, the score, the time). But it is just boolean logic and not all that difficult. I do end up spending time on the timer, which I make a separate class to be used as a property of the scoreboard:
function Scoreboard(options) {
  // ...
  this.timer = new __Timer();
  // ...
}
Rather than fiddle with timeouts or intervals, I stick with dates in that __Timer class:
function __Timer() {
  this.start = (new Date).getTime();
}
This makes the difference calculation easier:
__Timer.prototype.diff = function() {
  var now = (new Date).getTime();
  return now - this.start;
};
I then use that difference to format the elapsed time, zero padding as needed.:
__Timer.prototype.toString = function() {
  var minutes = "" + this._minutes(),
      seconds = "" + this._seconds();

  if (minutes.length == 1) minutes = "0" + minutes;
  if (seconds.length == 1) seconds = "0" + seconds;

  return minutes + ":" + seconds;
};
I do use an interval to update via a callback:
__Timer.prototype.onUpdate = function(cb) {
  var that = this;
  setInterval(function() {cb(that.toString());}, 100);
};
And, back in the Scoreboard class, I can update the timer element with that onUpdate callback:
Scoreboard.prototype.setTimer = function() {
  var that = this;
  this.timer.onUpdate(function(time) {
    that.timer_el.innerHTML = time;
  });
  this.timer_el.style.display = this.showTimer ? 'block' : 'none';
};
All of this means, that I can create a scoreboard with:
  var scoreboard = new Scoreboard({
    showTimer: true,
    message: "Get the green ring. " +
             "Click and drag blue ramps. " +
             "Click and R to rotate. " +
             "Left and right arrows to move player. " +
             "Be quick!"
  });
  scoreboard.show();

Which makes:



That is a lot easier than teaching beginners DOM methods. And after all, the goal of the book is to make JavaScript enjoyable—very few ever claimed working with the DOM was fun.



Day #661

Tuesday, February 12, 2013

Code Extracting for Fun (and less typing for small hands)

‹prev | My Chain | next›

I have a 350 line game candidate for 3D Game Programming for Kids. No kid (and no adult for that matter) is going to want to type in 350 lines of code. That's just crazy. Fortunately, much of the code is already boilerplate from templates in the ICE Code Editor. Some of the remaining code can either be placed in new templates or moved into a library. Tonight, I am going to move as much as possible out into a library to get a better idea of how large the actual code is.

It has been a while, but my fork of Mr Doob's code editor can be run locally as a simple node.js / express.js app:
➜  code-editor git:(gamingjs) ✗ node app
Express server listening on port 3000 in development mode
It would be nice if the order of the libraries did not matter, but, since my mouse library requires both Three.js and Physijs, I need to source them first, then my extracted mouse library:
<body></body>
<script src="/52/Three.js"></script>
<script src="/52/physi.js"></script>
<script src="/mouse.js"></script>
<script src="/52/ChromeFixes.js"></script>
<script>
The /52 libraries are local copies of the various libraries compatible with Three.js r52, which eventually make it up to http://gamingJS.com/ice.

Actually, I am able to move mouse.js ahead of the other two libraries if I add a timeout before monkey patching the built-in addEventListener():
function extendPhysijsAddEventListener() {
  if (typeof(Physijs) == 'undefined') return setTimeout(extendPhysijsAddEventListener, 5);

  var pjs_ael = Physijs.ConvexMesh.prototype.addEventListener;
  Physijs.ConvexMesh.prototype.addEventListener = function(event_name, callback) {
    if (event_name == 'drag') Mouse.addEventListener('drag', this, callback);
    pjs_ael.call(this, event_name, callback);
  };
}
extendPhysijsAddEventListener();
That does not seems particularly robust, so I will likely require readers to stick to proper ordering of the scripts (or add it to the boilerplate code).

With the mouse library extracted, my game is down to... 267 lines. Progress, but not enough. A bit more work eventually gets me down to 250 lines of code, with roughly 30 of it boilerplate. That is about 50 lines of code past my limit for a chapter. It is unfortunate, but not altogether unexpected. The game is moderately complex, after all:


The game includes a player (the red ball), movable ramps, stationary obstacles, screen boundaries, and a goal. On top of that, the recently added multi-level play is just too much. Indeed, if I remove the multi-level code, I get down to 200 lines of code. So it seems that, if I want multi-level games in the book, then I am going to need a separate chapter. I am not sure my editor is going to be happy about that.

The mouse handling code is in pretty ugly shape, but is probably good enough for my needs. So I commit that and put to the 3D Game Programming for Kids site. With that, I do not think there is much left for me in this chapter save to write the actual chapter. If a separate chapter is required, I may as well start on a new scoreboard library, which can be used to keep track of total time elapsed and score in the multi-level game. That is something for tomorrow.


Day #660