简体   繁体   中英

2D Collision Detection With Gravity - JavaScript

I am trying to make a game using the HTML5 canvas for fun but I am having trouble detecting collisions between two sprites because of gravity.

I have an tick() method in my Player class where, on each tick, increments the player's x and y coordinate by its velocityX and velocityY respectively. Then, I add gravity to velocityY. I then want to check for collisions between the player and the blocks on screen which are at lined up on the bottom of the screen and stored in an array.

This is the code:

Player.prototype.tick = function() {

    this.x += this.velocityX;
    this.y += this.velocityY;
    this.velocityY += GRAVITY;

    var blocks = world.getOnScreenBlocks();
    for (var i=0; i<blocks.length; i++) {
            //Check for collision of this object (Player) with blocks[i]
        }
    }
}

I have spent a few hours trying to get collision detection to work but gave up. I know how to compare the player's coordinates with each block's coordinates to detect collisions but the problem is adjusting in the case of a collision.

If I detect a collision at the top of the block I can set velocityY to 0 causing the player to stop moving vertically. However, in the next tick, velocityY will increase due to GRAVITY being added onto it causing a collision being detected again and the player to slowly pass through the block after many ticks.

If I set velocityY to 0 on collision AND fix the player's position to be just above the block, it will somewhat work except there will be constant collisions detected while standing on the block due to gravity trying to push it down.

This is the closest that I've gotten to getting collision detection on all four sides of a block:

var blocks = world.getOnScreenBlocks();
for (var i=0; i<blocks.length; i++) {
    var block = blocks[i];
    var left1 = this.x;
    var left2 = block.getX();
    var right1 = this.x + this.sprite.getWidth();
    var right2 = block.getX() + block.getSprite().getWidth();
    var top1 = this.y - this.sprite.getHeight();
    var top2 = block.getY() - block.getSprite().getHeight();
    var bottom1 = this.y;
    var bottom2 = block.getY();

    //Check for collision at top
    if (this.velocityY > 0) {
        if (left1 < right2 && right1 > left2 && bottom1 >= top2 && bottom1 <= bottom2) {
            this.y = top2;
            this.velocityY = 0;
        }
    }

    //Check for collision at bottom
    else if (this.velocityY < 0) {
        if (left1 < right2 && right1 > left2 && top1 <= bottom2 && top1 >= top2) {
            this.y = bottom2 + this.sprite.getHeight();
            this.velocityY = 0;
        }
    }

    //Check for collision on right
    if (this.velocityX > 0) {
        if (top1 < bottom2 && bottom1 > top2 && right1 >= left2 && right1 <= right2) {
           this.x = left2 - this.sprite.getWidth();
            this.velocityX = 0;
        }
    }

    //Check or collision on left
    else if (this.velocityX < 0) {
       if (top1 < bottom2 && bottom1 > top2 && left1 <= right2 && left1 >= left2) {
            this.x = right2;
            this.velocityX = 0;
        }
    }
}

This somewhat works but is really glitchy. I really want to avoid setting the x and y coordinates the sprite when a collision occurs and just set the velocities because I think that would be less glitchy.

I've also tried setting booleans for collision so to not add gravity if a collision was detected but nothing works. Is there a standard, proper way of doing this? Please help.

Also, to make it more efficient, I would really like it to only go through the loop to check for collisions if the player is moving but i'm not sure how because the velocityY will always be above zero due to gravity.

Thanks.

EDIT: I should include that in my second example above, the player and blocks x and y coordinates are based on the bottom-left corner and not the typical top-left corner. I did this due to my sprites being different heights and I wanted to get collision to work at the bottom where the sprites feet collide with the ground.

Do you though about a isGrouned var who will know when apply the gravity on your player, with it you can apply gravity only when isGrounded == false .

You are in a vector environment, you can make a kind of speculative collision ( here ), it can be difficult to understand, but you just have to know the n+1 position of your player and chekc if he will be in collision.

If you can read this french article about collision, it can be usefull in many case.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM