Search code examples
javascripthtmlcanvascollision-detectionpixel-perfect

Trouble with perfect-pixel collisions with canvas in html5


I think I'm close on this one, but am having some difficulty figuring out specifically what I'm missing. I'm trying to do a "pixel-perfect" collision detection in Canvas. The code is available in Github https://github.com/AlexChesser/jsSprite and http://chesser.ca/jsSprite/13-more-pixel-detection.php

Assuming I'm getting as far as determining that the trivial collisions are happening, the next phase is to check pixel level on the overlapping area.

So far my code for this test is:

        // Ok, compute the rectangle of overlap:            
        if (bottom1 > bottom2)  {   over_bottom = bottom2; 
        } else {                    over_bottom = bottom1; }

        if (top1 < top2)        {   over_top = top2; 
        } else {                    over_top = top1; }

        if (right1 > right2)    {   over_right = right2; 
        } else {                    over_right = right1; }

        if (left1 < left2)      {   over_left = left2; 
        } else {                    over_left = left1; }

        if (Sprite.debug == 1) {
            // again for the purposes of global debuggery I thought it'd be nice to be able to 
            // draw the area of overlap, but without ruining all other tests
            MainContext.fillStyle = "rgb(200,0,0)";
            MainContext.fillRect (over_left, over_top, over_right-over_left, over_bottom-over_top);
        }
        overlap_width = over_right-over_left;
        overlap_height = over_bottom-over_top;

        if (Sprite.Xpos > obj.Xpos) {
            // read the data start at the sprite's right side
            var sprite_data_x_start = Sprite.Xpos   +Sprite.width   -overlap_width;
            var obj_data_x_start    = obj.Xpos;
        } else {
            // read the data from the sprites left.
            var sprite_data_x_start = Sprite.Xpos;
            var obj_data_x_start    = obj.Xpos  +obj.width          -overlap_width;             
        }

        if (Sprite.Ypos > obj.Ypos) {
            // read the data start at the sprite's right side
            var sprite_data_y_start = Sprite.Ypos   +Sprite.height  -overlap_height;
            var obj_data_y_start    = obj.Ypos;
        } else {
            // read the data from the sprites left.
            var sprite_data_y_start = Sprite.Ypos;
            var obj_data_y_start    = obj.Ypos  +obj.height         -overlap_height;                
        }


        // 1. get the coordinates of the overlapped area for box object1
        // 2. get the coordinates of the overlapped area for this Sprite
        // 3. get the data for each into an array using GETIMAGEDATA
        //      eg:
        var sprite_overlap_image = Sprite.ctx.getImageData( sprite_data_x_start, 
                                                            sprite_data_y_start, 
                                                            overlap_width, 
                                                            overlap_height);
        var obj_overlap_image = obj.ctx.getImageData(       obj_data_x_start, 
                                                            obj_data_y_start, 
                                                            overlap_width, 
                                                            overlap_height);


        //soid  = sprite_overlap_image.data;
        soid = sprite_overlap_image;
        GlobalWatchSOID = sprite_overlap_image;
        GlobalWatchOOID = obj_overlap_image;            
        ooid    = obj_overlap_image;

        //MainContext.putImageData(sprite_overlap_image.data, 100, 100);
        //MainContext.putImageData(obj_overlap_image.data,  400, 400);

        for (idx in soid.data) {
            if (!isNaN(idx)) {
                if ((soid.data[idx] == 0 && ooid.data[idx] == 0)) {
                    return true;
                    //console.log(idx + ' ' + soid.data[idx] + ' omg hit! ' + ooid.data[idx]);
                }
            }
        }
        return false;           

Through inspecting the DOM in the Chrome Debugger, I know that I'm definitely able to loop through each pixel in the image, what I'm not able to figure out is why my comparison of the pixel in array1 at index and the pixel in array2 never seem to overlap.

Can anyone see the spot where I'm going wrong? Maybe there's a logical error in here somewhere, perhaps I'm not reading in the squares that I think I'm getting or something.

I'll keep hammering away and update if I find but more often than not, Stack's a lot smarter than me :)


Solution

  • I've cracked it.

            if (Sprite.Xpos > obj.Xpos) {
                // read the data start at the sprite's right side
                //var sprite_data_x_start = Sprite.Xpos -Sprite.width   -overlap_width;
                var sprite_data_x_start = Sprite.width  -overlap_width;             
                var obj_data_x_start    = 0;
            } else {
                // read the data from the sprites left.
                var sprite_data_x_start = Sprite.Xpos;
                var obj_data_x_start    = 0         -overlap_width;             
            }
    
            if (Sprite.Ypos > obj.Ypos) {
                // read the data start at the sprite's right side
                var sprite_data_y_start = Sprite.height -overlap_height;
                var obj_data_y_start    = 0;
            } else {
                // read the data from the sprites left.
                var sprite_data_y_start = 0;
                var obj_data_y_start    = obj.height            -overlap_height;                
            }
    

    I was pulling the wrong area of the individual canvas objects.