Category Archives: canvas

CraftJS; interesting potential

The other month/week I resumed some dabbling/hacking experiments in working with HTML5’s canvas. This go around, I decided to give CraftyJS a try.

The short version of my experiments, CraftyJS has a lot of potential if it can get it’s memory leaks under control, which might be a daunting task.

Here’s the example Pong game implementation that can be found on CraftyJS’s website:


Crafty.init(600,300);
Crafty.background("rgb(127,127,127)");

//Paddles
Crafty.e("Paddle, 2D, DOM, Color, Multiway")
    .color('rgb(255,0,0)')
    .attr({ x: 20, y: 100, w: 10, h: 100 })
    .multiway(4, { W: -90, S: 90 });

Crafty.e("Paddle, 2D, DOM, Color, Multiway")
    .color('rgb(0,255,0)')
    .attr({ x: 580, y: 100, w: 10, h: 100 })
    .multiway(4, { UP_ARROW: -90, DOWN_ARROW: 90 });

//Ball
Crafty.e("2D, DOM, Color, Collision")
    .color('rgb(0,0,255)')
    .attr({ x: 300, y: 150, w: 10, h: 10,
            dX: Crafty.math.randomInt(2, 5),
            dY: Crafty.math.randomInt(2, 5) })
    .bind('EnterFrame', function () {
        //hit floor or roof
        if (this.y <= 0 || this.y >= 290)
            this.dY *= -1;

        if (this.x > 600) {
            this.x = 300;
            Crafty("LeftPoints").each(function () {
                this.text(++this.points + " Points") });
        }
        if (this.x < 10) {
            this.x = 300;
            Crafty("RightPoints").each(function () {
                this.text(++this.points + " Points") });
        }

        this.x += this.dX;
        this.y += this.dY;
    })
    .onHit('Paddle', function () {
    this.dX *= -1;
})

//Score boards
Crafty.e("LeftPoints, DOM, 2D, Text")
    .attr({ x: 20, y: 20, w: 100, h: 20, points: 0 })
    .text("0 Points");
Crafty.e("RightPoints, DOM, 2D, Text")
    .attr({ x: 515, y: 20, w: 100, h: 20, points: 0 })
    .text("0 Points");

Now I will break this down.

Crafty.init(600,300);
Crafty.background("rgb(127,127,127)");

Relatively straight forward; this generates a HTML5 canvas element of 600x300 pixels, sets the color to a neutral color range.

Crafty.e("Paddle, 2D, DOM, Color, Multiway")
    .color('rgb(255,0,0)')
    .attr({ x: 20, y: 100, w: 10, h: 100 })
    .multiway(4, { W: -90, S: 90 });

Part of Crafty's cleverness is in it's component based entity system. The first line tells the Crafty library that you're defining a new entity called "Paddle" which is a 2D, DOM behavior like, color'd objected that reacts to multiple user inputs. Break down:

.color(...) defines the paddles color
.attr(...) defines the entities dimensions AND position.
.multiway(...) is a clear API method which states
the entities movement speed, and then which direction a triggered keyboard input will send the entitiy. So in the above case W is north, and S is south.

Skipping the other paddle, we come to the ball in a pong game.

Crafty.e("2D, DOM, Color, Collision")
    .color('rgb(0,0,255)')
    .attr({ x: 300, y: 150, w: 10, h: 10,
            dX: Crafty.math.randomInt(2, 5),
            dY: Crafty.math.randomInt(2, 5) })
    .bind('EnterFrame', function () {
        //hit floor or roof
        if (this.y <= 0 || this.y >= 290)
            this.dY *= -1;

        if (this.x > 600) {
            this.x = 300;
            Crafty("LeftPoints").each(function () {
                this.text(++this.points + " Points") });
        }
        if (this.x < 10) {
            this.x = 300;
            Crafty("RightPoints").each(function () {
                this.text(++this.points + " Points") });
        }

        this.x += this.dX;
        this.y += this.dY;
    })
    .onHit('Paddle', function () {
    this.dX *= -1;
})

Something I glossed over, Crafty's e(...) function is actually a Javascript prototype class generator. It's important to note that because of the interplay between .attr() and every other post e(...) method call.

In the above

.attr({ x: 300, y: 150, w: 10, h: 10,
            dX: Crafty.math.randomInt(2, 5),
            dY: Crafty.math.randomInt(2, 5) })

defines additional properties of the entity. x/y & w/h are already used by the 2D and DOM components to determine the entities position on but in the later .bind("EnterFrame") notice that the logic involved is referencing this attributes.

So inside the EnterFrame handler for the Ball are two crucial sections.

if (this.x > 600) {
            this.x = 300;
            Crafty("LeftPoints").each(function () {
                this.text(++this.points + " Points") });
        }
        if (this.x < 10) {
            this.x = 300;
            Crafty("RightPoints").each(function () {
                this.text(++this.points + " Points") });
        }

These two if clauses handle bounds check's and then depending on position award the appropriate side a point.

Notice the Crafty("") calls.  In this case Crafty("LeftPoints") calls up the appropriate entity or entities ( in this case there are only 1 per side ) and applies a closure to increment the winning side's this.points and body text.


The other crucial section is the 2 line closure 

