Saturday, May 1, 2010

Connecting Canvas Movements to Fab.js

‹prev | My Chain | next›

Tonight, I continue my exploration of <canvas>, this time with an eye toward hooking it up to fab.js. Last night, I was able to get a small dot representing my character to move about the <canvas> in response to key presses.

That will not work with the "game" my kids have in mind. They'd like a bunch of characters to meander about the board talking to each other. Moving dozens of characters about pixel-by-pixel is something that fab.js/node.js could probably handle, but dealing with network latency and hiccups is probably not worth the effort.

Instead, I will move the character around by having them walk after mouse click. The character will start in the middle of the canvas. A mouse click in the bottom right will result in a animation of my character moving from the middle of the board to the bottom right.

To accomplish this, I publish the clicks from the board to any subscribers interested in being notified. I have no expectation that anything else will be interested in mouse clicks, but pub-sub makes sense here not as a premature generalization, but rather to decouple the game board from my character. So, I establish my <canvas> listeners:
function init() {
var canvas = $("#canvas").get(0);
var ctx = canvas.getContext("2d");

var me = new Player();

canvas_events(canvas, [me]);
draw(ctx, me);
}
My only canvas event:
function canvas_events (canvas, listeners) {
$(canvas).click(function(evt) {
listeners.forEach(
function(listener) { listener.notify(evt) }
);
});
}
(as you may have noticed, I have started using jQuery for my events)

Great, so now I need a Player object that will encapsulate the position of my character and subscribe to the event publications. The instantiation of the class is straight-forward, defaulting to the middle of my 500x500 <canvas>:
var Player = function(options) {
if (!options) options = {};
this.x = options.x || 250;
this.y = options.y || 250;
}
Now, I establish my notify method:
Player.prototype.notify = function(evt) {
switch(evt.type) {
case "click":
this.walk_to(evt.offsetX, evt.offsetY);
break;
}
};
For now, I am only interested in click publications, which initiate a walk:
Player.prototype.walk_to = function(x, y) {
if (this.x < x) this.x = this.x + 1;
if (this.x > x) this.x = this.x - 1;
if (this.y < y) this.y = this.y + 1;
if (this.y > y) this.y = this.y - 1;

if (this.x != x || this.y != y) {
setTimeout(function(){self.walk_to(x,y)}, 25);
}
};
Easy enough!

Now, to tell fab.js what I am up to. I start with my skeleton app:
var puts = require( "sys" ).puts;

with ( require( ".." ) )

( fab )

( listen, 0xFAB )

( 404 );
For now, I am just going to print out the contents of posted location information. I will push that information to other clients later. The fab.js application to do this:
var puts = require( "sys" ).puts;

with ( require( ".." ) )

( fab )

( listen, 0xFAB )

( /move/ )

(
function () {
var out = this;
return function listener( obj ) {
if ( !obj ) out();
else if ( obj.body ) puts(obj.body);
return listener;
};
}
)


( 404 );
To tell my Player class to POST that information, I add a second action in notify():
Player.prototype.notify = function(evt) {
switch(evt.type) {
case "click":
this.walk_to(evt.offsetX, evt.offsetY);
this.notify_server({id:"me",x:evt.offsetX, y:evt.offsetY});
break;
}
};
The server_notify method make a simple jQuery POST:
Player.prototype.notify_server = function(change) {
$.post("http://localhost:4011/move", change);
};
Now, clicking on the board moves my character and tells fab.js:
cstrom@whitefall:~/repos/fab$ node ./play/game.js
id=me&x=322&y=184
id=me&x=364&y=352
id=me&x=199&y=198
id=me&x=350&y=401
Nice! That is a good stopping point for tonight. Tomorrow, I will work on pushing that information to other boards so that others can see my character move.

Day #90

No comments:

Post a Comment