bounce check function for HTML5 canvas fails at corners

Go To StackoverFlow.com

0

I have a HTML5 canvas that generates a bouncing box every time you click on it. The box array stores the x-value, y-value, x-velocity, and y-velocity of each box created. The box will travel in a random direction at first and will bounce of the sides of the canvas but if it hits a corner the box dissappears instead of bouncing back. EDIT: I answered my own question noticing that the soundY and soundX functions were causing the problem.

var box = new Array();
var width  = window.innerWidth;
var height = window.innerHeight;
var field = document.getElementById('canvas');

field.width  = width;
field.height = height; 
field.ctx = field.getContext('2d');
field.ctx.strokeStyle = 'rgba(255,255,255,1)'; 
setInterval('redraw()', 200);
addEventListener('click', createBox, false);

function createBox(e) { // this box will always fail collision detection at the upper-left corner
  box.push(100); // x-value is normally mouse position
  box.push(100); // y-value is normally mouse position
  box.push(-5); // x-speed is normally random
  box.push(-5); // y-speed is normally random
}

function redraw() {
  field.ctx.clearRect(0,0,width,height); 

  for(var i = 0; i < box.length; i+=4) {
         if(box[i] < 0)        { box[i+2] *= -1; soundY(box[i+1]); } // parameter of soundY is less than 0 
    else if(box[i] > width)    { box[i+2] *= -1; soundY(box[i+1]); } // which is invalid and causes this to break

         if(box[i+1] < 0)      { box[i+3] *= -1; soundX(box[i]); }
    else if(box[i+1] > height) { box[i+3] *= -1; soundX(box[i]); }

    box[i] += box[i+2];
    box[i+1] += box[i+3];
    field.ctx.strokeRect(box[i], box[i+1], 4, 4);
  }
}

function soundX(num) {
  // play a sound file based on a number between 0 and width
}

function soundY(num) {
  // play a sound file based on a number between 0 and height
}
2012-04-04 20:38
by kanoko
Do you have corresponding HTML to go with this - joeschmidt45 2012-04-04 20:42


0

It was additional code (see edit) that I left out assuming it was unrelated to the issue, but removing the code solved the problem as it appears this use-case would cause an invalid input in this part of the code.

2012-04-05 18:56
by kanoko


1

The only way I could recreate the problem was by generating the box in one of the corners so that with the right x and y velocity the box was initially created outside the bounds of the canvas. When that happens, the inversion of the velocity isn't enough to bring the item back in bounds and so on the next frame the velocity is inverted again (and so on).

I think this might solve your problem:

        var boxes = [];
        var boxSize = 4;
        var width = window.innerWidth;
        var height = window.innerHeight;
        var field = document.getElementById('canvas');

        function redraw() {
            field.ctx.clearRect(0, 0, width, height);
            var box;
            for (var i = 0; i < boxes.length; i++) {

                box = boxes[i];
                field.ctx.strokeRect(box.x, box.y, boxSize, boxSize);

                if (box.x < 0) {
                    box.x = 0;
                    box.dx *= -1;
                } else if (box.x > width - boxSize) {
                    box.x = width - boxSize;
                    box.dx *= -1;
                }

                if (box.y < 0) {
                    box.y = 0;
                    box.dy *= -1;
                } else if (box.y > height - boxSize) {
                    box.y = height - boxSize;
                    box.dy *= -1;
                }

                box.x += box.dx;
                box.y += box.dy;
            }
        }

        field.width = width;
        field.height = height;
        field.ctx = field.getContext('2d');
        field.ctx.strokeStyle = 'rgba(0,0,0,1)';

        setInterval(redraw, 200);
        addEventListener('click', createBox, false);

        function createBox(e) {
            boxes.push({
                x: e.clientX - 10,
                y: e.clientY - 10, // arbitrary offset to place the new box under the mouse
                dx: Math.floor(Math.random() * 8 - boxSize),
                dy: Math.floor(Math.random() * 8 - boxSize)
            });
        }

I fixed a few errors in your code and made some changes to make it a bit more readable (I hope). Most importantly, I extended your collision detection so that it resets the coordinates of the box to the bounds of your canvas should the velocity take it outside.

Created a jsfiddle which might be handy if further discussion is needed.

2012-04-05 00:10
by net.uk.sweet
i agree that resetting a coordinate after a collision would help. but i don't see why it is needed. Since the speed is constant, if a coordinate passes a threshold and then is backed up one step and reversed, why would this fail only when both coordinates are affected - kanoko 2012-04-05 14:38
I changed the first (function) parameter of your setInterval call because referring to it by string was causing an error in jsfiddle, but otherwise this is your test code link and it appears to me to be working (like I said, I couldn't recreate the issue like this). Although not related to this issue, I think you should look at the way you're storing the data for your boxes on the array. Using an object to represent each box is much more readable and maintainable - net.uk.sweet 2012-04-05 15:58
I found out it was actually other code that was causing the problem (see above). I was playing a sound based on the y-value when x was out of bounds and vice versa. I guess if I am accessing an invalid value for x or y, the bounds checking and updating code could break. Your suggestions were helpful though - kanoko 2012-04-05 18:43
Ads