Function#debounce Useful JavaScript Game Extension #36

I got this from Underscore.js.

###*
Calling a debounced function will postpone its execution until after 
wait milliseconds have elapsed since the last time the function was 
invoked. Useful for implementing behavior that should only happen after 
the input has stopped arriving. For example: rendering a preview of a 
Markdown comment, recalculating a layout after the window has stopped 
being resized...
 
lazyLayout = calculateLayout.debounce(300)
$(window).resize(lazyLayout)
 
@name debounce
@methodOf Function#
@returns {Function} The debounced version of this function.
###
Function::debounce = (wait) ->
  timeout = null
  func = this
 
  return ->
    context = this
    args = arguments
 
    later = ->
      timeout = null
      func.apply(context, args)
 
    clearTimeout(timeout)
    timeout = setTimeout(later, wait)

Already I can see an important use for it. In the code editor we copy out the buffer every keyup. This can lead to a bunch of extra work for the browser when typing. Usually it’s not a big deal because the files are generally small, but on larger files it can add up and cause some lagging. When using debounce the editor will only trigger after enough milliseconds have elapsed since the end of new keyup events. This will keep the IDE from lagging on larger files while you are typing.

Another place debounce is useful is in a game that implements a Halo style health regeneration. Every time the player takes damage a debounced startRegen function could be called. That way the regen will only start after enough time without taking damage has elapsed. In the game version it would probably be better to constrain it to ticks of the engine, or engine elapsed time for more accuracy.

I’m always a big fan of Function functions, they are a great way to reduce boilerplate and solve higher-order problems.

JavaScript Number#even and Number#odd, Useful JavaScript Game Extensions #33 and #34

Wow, it’s been a while! But enough with the small talk, time for some more simple JavaScript extensions!

###*
Returns true if this number is even (evenly divisible by 2).
 
@name even
@methodOf Number#
 
@type Boolean
@returns true if this number is an even integer, false otherwise.
###
Number::even = ->
  this % 2 == 0
 
###*
Returns true if this number is odd (has remainder of 1 when divided by 2).
 
@name odd
@methodOf Number#
 
@type Boolean
@returns true if this number is an odd integer, false otherwise.
###
Number::odd = ->
  if this > 0
    this % 2 == 1
  else
    this % 2 == -1

These are just a couple of simple methods so you can do things like if exponent.even() to make code quite a bit more readable. The only trick is for Number#odd, due to the way the default mod operator works in JavaScript it would be a pain to check if a negative number were odd without this helper.

Stay tuned for the next 222 parts of the series!

Optimizing Matrix and Point Classes in PixieEngine

I was able to go a long way without optimizing the Matrix and Point classes. Since most of my games and prototypes were relatively simple or made limited use of points and matrices it didn’t matter too much, that is until Red Ice, which was using 5 physics steps per frame and computing tons of vectors and collision responses.

Optimizing the Drawing

The problems became evident in the profiler. First was the classic of “draw less”. Because Red Ice is running at 1024×768 even clearing the entire canvas can get costly, not to mention copying over the ice and blood canvases to the main one. In order to break through this bottleneck I separated out the rink/background and blood canvases into layers that stay below the main canvas. Since the rink never redraws and since the blood only adds and erases strokes in a semi-permanent way, this saves copying a ton of image data to the main canvas, which in turn saves a ton of time. I still clear the entire main canvas every frame, but implementing selective clearing is a big project and would have limited performance gains (we still need to clear big chunks of the canvas most updates with all the stuff moving around).

Optimizing Joystick Input

Now that we are drawing less the next big bottleneck came from joystick input. This sounds pretty ridiculous for anyone coming from a non-web background, but considering that using 6 XBox 360 controllers as input into an HTML5 game was pretty much unheard of, it’s not inconceivable that the first time it has been done might not be the most optimal. The core of the issue was that every single piece of data that is transferred from the native extension into JavaScript slows things down. Even something as simple as an array of true/false values for buttons. Even an array of six integers for six axes. Even an object with two properties buttons and axes. Each single piece slows things down.

