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);
  });

New Pixie Color Palette

On Pixie we recently redesigned our color palette.

It’s important to choose a good default color palette for your application. For most people the color palette is one of their first impressions of your product, and the palette will largely determine the kinds of things they can make. In addition less than half the people who interact with your product for the first time will even use a custom color, or a different tool. That makes the default choice doubly important.

Let’s say you were stranded on a desert island and only got to take 10 crayons, what would you take? Answering that question is very similar to answering the default palette question.

Since prehistoric times people have enjoyed drawing what they see in their environment. I have a friend, and he wears a blue shirt, so I need the color Shirt Blue. Sometimes he eats apples; I’d better also have Apple Red. The apple came from an apple tree, good thing there’s Leaf Green, and Tree Brown.

Modularity and context are also important. Each of these colors shouldn’t only be a uni-tasker. Shirt Blue can double as Lake Blue when drawn on the ground. Hair Blonde or Hay-bale Yellow? Same color, but it depends on the context. Remember, you’re trapped on an island and need to make these colors count.

The classic defaults are pretty weak. If they had crayola names they’d be something like Maximum Red, Maximum Green, Maximum Blue, Horrendo Cyan, Staring into the Sun Yellow and Good God Man Make it Stop Magenta.

Maybe your friend eats GMO Apples (for maximum redness), as well as wearing outrageous clothing. Mine usually don’t, that’s why I developed the Best Friend’s Apple / Desert Island default color palette.

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();