.onHit('Paddle', function () {
    this.dX *= -1;
}

A fairly cool concept in Crafty is that collision detection between almost any entity is dealt with by Crafty, so to determine if the Ball hits a paddle, you just have to ask Crafty.

Summary

I was fairly impressed with CraftyJS, it is very much the spiritual relative of jQuery for a dirt simple and comfortably clever API. Unfortunately Crafty can blind side you with it's cleverness if you're not careful. What I mean is that in experimenting with CraftyJS I found it fairly easy to create non-obvious memory leaks and or abuse closures to much.

Still it's a fairly nice library and though this post is slightly long, I am only covering a thin scratch of the depth that is in CraftJS. This could be a fantastic building stage for making a slew of quick and dirty games.

HTML5 Canvas windowing proof of concept/prototype

Code is here ( https://gist.github.com/a17216d5b1db068ab41b )

Taking a break from a client project today and decided to try something real quick out. For one of my unpublished pet projects ( a near real time web based MUD ) I wanted a little map window to give visual feedback to the user on where exactly they were. Fortunately the Canvas API makes this stupid easy.

       var oX = this.player.x * (this.mMX / this.rMX);
    var oY = this.player.y * (this.mMX / this.rMY);
    
    var halfX = ( this.wMX / 2 );
    var halfY = ( this.wMY / 2 );
    
    var startX = Math.max(0, oX - halfX);
    var startY = Math.max(0, oY - halfY);
    
    var endX = Math.max(startX + this.wMX);
    var endY = Math.max(startY + this.wMY);
    
    var buffer = this.ctx.getImageData(startX, startY, endX, endY);
    this.window.putImageData(buffer, 0, 0);

First block takes the player or avatars position then scales up their grid position to the map canvas position.

Second block gets middle point of the window canvas, in this case if the window is 150×150, the origin is (75,75)

Now given the player’s position in the canvas map it finds the upper left and lower right corners of the map that is going to be copied from the map canvas into the window canvas. The Math.Max’s are there to prevent the upper left corner from being an impossible point (123, -50 ).

The end*’s use of Math.Max is actually crap on my part and isn’t needed.

Canvas optimization trick – avoid put/getImage calls unless necessary

Thanks to the power of google and some very explicit search terms, I stumbled upon this ( http://www.onaluf.org/en/entry/13 ) valuable tip.

Inside Ping, my implementation of putPixel uses either a fillRect or arc path because I’d already found making direct writes to the canvas pixel array terrifyingly expensive.

Next up for Ping is to use multiple canvas’s to store static images ( examples background layout ) and see how that works out.

Ping documentation in progress

I’ve been writing class/method level documentation and ideally plan to make a step by step explanation of what the heck is going on inside the Ping toolset. Until then it’s pretty doubtful anyone will want to use it. Never mind everything is still in Alpha state and there is very little guarantees that something won’t be written.

Snake game prototype

Running live here

User input is fed off the left & right cursor keys.

Ping additions included a formalized Quad tree library into the ping.Lib namespace but I want to fix up the factory function I wrote to take an instance of the Canvas rendering class instead of manually writing out the root dimensions. Otherwise collision checks are still a tad wonky… there’s a high propensity for near misses unfortunately 🙁

Ping, a canvas toolset, is nearing Alpha Alpha stage

So I’ve got a working Quadtree implementation that is relatively performent on Google Chrome 10

Demo here

GitHub repo here

So far bounding box detection isn’t but working on that, also documentation, and unit-tests are non-existent but I’m working on that now.

Once I hash those out, then this should be an useful collision detection/proximity check library for canvas tag games.

Canvas pixel collision detection

So… a fair number of people arrive here looking for how to detect when a pixel/object/thing has collided with something else.

I’ll add more information in the future but in the interim, the quick and dirty advice I can provide:

Given p = x, y where this is the point your trying to determine if it intersects another… the best place is to start with bounding box logic.  Regardless of what shape your object is, if wrap it in an invisible box boundry, it’s relatively trivial to do:

box upper left origin is bX, bY off size sizeX, sizeY.   If p.x greater then bX and p.x less then bX + sizeX AND p.y greater then bY and p.y less then bY + sizeY   you’re inside the bounding box and it’s highly likely your point has or will soon be colliding with an object.

That’s pretty much it and is perfect if you don’t mine lagging the crap out of whatever is running this logic.    To make these collision checks performant, you need to make the computer do less work.   For two dimensional space, my personnel research has led me to the Quad tree structure ( wikipedia article ).  I’ve got a brute force / naive implementation prototyped  @ here & here but as of 2011 March 9th it is not finished and needs some more work.

Basically Quad tree’s provides a super quick way for you to determine if there is any other objects in relative space to an entity.  Doing a quick lookup, if there’s nothing nearby then you can skip the bounding box checks and then the stupidly expensive perimeter intersection checks.

Full library is here and like most of my pet projects is heavily undocumented and completely absent of unit-testing.

Stellar simulator thingy

Inspired by this
IF YOU CAN SEE THIS, YOUR BROWSER SUCKS!





Source code @ Github here

So… this basically covers elliptical paths ( which I want to play with more ), a rendering chain I’m happy with, and a few others things. What isn’t exactly right: speed, planetary/stellar surface animation is missing, and I’d like to have a fly by mechanism that shifts the Y ratio progressively over time.

Return to canvas tag

Currently in between clients and waiting for the silent alpha of my delicious clone to wrap up….so its back to experiments with the canvas tag.

That means back to my unframework canvas library Ping. Some of the code still makes sense, but other bits are… well special. Currently there is an .ex object thats appended to the CanvasRenderingContext2D internal class and it works, but what I’m thinking of is re-working the code so that they’re added post-instantiation like a factory.


canvasMaker = function(elemId, options){
    var element = document.getElementById(elemId);
    var context = element.getContext("2d");
    //Here is where all the extension methods would be dynamically assigned to the 2d canvas instance
    wrappedContext = wrapContext(context);
    //Maybe add a singleton check around the bulk of this that keys on the elemId
    return wrappedContext;
}

The problem I’m trying to work around specifically is the need should ever arise to have n+1 managed tags on the same document. Currently there is so many hardwired references that it would be impossible to have the two cooperate without collisions and undesirable behavior.