Useful JavaScript Game Extensions: rand

Rand Atlas Shrugged
Part 2 of my 256 part series: Useful JavaScript Game Extensions!

/**
 * Generate uniformly distributed random numbers.
 *
 * @param {Number} [n]
 * @returns A Random integers from [0, n) if n is given, otherwise a random float
 * between 0 and 1.
 * @type Number
 */
function rand(n) {
  if(n !== undefined) {
    return Math.floor(Math.random() * n);
  } else {
    return Math.random();
  }
}

Some games make use of random numbers… a lot. So when choosing the best interface it is important to keep that in mind. Often times uniform distributions are the way to go, such as when selecting from an array, or rolling a six sided die. If one often has need of a variety of random distributions then having different methods live in a Random namespace would probably be best. For simple, everyday use a little rand(6) is very convenient.

Useful JavaScript Game Extensions: Clamp

Give him the clamps!

Part 1 of my new 256 part series on simple JavaScript extensions to make game programming easy and fun!

/**
 * Returns a number whose value is limited to the given range.
 *
 * Example: limit the output of this computation to between 0 and 255
 * (x * 255).clamp(0, 255)
 *
 * @param {Number} min The lower boundary of the output range
 * @param {Number} max The upper boundary of the output range
 * @returns A number in the range [min, max]
 * @type Number
 */
Number.prototype.clamp = function(min, max) {
  return Math.min(Math.max(this, min), max);
};

Note that this extends the behavior of the Number class. Some may consider it poor form to “mess with” the JS built-ins, but I consider it poor form to expose a worse than necessary interface to the programmer. Compare:

// Namespace?
STRd6.Util.clamp(x * 255, 0, 255);

// global function clamp?
clamp(x * 255, 0, 255);

// Clean, simple and object oriented!
(x * 255).clamp(0, 255);

Stay tuned for the next 255 parts of the series!

Matrix.js 1.0.1 Released

Thoroughly documented, tested, and minifies very small (1.4k), this is the best release yet!

Observe the sweetness:

  // Rotate 90 degrees
  var matrix = Matrix.rotation(Math.PI / 4);

  // Scale and move
  var finalTransform = matrix.scale(1/2).translate(125, 175);

  canvas.withTransform(finalTransform, function() {
    // Draw a circle or whatever in the transformed coordinates
    // canvas.fillCircle(0, 0, 50);
  });

TextArea – The All-Purpose, All-Platform Editor

I finally took the plunge and switched to this simple, yet powerful new editor. The main reason for my change was the ubiquity of it. It is supported by every major browser, and on every major OS**, straight out of the box, with no modifications.

Cursor Navigation

Home Move cursor to beginning of line
End Move cursor to end of line
Up Move cursor up one line
Down Move cursor down one line
Left Move cursor one character left
Right Move cursor one character right

Deletion

Backspace Delete selection or character behind the cursor if no selection
Delete Delete selection or character ahead of cursor if no selection
Ctrl+Backspace Delete previous word (useful for correcting typos)
Ctrl+Delete Delete next word
Ctrl+Shift+Backspace Delete from cursor to the beginning of the line
Ctrl+Shift+Delete Delete remainder of line

Useful Combos

Ctrl+Left Jump previous word
Ctrl+Right Jump next word
Ctrl+Shift+Right Select next word
Ctrl+Shift+Left Select previous word
Home, Shift+End, Delete Delete line

Mouse Tricks

Double Click Select word
Triple Click Select line

Meta-Keys

Shift Selection mode
Control Word jump mode

Try this amazing new editor for yourself!

Psst… It’s the comments box…

** Once Steve Jobs sorts out the goddam Command, Control, Option key debacle

Giving up on has_many_polymorphs bug after an hour

I remember reading about has_many_polymorphs a couple of years ago, then again last year. Each time around when I wanted some sort of polymorphic has_many :through. Each time I figured, “Eh, it’s just another couple of tables” or “I can just map them in a method in the model, there’s not that much data”. But this time I finally gave it a try.

