Reputation: 51
I'm currently working on a little platform game. But it's not going really well with the collision detection. I've looked at many tutorials on this but is can't seem to understand it. Here's my code:
var ctx = document.getElementById("canvas").getContext("2d");
var rightPressed =false;
var spacePressed =false;
var leftPressed=false;
var gravity = 1;
//Player object
var player = {
x:50,
y:370,
dy:0,
dx:0,
width:10,
height:10,
speed:10,
jumping:false
};
//Box object
var box ={
x:200,
y:350,
width:50,
height:50
};
document.addEventListener("keydown", keyDown);
document.addEventListener("keyup", keyUp);
function keyDown(e) {
if(e.keyCode ==39){
rightPressed=true;
}
if (e.keyCode ==32){
spacePressed=true;
}
if (e.keyCode ==37){
leftPressed =true;
}
}
function keyUp(e) {
if(e.keyCode ==39){
rightPressed=false;
}
if (e.keyCode ==32){
spacePressed=false;
}
if (e.keyCode ==37){
leftPressed =false;
}
}
//Draw the ball
function drawBall(){
ctx.beginPath();
ctx.arc(player.x,player.y,10,0,Math.PI*2);
ctx.fillStyle="red";
ctx.fill();
}
//Draw the box
function drawBox() {
ctx.fillRect(box.x,box.y,box.width,box.height);
}
//The main function that calls the other functions and handles the logic
function draw(){
ctx.clearRect(0,0,canvas.width,canvas.height);
drawBall();
drawBox();
if (rightPressed){
//Right speed of the ball
player.x+=5;
}
if(leftPressed){
//Left speed of the ball
player.x-=5;
}
if(spacePressed){
if(!player.jumping){
//The hight of the jump
player.dy=-player.speed * 2;
player.jumping = true;
}
}
player.dy+=gravity;
player.x+=player.dx;
player.y+=player.dy;
if(player.y >= 390){
player.y = 390;
player.jumping = false;
}
//Box collision logic
window.requestAnimationFrame(draw)
}
window.requestAnimationFrame(draw);
canvas {border:1px solid black;}
<canvas id="canvas" width="400px" height="400px"> </canvas>
Upvotes: 1
Views: 2118
Reputation: 3950
I think this is the behaviour you're looking for. The following code does all the magic:
(see the code snippet below for a working demo)
//CHECK COLLISION
var collisionObjects = [rect.box, rect.wall];
for (var i=0,count=collisionObjects.length; i<count; ++i) {
var obj = collisionObjects[i];
var playerLeft=x-player.width, playerRight=x+player.width, playerTop=y-player.height, playerBottom=y+player.height;
var objectLeft=obj.x, objectRight=obj.x+obj.width, objectTop=obj.y, objectBottom=obj.y+obj.height;
//check if player is either touching or within the object-bounds
if (playerRight>=objectLeft && playerLeft<=objectRight && playerBottom>=objectTop && playerTop<=objectBottom) {
if (player.y+player.height==objectTop || player.y-player.height==objectBottom) {y=player.y;} //player is already colliding with top or bottom side of object
else if (player.x+player.width==objectLeft || player.x-player.width==objectRight) {x=player.x;} //player is already colliding with left or right side of object
else if (playerRight>objectLeft && playerLeft<objectRight && playerBottom>objectTop && playerTop<objectBottom) {
//check on which side the player collides with the object
var sides = {left:Math.abs(playerRight-objectLeft), right:Math.abs(playerLeft-objectRight), top:Math.abs(playerBottom-objectTop), bottom:Math.abs(playerTop-objectBottom)};
var side = Math.min(sides.left, sides.right, sides.top, sides.bottom); //returns the side with the smallest distance between player and object
if (side==sides.top) {y=objectTop-player.height;} else if (side==sides.left) {x=objectLeft-player.width;} //first check top, than left
else if (side==sides.bottom) {y=objectBottom+player.height;} else if (side==sides.right) {x=objectRight+player.width;} //first check bottom, than right
}
}
}
But in order for this to work, I had to change some other things too:
player.x
and player.y
, I had to create variables x
and y
at the start of draw, and only at the and update player.x=x, player.y=y
.draw()
should be copied I think to be save.But don't blindly copy it, I also made some other changes, mostly for my own understanding and readability of your script. Do with that what you want.. you like it, copy it, you don't, leave it:
player.speed
into player.speedx
and player.speedy
. These properties are also used in draw()
, so be careful with that.floor
and end
, to restrict the ball from leaving the canvas.gravity
value to the gravity value on earth.var box
to var rect
, an object containing all rectangular shapes. If you want to place a new rectangle, just add a new one in the rect
object and that's it.draw()
I changed drawBox();
to for (key in rect) {if (rect.hasOwnProperty(key)) {drawRect(rect[key]);}}
, a loop that draws all the objects that are inside the rect
object.var collisionObjects = [rect.box, rect.wall];
is an array that contains all the objects that should trigger a collision with the player.shelf
doesn't trigger a collision, because it is not in the array.shelf
to the collisionObjects-array, the ball still falls through after about half a second, I haven't figured out why. I thought it had to do with the width (height
in code), but that's not it. For some reason, if the object doesn't touch the floor, the ball will eventually fall through.)Code snippet:
var ctx = document.getElementById("canvas").getContext("2d");
//OBJECTS====================
var player = {x:50,y:100, dx:0,dy:0, width:10,height:10, speedx:5,speedy:10, jumping:false, color:"red"};
var rect = {
box: {x:150,y:350, width:50,height:50, color:"blue"},
shelf: {x:220,y:250, width:60,height:5, color:"gold"},
wall: {x:360,y:300, width:10,height:100, color:"green"}
};
//VARS====================
var rightPressed = false;
var spacePressed = false;
var leftPressed = false;
var gravity = 0.98;
var floor = canvas.height-player.height;
var end = canvas.width-player.width;
//KEY-HANDLERS====================
document.addEventListener("keydown",function(e) {
if (e.keyCode==39) {rightPressed=true;} //RIGHT
if (e.keyCode==37) {leftPressed=true;} //LEFT
if (e.keyCode==32) {spacePressed=true;} //JUMP
});
document.addEventListener("keyup",function(e) {
if (e.keyCode==39) {rightPressed=false;} //RIGHT
if (e.keyCode==37) {leftPressed=false;} //LEFT
if (e.keyCode==32) {spacePressed=false;} //JUMP
});
//DRAW====================
//OBJECTS--------------------
function drawPlayer() {
ctx.fillStyle = player.color;
ctx.beginPath();
ctx.arc(player.x,player.y,10,0,Math.PI*2);
ctx.fill();
}
function drawRect(obj) {
ctx.fillStyle = obj.color;
ctx.fillRect(obj.x,obj.y,obj.width,obj.height);
}
//SCENE--------------------
function draw() {
ctx.clearRect(0,0,canvas.width,canvas.height);
drawPlayer(); //draw player
for (key in rect) {if (rect.hasOwnProperty(key)) {drawRect(rect[key]);}} //draw all objects in 'rect'
//MOVE PLAYER
var x=player.x, y=player.y;
if (rightPressed) {x = x+player.speedx;} //right
if (leftPressed) {x = x-player.speedx;} //left
if (spacePressed && !player.jumping) { //jump
player.jumping = true;
player.dy = -player.speedy * 2; //jump-factor
}
x = x+player.dx;
if (x <= player.width) {x=player.width;}
if (x >= end) {x=end;}
player.dy += gravity;
y = y+player.dy;
if (y >= floor) {y=floor; player.jumping=false;}
//CHECK COLLISION
var collisionObjects = [rect.box, rect.wall];
for (var i=0,count=collisionObjects.length; i<count; ++i) {
var obj = collisionObjects[i];
var playerLeft=x-player.width, playerRight=x+player.width, playerTop=y-player.height, playerBottom=y+player.height;
var objectLeft=obj.x, objectRight=obj.x+obj.width, objectTop=obj.y, objectBottom=obj.y+obj.height;
//check if player is either touching or within the object-bounds
if (playerRight>=objectLeft && playerLeft<=objectRight && playerBottom>=objectTop && playerTop<=objectBottom) {
if (player.y+player.height==objectTop || player.y-player.height==objectBottom) {y=player.y;} //player is already colliding with top or bottom side of object
else if (player.x+player.width==objectLeft || player.x-player.width==objectRight) {x=player.x;} //player is already colliding with left or right side of object
else if (playerRight>objectLeft && playerLeft<objectRight && playerBottom>objectTop && playerTop<objectBottom) {
//check on which side the player collides with the object
var sides = {left:Math.abs(playerRight-objectLeft), right:Math.abs(playerLeft-objectRight), top:Math.abs(playerBottom-objectTop), bottom:Math.abs(playerTop-objectBottom)};
var side = Math.min(sides.left, sides.right, sides.top, sides.bottom); //returns the side with the smallest distance between player and object
if (side==sides.top) {y=objectTop-player.height;} else if (side==sides.left) {x=objectLeft-player.width;} //first check top, than left
else if (side==sides.bottom) {y=objectBottom+player.height;} else if (side==sides.right) {x=objectRight+player.width;} //first check bottom, than right
player.jumping=false;
}
}
}
//SET PLAYER POSITION
player.x=x, player.y=y;
//NEXT FRAME--------------------
requestAnimationFrame(draw)
} draw();
canvas {border:1px solid black;}
<canvas id="canvas" width="400px" height="400px"></canvas>
Upvotes: 1
Reputation: 699
So I know this isn't going to be helping you fix your code, but I would highly recommend you check out http://wickeditor.com/ if you look at the demo on the front page (click to like the 4th one) you will see it offers great collision detection, you can download the project from the site and it should make it super easy to replicate to fit your needs.
Upvotes: 0