The first performance improvement I made was to use a single integer for all the buttons, holding each one in a bit. This gave an immediate 25% speedup, confirming that going from an array of 10 booleans to 1 integer (reducing the number of items passed across) speeds things up. Next, since I wasn’t using the additional 4 axes I decided to only pass 2 axes as a temporary fix. This also gave a decent speedup, especially at the 6 controller mark, because each additional controller was additional data.

At this point I was stumped for a while, but then I realized that if I could pass a single JSON string across that would only be a single item of data, no matter how many joysticks or axes were active. I assumed that browsers were pretty good at parsing JSON, since that is most of what the JavaScript interpreter does on all web pages, and after a day of struggling getting C++ to spit out JSON it was legit and joystick input was no longer an issue.

Optimizing the Points

This finally exposed the remaining problems of the Point class and garbage collection being the next bottle neck. Due to all the physics updates and point computations it was creating tons of new Point objects and each additional point operation created more because the operations were non-destructive. The good news is that I have a full test suite for the Point class, so that I can refactor and optimize without any fear. This was quite valuable when testing crazy new ideas to see if they would improve performance without breaking everything. I figured that since I was creating so many points that if I added optional destructive methods, and used them in the right places, that could reduce the new point creation and also GC load quite a bit.

I wish that JavaScript or CoffeeScript would allow for a shortcut to use ! as a method suffix like in Ruby, because it is a known convention for destructive methods. The closest I could come would be point["add!"](otherPoint), which was too brutal on the mind and eyes to make it into common usage. If CoffeeScript could auto-compile point.add!(otherPoint) into the index-operator notation like it does with a.class and a.new then it would be okay, but until then the ! suffix is out.

JavaScript does allow for the $ symbol in variable and method names so, by necessity, I have chosen to use $ as the glyph of destruction, which has its own poetry in a way. point.add$(otherPoint) not bad, but not my first choice.

Now, armed with new destructive methods, I set about looking for places in the physics and collisions where I could slip them in to prevent the creation of unwanted/unused points. Then a funny thing happened, the majority of the places I looked needed the operators to be non-destructive, and it was difficult to see exactly where a destructive method could be added without unwanted side-effects except in a few simple situations such as point = point.add$(delta) => point.add$(delta).

While I was cleaning up the Point code I was thinking about a conversation I had on GitHub about the performance benefits from using prototype inheritance rather than object augmentation. This sounded like a good idea in this case, as Points are primitive objects with their x and y properties on the outside and all of their methods using this everywhere. The one sticking point was that I could not abide having to stick new in front of the point constructor in ten thousand places in my existing and future code. If only there was some way to get the advantages of prototype performance, without pushing the syntactic hassles onto the people using the class.

The good news is that there is a way to set an objects prototype. All you need to do is to set the __proto__ property. So now my Point constructor looks like this:

  Point = (x, y) ->
    __proto__: Point::
    x: x || 0
    y: y || 0

You know you’ve been programming in the browser too long when __proto__: Point:: becomes a thing of beauty.

All the instance methods are defined below as follows:

  Point:: =
    copy: ->
      Point(this.x, this.y)
 
    add: (first, second) ->
      this.copy().add$(first, second)
 
    add$: (first, second) ->
      if second?
        this.x += first
        this.y += second
      else
        this.x += first.x
        this.y += first.y
 
      this
 
    ...

This gives all the performance benefits of using prototypes rather than making an anonymous function for every method as well as the additional side benefit that developers can extend Point.prototype with additional methods for use in their own projects if they want to. Another advantage is that the syntax remains unchanged, no need to use the new operator, and all the tests still pass.