First I got this error when using the gem with Rails 3: has_many_polymorphs/support_methods.rb:69:in `warn': wrong number of arguments (1 for 3) (ArgumentError). So I looked at the github network graph and found a branch that seemed to fix that.

That worked, but next I got this: Could not find a valid class for :collections_items (tried CollectionsItem). If it's namespaced, be sure to specify it as :"module/collections_items" instead. (ActiveRecord::Associations::PolymorphicError) What are they trying to do, pluralize post positive adjectives? My model is named CollectionItem, which seems sensible to me.

This time I try adding a :through option to set it straight.

  has_many :collection_items
  has_many_polymorphs :items, :through => :collection_items, :from => [:sprites, :collections]

Now I get the following amazing error message:

has_many_polymorphs/class_methods.rb:441:in `create_has_many_through_associations_for_children_to_parent': You can't have a self-referential polymorphic has_many :through without renaming the non-polymorphic foreign key in the join model. (ActiveRecord::Associations::PolymorphicError)

This probably makes sense to everyone except me, but I’ve got to work with the skills I’m given.

What I’d really like is for polymorphic has_many :through to ‘just work'(TM).

Something kind of EXACTLY LIKE this:

class CollectionItem  true
end

class Collection  :collection_items
end

Why is that so hard? No one will ever know…

So I did what I always do:

class Collection < ActiveRecord::Base
  belongs_to :user
  has_many :collection_items

  def items
    collection_items.map(&:item)
  end
end

After all, there’s not that much data anyway…

Rails Migration undefined method `type=' for nil:NilClass

Just wanted to share a quick tip about an error I ran into. When running a Rails migration and changing a column that doesn’t exist, you’ll get an undefined method `type=' for nil:NilClass error. The cause for me was that I had used change_column on login, which my table didn’t have instead of email, which my table did.

Load an Image from an Arbitrary URL into HTML Canvas

While working on Pixie we wanted to be able to import any image from the web into the editor. Ideally this would all occur in client-side JavaScript, but due to a “security” restriction* I believe that it is not possible without extensive workarounds. Fortunately loading an image on the server is actually much easier than working with the Canvas ImageData API (no joke), so though a loss from an efficiency standpoint, from a simplicity standpoint it may be win.

This particular implementation requires RMagick, though any image library should be about as easy. The first step is to read the image from the user-supplied URL, next gather the width and height meta-data.

  def data_from_url(url)
    image_data = Magick::Image.read(url).first
    width = image_data.columns
    height = image_data.rows

    data = image_data.get_pixels(0, 0, width, height).map do |pixel|
      hex_color_to_rgba(pixel.to_color(Magick::AllCompliance, false, 8, true), pixel.opacity)
    end

    return {
      :width => width,
      :height => height,
      :data => data,
    }
  end

The only tricky part is converting all the pixel data into a format that can be used by JavaScritpt. I decided on rgba format as that is simple to read, implement, and test. The one downside that I can see is that for large images it will be somewhat inefficient to convert each pixel into a large text string, but since I mostly plan on dealing with images < 100×100 it shouldn’t be a big deal. The hex_color_to_rgba helper method takes care of all the dirty work of converting the moderately unwieldy output from RMagick. It turns "#FAFAFA", 65535 into "rgba(250, 250, 25, 1)" a format that browsers respect.

  def hex_color_to_rgba(color, opacity)
    int_opacity = (Magick::QuantumRange - opacity) / Magick::QuantumRange.to_f

    match_data = /^#([A-Fa-f0-9]{2})([A-Fa-f0-9]{2})([A-Fa-f0-9]{2})/.match(color)[1..3].map(&:hex)

    "rgba(#{match_data.join(',')},#{int_opacity})"
  end

In the end we return a hash containing the the width, height, and pixel data. This can easily be converted to json via the to_json method, or whatever other format tickles your fancy.

The JavaScript code is pretty dumb, it reads the width and height, then iterates over the data and paints each pixel individually. Not super exciting, but simple and effective. That’s that for loading arbitrary images into HTML Canvas, though I hope one day to develop an entirely JS solution.

* I do not understand the reasoning why you are allowed to load an image from an arbitrary url and display that image, but not have access to the pixel data from it. It sounds pretty silly, especially considering that you can load scripts from an arbitrary URL and run them, or load json from an arbitrary URL. I have no idea what “security” that this restriction provides, other than the “security” of having to load images through your server and hinder the user experience. Here’s a related discussion, but I still don’t get it. This is a hole in my personal understanding, so if anyone knows the reasoning behind the restriction I’d love to hear it.

jQuery style events

jQuery events are cool. But what if you want to trigger some events but not extend all your JavaScript objects to jQuery ones? If you are drawing thousands and thousands of these objects to screen you probably don’t want to worry about the overhead of making them jQuery objects.

