0

I'm writing a simple game in javascript and I'm wondering what the best way to handle collisions between the player and the world objects.

<script>

var isJumping = false;
var isFalling = false; 
var w = 1;
var recwidth = 400;
var recheight = 400;
var xpos = 50;
var ypos = 279;
window.onload = function() {
    var FPS = 30;
    var ground = new myObject();
    setInterval(function() {
        clear();
        draw();
        ground.draw(0, 325);
        ground.draw(125,325)
    },     1000/FPS);
};

function myObject(){ 
    this.draw = function drawground(groundx, groundy){
        var canvas = document.getElementById('canvas')
        var  context = canvas.getContext('2d');
        //context.fillRect(xpos,ypos,100,100);
        var img=new Image()
        img.src="ground.png"
        img.onload = function() { 
        context.drawImage(img,groundx,groundy)}

    }

};




function jump()
{
    var t=.1;
    isJumping=true;

    var jumpint= setInterval(function() {
        yup = 12*t-(5*t*t);
        ypos= ypos - yup;
        t = t + .1
        if(yup < 0)
        {
            isJumping = false;
            isFalling = true;
            clearInterval(jumpint);
            jumpint = 0;
            fall();
            return;
        }

    }, 20);

}

function fall()
{
    t=.10
    var fallint= setInterval(function() {
        ydown = (5*t*t);
        ypos= ypos + ydown;
        t = t + .1
        if(ypos > 275)
        {
            isFalling == false;
            clearInterval(fallint);
            fallint = 0;
            return;
        }

    }, 20);

}



function changex(x){
    xpos = xpos + (x);
    //clear();
    //draw();
}
function changey(y){
    ypos = ypos + (y);
    //clear();
    //draw();
}



function draw(){
    var canvas = document.getElementById('canvas')
    var  context = canvas.getContext('2d');

    var img=new Image()
    img.src="character.png"
    img.onload = function() { 
        context.drawImage(img,xpos,ypos)}

}

function clear(){
    var canvas = document.getElementById('canvas')
    var  context = canvas.getContext('2d');
    context.clearRect(0,0, canvas.width, canvas.height);
}


document.onkeydown = function(event) {
    var keyCode; 

    if(event == null)
    {
        keyCode = window.event.keyCode; 
    }
    else 
    {
         keyCode = event.keyCode; 
    }

    switch(keyCode)
    {
    // left 
    case 37:
        //left
        changex(-5);
        break; 

    // up 
    case 38:
        // action when pressing up key
        jump();
        break; 

    // right 
    case 39:
        // action when pressing right key
        changex(5);
        break; 

    // down
    case 40:
        // action when pressing down key
        changey(5);
        break; 

    default: 
        break; 
    } 
}
</script>

So, as you can see I'm creating two objects so far, and the player stops falling at any arbitrary point. I feel collisions at this stage wont be too difficult, but once I start adding more I feel it's going to get more difficult. I'm not going to be using the instance of the object with the same image for each instance of the object, so at some point I'm going to change the myobject function to be able to accept the image as a parameter, and then checking for collisions will be a bit more tricky. I also plan on making this into a side scroller, so once one end the map is hit it changes into the next area, which is going to cause performance issues. If I'm checking for collisions on every single object in the entire game every interval I imagine things are going to get slow. What is going to be the best way to limit the number of collisions checked? Obviously, if the object isn't on screen there is no need to check it, but is there a way to limit that. I'm thinking of making an array for every frame of the game, and filling that array with it's objects. Then, only check the array the of the frame the player is currently in. Is this feasible or still going to cause too many issues? Any help is greatly appreciated.

Passerby
  • 9,715
  • 2
  • 33
  • 50
Peter Jewicz
  • 664
  • 1
  • 10
  • 27
  • A common solution: [quadtrees (2d)](http://en.wikipedia.org/wiki/Quadtree)/[octrees (3d)](http://en.wikipedia.org/wiki/Octree). – Matt Ball May 06 '13 at 04:20
  • Check this [SO thread](http://stackoverflow.com/a/16351581/2252829) and look at this [quadtree implementation](http://www.mikechambers.com/blog/2011/03/21/javascript-quadtree-implementation/). As @Matt said, it's a common (and effective) solution. – Gustavo Carvalho May 06 '13 at 09:32

1 Answers1

-1

If you want pixel perfect collisions, I have some plain javascript code that worked for me with canvas2d rendering context.

function collide(sprite, sprite2, minOpacity=1) {
    // Rectangular bounding box collision
    if (sprite.x < sprite2.x + sprite2.width && sprite.x + sprite.width > sprite2.x && sprite.y < sprite2.y + sprite2.height && sprite.y + sprite.height > sprite2.y) {
        // Finds the x and width of the overlapping area
        var overlapX = (this.rect.x > other.rect.x) ? [this.rect.x, (other.rect.x + other.rect.width) - this.rect.x + 1] : [other.rect.x, (this.rect.x + this.rect.width) - other.rect.x + 1];
        // Finds the y and height of the overlapping area
        var overlapY = (this.rect.y + this.rect.height > other.rect.y + other.rect.height) ? [this.rect.y, (other.rect.y + other.rect.height) - this.rect.y + 1] : [other.rect.y, (this.rect.y + this.rect.height) - other.rect.y + 1];
        // Creates a canvas to draw sprite.image to
        var spriteImageCanvas = new OffscreenCanvas(overlapX[0] + overlapX[1], overlapY[0] + overlapY[1]);
        var spriteImageCanvasContext = spriteImageCanvas.getContext("2d");
        // Draws sprite.image to spriteImageCanvasContext
        spriteImageCanvasContext.drawImage(this.image, sprite.x, sprite.y, sprite.width, sprite.height);
        // Creates a canvas to draw sprite2.image to
        var sprite2ImageCanvas = new OffscreenCanvas(overlapX[0] + overlapX[1], overlapY[0] + overlapY[1]);
        var sprite2ImageCanvasContext = otherImageCanvas.getContext("2d");
        // Draws sprite2.image to sprite2ImageCanvasContext
        sprite2ImageCanvasContext.drawImage(sprite2.image, sprite2.x, sprite2.y, sprite2.width, sprite2.height);
        // Loops through the x coordinates in the overlapping area
        for (var x = overlapX[0]; x <= overlapX[0] + overlapX[1]; x++) {
            // Loops through the y coordinates in the overlapping area
            for (var y = overlapY[0]; y <= overlapY[0] + overlapY[1]; y++) {
                if (/* Checks if the pixel at [x, y] in the sprite image has an opacity over minOpacity input */ thisImageCanvasContext.getImageData(x, y, 1, 1).data[3] >= minOpacity && /* Checks if the pixel at [x, y] in the sprite2 image has an opacity over minOpacity input */ otherImageCanvasContext.getImageData(x, y, 1, 1).data[3] >= minOpacity) {
                    return true;
                };
            };
        };
    };
}

Or if you just want rectangular collision, use the first if statement in the function.

HKH
  • 1
  • 1
  • 1
    This is insanely highly inefficient. To check every image's pixel data, inside a requestAnimationFrame loop, for N entities,.. - is a nonsensical performance killer. Pixel perfect is not needed in any game development. Just collision areas. Sometimes the best it to round it to a rectangle or circle, or to create a collision box for polygons (SAT) like i.e: matter.js does https://brm.io/matter-js/demo/#svg. – Roko C. Buljan Jan 22 '23 at 17:34