The best news is that this provides a 90% reduction in time that the code spends constructing and garbage collecting points, and was simple enough to pull into the Matrix class with a two line change as well. For primitive objects like Points, Matrixes, Arrays, Numbers, and the like I wholeheartedly recommend this approach. For complex objects that require mixins, private variables, and instance variables I don’t think it will be possible because each object actually does need it’s own functions that are in the correct closure scope.

Another interesting thing is that this last optimization voided the assumption of my previous one about destructive operators. I assumed that because creating points was expensive it would be worthwhile to go to extra lengths to prevent their creation unnecessarily. Using the prototypesque construction the cost of point creation and garbage collection was reduced so much that it’s not worth it to try and squeeze out the now slight performance gains that would produce except in the hottest inner loops. I’ll still keep the destructive methods around for situations where points actually want to be updated in place, like p = p.norm(speed) => p.norm$(speed), but I won’t be quick to begin trying to “optimize” by defaulting to using destructive methods and then spend hours debugging issues that come up because two objects are actually sharing the same point reference.

Points are cheap now, use them freely!

Better window.requestAnimationFrame Shim

There’s been quite a bit of copy/pasting of the compatibility shim for requestAnimationFrame going around on the Net, which is all fine and dandy, but sadly the popular shim isn’t compatible with passing in the timestamp on the setTimeout fallback.

Here’s the improved one:

window.requestAnimationFrame ||= 
  window.webkitRequestAnimationFrame || 
  window.mozRequestAnimationFrame    || 
  window.oRequestAnimationFrame      || 
  window.msRequestAnimationFrame     || 
  (callback, element) ->
    window.setTimeout( ->
      callback(+new Date())
    , 1000 / 60)

Also, a JavaScript version for those of you who have suffered the misfortune of not choosing to use CoffeeScript.

window.requestAnimationFrame || (window.requestAnimationFrame = 
  window.webkitRequestAnimationFrame || 
  window.mozRequestAnimationFrame    || 
  window.oRequestAnimationFrame      || 
  window.msRequestAnimationFrame     || 
  function(callback, element) {
    return window.setTimeout(function() {
      callback(+new Date());
  }, 1000 / 60);
});

Take notice of the param passed to the callback +new Date(). It’s the timestamp that leading implementations pass in.

It has also been rumored that Chrome10 doesn’t pass in the timestamp either, so for super reliability you’ll want to have timestamp ||= +new Date() as the first line of your callback as well.

There were also several issues mentioned in this gist which have not been decisively resolved, so any feedback is certainly welcome. I also decided to just polyfill that ‘ish, because it seems legit enough.

Good luck and Godspeed.

Array#compact Useful JavaScript Game Extension #31

###*
Returns a copy of the array without null and undefined values.
 
@name compact
@methodOf Array#
@type Array
@returns An array that contains only the non-null values.
###
Array::compact = ->
  this.select (element) ->
    element?

This array compact method is pretty useful. Prototype.js got it from Ruby and I got it from both. Now it’s a proud member of my CoffeeScript corelib.

Not much interesting behavior wise, but one thing to take note of is how short and sweet the code is thanks to using CoffeeScript.

31 down, 225 to go. Peace!

LiveEdit

You know those edit in place plugins for jQuery? Well I couldn’t find any that met these two criteria:

  1. Work on elements not yet in the DOM
  2. Didn’t post anything to the server

Now the first requirement probably makes a lot of sense to you, but I bet you’re wondering about the second one. In these crazy mixed up times with rich HTML5 apps, I don’t want to post to the server often. Maybe I’m using local storage, or perhaps I just want to send a big heap of JSON now and again, but I definitely don’t want to post every time anyone changes the value of an editable field.

So after examining all the terrible options (classic first step when choosing jQuery plugins) I decided to throw my own terrible option into the mix.

