Collision Detection in HTML5 2D Games


I wanted to make the Simple HTML5 Game I had created a couple of weeks back a little more interesting. I decided that I would add some obstacles in the way of the moving object. The goal then would be to prevent the moving object from hitting the obstacles and boundaries of the Canvas. This required implementation of collision detection (between moving object and obstacles) logic. If shape of the image is rectangular, then it is easy to detect collision; this is something I had already implemented in the last game, where I checked if the moving object hits any of the four boundaries. But if shape of the image is irregular, then it requires a bit more work to detect collision. But first, try out the modified game below and see how collision detection works –

Acknowledgments: I used images from following URLs for this game – space, asteroid, planet .

I decided to use KineticJS for the modified game. The old game did not use any library for drawing and animation. But using KineticJS is much more convenient for drawing and animation on the Canvas. Images asteroid and planet are not of rectangular shape. So I made area surrounding main images transparent.

Above images are zoomed in a bit to show transparent checkered area clearly. Checking if the moving object touches rectangular image boundary of any stationary image is not enough for hit test to be successful. It has to touch non-transparent area of the image. One option to check this is to compare pixels of each images at the same position on the Canvas and get color attributes of each image there – if pixels of each image have non-transparent color at any location, then objects in the image are touching (hit test positive). But this method is very slow when you need to perform hit test every time after changing position of the moving object.

So I decided to optimize this. I identified a few points on the outer boundary of the moving object and stored their X, Y co-ordinates with respect to the top left corner of the image .I stored this information in the following variable –

movingOInfo = {
    positionPoint : {x:0, y:0},
    boundryPoints : [{x:0,y:18},{x:17,y:0},{x:35,y:17},{x:18,y:35},{x:5,y:5},{x:30,y:5}
               ,{x:30,y:30},{x:5,y:30},{x:2,y:10},{x:8,y:3},{x:26,y:2},{x:33,y:8}
            ,{x:33,y:28},{x:27,y:33},{x:8,y:33},{x:2,y:26}
                ]
},

In the hit test method, I only check if any of the boundryPoints fall within the stationary objects/images. To check if the corresponding point on a stationary object is transparent, you need color information of that pixel in the image. I got image data of stationary object/image by first drawing that image to an invisible Canvas and then calling getImageData method. This is done in the getColorAt method –

function getColorAt (x,y,kimg)
{
    var width = obstImage.width,
    height = obstImage.height;

    if (obstacleImageData == null)
    {
        var dummy = $("#dummtCanvas")[0];
        $(dummy).width = width;
        $(dummy).height = height;
        var ctx = dummy.getContext("2d");
        ctx.drawImage(kimg.getImage(),0,0);
        obstacleImageData = ctx.getImageData(0,0,width,height);
    }

    x -= kimg.getX();
    y -= (kimg.getY()+ 1);

    var index = (width * 4 * y ) + (x * 4),
        data = obstacleImageData.data;

    if (index < 0 || index >= data.length)
        return null;

    return {index:index, r:data[index++],g:data[index++],b:data[index++],a:data[index]};
}

ctx.getImageData returns image information in a one dimensional array. Also for each pixel, it returns four values – Red, Green, Blue and Alpha values. My hit test method first checks if any of the points we have identified on the moving object fall in the rectangle of any stationary images (by calling intersects method of Shape object in KineticJS). If a point falls in the rectangle, then the next step is to check if the point at that location is transparent in the stationary image .I do that in confirmHitTest method.

function hitTest()
{
    var imagePoints = new Array();
    for (var i = 0; i < movingOInfo.boundryPoints.length; i++)
    {
        var x = currXPos + movingOInfo.boundryPoints[i].x;
        var y = currYPos + movingOInfo.boundryPoints[i].y;

        if (x <= 0 || x >= width || y <= 0 || y >= height)
            return false;

        for (var j = 0; j < obstacles.length; j++)
        {
            if (obstacles[j].intersects({x:x,y:y}))
            {
                if (!confirmHitTest(x,y,obstacles[j]))
                    return false;
            }
        }
    }

    return true;
}

function confirmHitTest (x,y, kimg)
{
    var color = getColorAt(x,y,kimg);

    if (color == null || ( color.r   == 0 && color.g == 0 && color.b == 0 && color.a == 0))
        return true;

    return false;
}

The above method worked quite well for this simple game. The more point you identify on the boundary of moving object, the more accurate will be the hit test.

-Ram Kulkarni

 

3 Replies to “Collision Detection in HTML5 2D Games”

  1. im new to code and i was wondering what you did with the dummt canvas like what is the canvas and how was it made because i am using a html canvas and getting the canvas var by using document.getElementById(“id”); and if what im doing will work with this.

Leave a Reply to planktonfun123 Cancel reply