Here’s how you can write your own simple jQuery style events:

function Meteor() {
  var eventCallbacks = {
    'destroy': alert('destroyed')
  };

  var destroyed = false;

  var self = {
    bind: function(event, callback) {
      eventCallbacks[event] = callback;
    },
    destroy: function() {
      if (!destroyed) {
        destroyed = true;
        self.trigger('destroy');
      }
    },
    explode: function() {
      // Kaboom
    },
    trigger: function(event) {
      eventCallbacks[event](self);
    },
  };
  return self;
}

Here’s how you use it

var meteor = Meteor();
meteor.bind('destroy', function() {
  meteor.explode();
});

...

meteor.destroy();

matrix.js Demo

Excerpts, source code and images are great, but a working example is even greater. That’s why I’ve cobbled together a working demo just for you! View it here: Matrix Demo. I’ve extracted it to just the minimum that you need to work with. Jump Straight to the guided tour.

Some Background

Though matrix.js has no dependencies, I depend on jQuery to code in JavaScript without sobbing uncontrollably. This code makes use of jQuery, though you should be fine if you are familiar with any major JS framework.

There are two main components that need to be brought together to make a decent demonstration: object(s) with nested component(s) and a canvas that can understand matrices.

Canvas

The canvas implementation of matrix transformations is mega-clunky, but that won’t bother us any longer.

        var canvas = $('#gameCanvas').get(0).getContext('2d');

        $.extend(canvas, {
          withTransform: function(matrix, block) {
            this.save();

            this.transform(
              matrix.a,
              matrix.b,
              matrix.c,
              matrix.d,
              matrix.tx,
              matrix.ty
            );

            try {
              block();
            } finally {
              this.restore();
            }
          }
        });

The withTransform method takes a matrix and a function (code block). It handles saving the context, applying the elements in the matrix to the correct parameters, calling the code block, and restoring the saved context no matter what, even if the code block throws an exception up in its face after eating bad seafood. It will stop at nothing to make your programming dreams come true.

Rather than handle all that junk ourselves every time want to draw a rotated top hat, we can instead do something like:

        var matrix = Matrix.rotation(Math.PI/2);
        canvas.withTransform(matrix, function() {
          // I'm so carefree, I can draw and draw without worrying about
          // saving or restoring the context, or what order those 4 trig
          // dealies go in. Thank you matrix.js for saving my life and
          // becoming my new best friend. <3<3<3 XOXO !!!1
        });

Hand-crafted matrix elements do not have more value than those forged in the heart of a machine.

Objects

Ok, so the canvas can handle matrix transforms easily, big deal. I just want to put a top hat on a dinosaur, make him dance, and laugh on into the early morning light.

  var hat = GameObject("images/accessories/tophat.png", Matrix.translation(30, -32));

  var dino = GameObject("images/levels/dino1.png", Matrix.translation(320, 240), [
    hat
  ]);

Done!

I’ve called my objects GameObject because sometimes I make games. The constructor takes 3 arguments, a url for an image, the transformation matrix of the object, and a list of component objects to draw inside it. I’ve got a dinosaur and the dinosaur has a hat.

To actually draw the dino we have a classic game loop.

  setInterval(function() {
    canvas.fill('#A2EEFF');

    dino.draw(canvas);
  }, 33);

Only the dino needs to be drawn because he’ll take care of drawing his own hat.


Making him dance

This is the part where you open up the demo page, fire up your JS console, and play along.

We want to move the dino up a little bit. Remember, up is negative.

  dino.translate(0, -50);

Now we want to rotate him.

  dino.rotate(Math.PI / 3);

Now we want him to walk warily down the street with his brim pulled way down low.

  // Brim down
  hat.translate(0, 10);

  // Walk warily...?
  (function() {
    var i = 0;
    setInterval(function() {
      dino.rotate(Math.sin(i / 8) * (Math.PI / 6));
      i++;
    }, 33);
  }());

Well you get the idea. If things get too nuts just refresh and start again.

Images Extras (Bonus Section!)

Even assuming you already have an image and HTML5 Canvas all set up, it is still a giant pain to draw it on there. Not to mention loading an image or waiting for it to load. So step one is to remove the tedium from drawing images onto the canvas forever.

  var sprite = Sprite.load(imageUrl);
  sprite.draw(canvas);

Wow, that was easy! Take a look at the Sprite class in the source for more on this miraculous occurrence.