(($) ->
  $.fn.liveEdit = () ->
    this.live 'dblclick', () ->
      $this = $(this)
 
      return if $this.is("input")
 
      textInput = $("<input/>",
        class: $this.attr("class")
        "data-origType": this.tagName
        id: if id = $this.attr("id") then id else null
        type: "text"
        value: $.trim($this.text())
      )
 
      $this.replaceWith textInput
 
      textInput.focus().select()
 
    this.live 'blur keydown', (event) ->
      if event.type == "keydown"
        return unless event.which == 13 || event.which == 9
 
      $this = $(this)
 
      return if $this.data("removed")
      return unless $this.is("input")
 
      $this.attr("data-removed", true)
 
      $this.replaceWith $("<" + $this.data("origType") + " />",
        class: $this.attr("class")
        id: if id = $this.attr("id") then id else null
        text: $this.val()
      )
 
    return this
 
)(jQuery)

There are two tricks here. The first is that this plugin is really a macro that makes two calls to live. The second is that the editable content keeps it’s same id and class when switched out to a text field, so as long as your selector is not based on the element type it will work. There’s plenty of room for improvements, but this is a super simple first step that meets my needs. Enjoy!

jQuery Drag Image From Desktop Plugin

Here’s the CoffeeScript for a jQuery plugin I wrote that makes accepting images dragged in from the desktop super easy. The event.fix part at the beginning is because jQuery currently doesn’t pass on the dataTransfer attribute of events. Once that’s taken care of we create the plugin.

(($) ->
  $.event.fix = ((originalFix) ->
    (event) ->
      event = originalFix.apply(this, arguments)
 
      if event.type.indexOf('drag') == 0 || event.type.indexOf('drop') == 0
        event.dataTransfer = event.originalEvent.dataTransfer
 
      event
 
  )($.event.fix)
 
  $.fn.dropImageReader = (callback) ->
    stopFn = (event) ->
      event.stopPropagation()
      event.preventDefault()
 
    this.each () ->
      element = this
      $this = $(this)
 
      $this.bind 'dragenter dragover dragleave', stopFn
 
      $this.bind 'drop', (event) ->
        stopFn(event)
 
        Array.prototype.forEach.call event.dataTransfer.files, (file) ->
          imageType = /image.*/
          if !file.type.match(imageType)
            return
 
          reader = new FileReader()
 
          reader.onload = (evt) ->
            callback.call(element, file, evt)
 
          reader.readAsDataURL(file)
 
)(jQuery)

The plugin takes a callback that will be called when any of the matched elements receive an image file via the drop event. Here’s an example usage:

$(".tiles").dropImageReader (file, event) ->
  img = $ "<img/>",
    alt: file.name
    src: event.target.result
    title: file.name
 
  $(this).append img

For image drops all you really care about is the file name and the data url, but if for some reason you need different file results here’s the line to modify: reader.readAsDataURL(file). You can also extend or alter what is passed to the callback if this is too mundane for your needs.

And here’s the JS version for anyone eager to copy/paste:

  (function() {
    $.event.fix = (function(originalFix) {
      return function(event) {
        event = originalFix.apply(this, arguments);
 
        if (event.type.indexOf('drag') === 0 || event.type.indexOf('drop') === 0) {
          event.dataTransfer = event.originalEvent.dataTransfer;
        }
 
        return event;
      };
    })($.event.fix);
 
    $.fn.dropImageReader = function(callback) {
      var stopFn;
 
      stopFn = function(event) {
        event.stopPropagation();
        event.preventDefault();
      };
 
      return this.each(function() {
        var $this, element;
 
        element = this;
        $this = $(this);
 
        $this.bind('dragenter dragover dragleave', stopFn);
 
        $this.bind('drop', function(event) {
          stopFn(event);
 
          Array.prototype.forEach.call(event.dataTransfer.files, function(file) {
            var imageType, reader;
 
            imageType = /image.*/;
            if (!file.type.match(imageType)) {
              return;
            }
 
            reader = new FileReader();
 
            reader.onload = function(evt) {
              return callback.call(element, file, evt);
            };
 
            reader.readAsDataURL(file);
          });
        });
      });
    };
  })(jQuery);

Happy image dropping!