Sunday, July 25, 2010

Debugging with Chrome's Javascript Console

‹prev | My Chain | next›

I tried using my raphaël.js plugin for animating SVG frames (raphael-animate-frames) without much luck yesterday. I had hoped to pull it into my (fab) game, but it did not go as smoothly as I hoped.

When I click in my (fab) game, the player should walk to the click location—ideally animated as seen in my plugin demo. What happens, however, is that the there is no smooth translation of the player from point A to point B and the player's body parts fly apart:



Each body part is moved indivdually by the translate_object() method in raphael-animate-frames. I start my troubleshooting there with some conspicuously placed console.debug statements:
     translate_object: function(frame, x, y, seconds) {
// offset between end coordinates and current location
var x_diff = x - frame[0].getBBox().x;
var y_diff = y - frame[0].getBBox().y;

for (var i=0; i<frame.length; i++) {
var obj = frame[i];
// calculate path starting from absolute coordinates and moving
// relatively from there by the offset
var p = "M " + obj.getBBox().x + " " + obj.getBBox().y +
" l " + x_diff + " " + y_diff;

console.debug(obj);
console.debug(p);

// animate along that path
obj.animateAlong(p, seconds);
};
}
Each obj in the frame is an SVG stroke, drawing a body part. By console.debugging the object itself, I can see the object's properties in Chrome's javascript debugger:



I can also expand attributes on that object to see if they are what I expect them to be. For instance, I want to make sure that the body parts are raphaël objects whose prototype has methods like animateAlong() (used at directly the end of my translate_object method) and attr() (used to get/set raphaël properties):



I have learned quite a lot from just this bit of information. The translate_object method is being called. It is being called for each of the body parts. Despite the added complexity of being used in my (fab) game, the body are still regular raphaël elements—seemingly with all of the correct behaviors still attached. I am also not seeing any javascript errors so, as far as Chrome is concerned, nothing is going wrong.

So why does my player get transported immediately the destination coordinates (rather than gliding there) and why is my player in pieces when it gets there?



I keep thinking about the scene from GalaxyQuest where they are trying to transport the alien pig-thing and it gets turned inside out (and explodes).

Next, I try from a different approach. Everything seems to be getting called as I expect it to, let me try to move an individual part of the player through the Chrome Javascript console. Happily, the console does a nice job of guiding me through this. If I pull myself from the list of players, then type "ava", Chrome lets me know that I probably want the avatar (raphaël's representation of the player in the game):



After accepting the auto-complete suggestion (via right-arrow), I pull the "body" of the player out of the frame. It is the first element in the first frame. I use one of the lineto paths printed to the console and attempt to animateAlong that path:



Hunh? What does that mean?
TypeError: Object #<an Object> has no method 'attr'
Gah! When there are no line numbers, there is an anonymous function. This is one of those occasions that I have to fall back to Firebug. Firebug—still the best.

In firebug, I find that this error traces back to line 113 of the player object in my (fab) game:



Dang it. I thought that I had defined an attr function on my SVG frames object. Clearly I am not:
  avatar.onAnimation(function(){
self.label.attr({x: avatar.attr("cx"), y: avatar.attr("cy") + Player.shadow_distance});
// ...
I solve that via simple delegation, the Javascript way:
    attr: function() {
// delegate to last object in first frame
var obj = this.list[0][this.list[0].length-1];
return obj.attr.apply(obj, arguments);
}
I grab the last element in the frame (the one that will be drawn on top) and apply its attr method. I ensure that the this variable inside that method will refer to that object by passing it in as the first argument to apply. Lastly, I send the arguments to my SVG frames object along to the individual object in the frame.

I could have called attr() directly if I was sure that it would always be called with one or no arguments:
    attr: function() {
// delegate to last object in first frame
var obj = this.list[0][this.list[0].length-1];
return obj.attr(arguments[0]);
}
I could done that, but why not future-proof if it is that easy?

With my apply based attr(), I can animate the individual pieces of the frames, but my player is still blowing up when I try to move it in my (fab) game. I will pick back up trying to solve that problem tomorrow.


Day #175

No comments:

Post a Comment