Wednesday, July 28, 2010

Always use getBBox() for Raphaël Coordinates

‹prev | My Chain | next›

Up today, I would like to get the onAnimation callback in my (fab) game working with my raphael-animate-frames plugin.

The onAnimation callback is standard raphaël.js that is called after every step of an animation. The animation in my (fab) game does quite a bit. The first thing is move a label (the player's name) along with the player:
  avatar.onAnimation(function(){
console.debug(avatar.attr);
console.debug("x: " + avatar.attr("cx") + ", y: " + avatar.attr("cy"));
self.label.attr({x: avatar.attr("cx"), y: avatar.attr("cy") + Player.shadow_distance});

// more animation code...
}
Unfortunately, that does not work (nor does any of the rest of the animation code). The debug code in there tells me that the x-y coordinates are undefined even though the correct function is being called:
x: undefined, y: undefined
function () {
// delegate to last object in first frame
var obj = this.list[0][this.list[0].length-1];
return obj.attr.apply(obj, arguments);
}
Dang. It would seem that pulling the attr() values off of SVG is not as consistent as I would like. Fortunately, the getBBox() method is available and seems quite a bit more reliable.

If there is one thing that is slowly dawning on me as I do more and more raphaël work, it is that getBBox() is what I need when I want location information. The values that attr() is supposed to have (cx, c, rx, etc) are very much hit-or-miss. I suspect that these are only available if explicitly set when drawing, but that is an investigation for another day. For now, I replace every instance of attr() with a corresponding getBBox() call:
  avatar.onAnimation(function(){
self.label.attr({x: avatar.getBBox().x, y: avatar.getBBox().y + Player.shadow_distance});

// more animation code...
}
One last thing before stopping for the night is that the x-y coordinates from getBBox() are the top-left coordinates. The reason that I had been using cx-cy before was because I wanted the center coordinates for the object.

For that, I add a method, getCenter() to my raphael plugin:
    getCenter: function() {
var bounding_box = this.getBBox();
return {
x: Math.floor(bounding_box.width/2) + bounding_box.x,
y: Math.floor(bounding_box.height/2) + bounding_box.y
};
}
Now I can get more accurate placement in my animations with:
  avatar.onAnimation(function(){
self.label.attr({x: avatar.getCenter().x, y: avatar.getCenter().y + Player.shadow_distance});

// more animation code...
}
I got in trouble adding non-standard methods to my raphaël plugin last night, so I will sleep on the getCenter() method name and implementation before committing it to raphael-svg-frames. Then it will be time to move onto collision detection.


Day #178

3 comments:

  1. It took me a while to find out about the goodness of getBBox too...I like your very instructive blogging style, keep up with the good work!

    ReplyDelete
  2. This blog post just saved me from shooting myself in the dick. Thank you.

    ReplyDelete
  3. Thank you! Searching for this information for a while.

    ReplyDelete