Collisions in a HTML5 Canvas - javascript

I'm fairly new to Javascript and using a canvas in general and I cant get collision detection to work with my canvas walls.
I normally have a small square painted on the canvas that I can control with the arrow keys on the keyboard but it would continue past the canvas.
This is the working code without my attempts of collision detection.
<!DOCTYPE html>
<html>
<head>
<canvas id = "gameCanvas" width="400" height="400" style = "border:1px solid #000000;"></canvas>
<style type="text/css"></style>
</head>
<body>
<script type="text/javascript">
var c = document.getElementById("gameCanvas");
var ctx = c.getContext("2d");
ctx.fillStyle = "rgb(255, 0, 0)";
var snake = {
x: 5
, y: 5
};
function drawSnake() {
ctx.clearRect(0,0, ctx.canvas.width, ctx.canvas.height);
ctx.fillRect(snake.x ,snake.y,20,20);
}
window.addEventListener("keydown", function(event) {
if (event.keyCode == 39)
snake.x += 5;
else if (event.keyCode == 37)
snake.x -= 5;
else if (event.keyCode == 38)
snake.y -= 5;
else if (event.keyCode == 40)
snake.y += 5;
drawSnake();
});
drawSnake();
</script>
</body>
</html>
This is my attempt at collision detection.
//Wall Collision
if(snake.x >= canvas.w || snake.x <= -1 || snake.y >= canvas.h || snake.y <= -1) {
return;
}
Entering this code leaves me with a blank canvas and I don't understand why.
I want the square to reset its position once it collides with the canvas wall and that's where I'm having all the trouble.

You shouldn't put it under addEventListener. Then, the return is ending the JS script.
You want to put your collision detection code in (the beginning of) drawSnake().
See this working example.
There are also a few other things wrong with your code that I feel that I should address (all changed in the example).
<canvas> should be in the <body> element, not the <head>
canvas is not defined in JS (you can remove it from ctx.canvas.width
change canvas.w and canvas.h to ctx.width and ctx.height in the collision detection code
instead of returning from the drawSnake() function, reset the snake to its default coordinates
draw a better snake

Related

Canvas disappearing on event listener

I am trying to create a rect that moves on keypress for a pong game and when I press the key the left rect disappears..
Any ideas how to fix the problem? It is really important..
The code is written in vanilla javascript so please don't write jQuery..
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Pong</title>
<script type="text/javascript">
var x = 100;
var y = 100;
var xmoveFirst = 720;
var ymoveFirst = 0;
var xmoveSecond = 30 ;
var ymoveSecond = 0;
function canvas() {
var can = document.getElementById('theCanvas');
can.style.backgroundColor = "black";
var ctx = can.getContext('2d');
//first player
ctx.fillStyle="white";
ctx.fillRect(xmoveFirst,ymoveFirst,5,75);
//second player
ctx.fillStyle = 'white';
ctx.fillRect(xmoveSecond,ymoveSecond,5,75);
//first player move
function moveFirst(eFirst) {
ctx.clearRect(0,0,750,750); //clear rects
if (eFirst.keyCode == 40) {
ymoveFirst+=25;
console.log("first,"+ymoveFirst);
}
else if (eFirst.keyCode == 38) {
ymoveFirst-=25;
console.log("first,"+ymoveFirst);
}
ctx.fillStyle="white";
ctx.fillRect(xmoveFirst,ymoveFirst,5,75);
ctx.fillRect(xmoveSecond,ymoveSecond,5,75);
}
var first = document.onkeydown = moveFirst;
//second player move
function moveSecond(eSecond) {
ctx.clearRect(0,0,750,750);
if (eSecond.keyCode == 83) {
ymoveSecond+=25;
console.log("second,"+ymoveSecond);
}
else if (eSecond.keyCode == 87) {
ymoveSecond-=25;
}
ctx.fillStyle="white";
ctx.fillRect(xmoveFirst,ymoveFirst,5,75);
ctx.fillRect(xmoveSecond,ymoveSecond,5,75);
console.log("second,"+ymoveSecond)
}
var second = document.onkeydown = moveSecond;
}
83,87
</script>
</head>
<body onload="canvas()">
<canvas id="theCanvas" width="750" height="750"></canvas>
</body>
</html>
This line: can.width=can.width; resets the canvas width.
When you change the canvas size (width or height), you clear the canvas (making everything black). After this line, you are only redrawing the rightmost paddle, so the left one remains missing.
You'll need to redraw the leftmost paddle as well if you want it to come back. Here is the modified moveRight() function:
function moveRight(eFirst) {
if (eFirst.keyCode==40) {
ymoveFirst+=25;
console.log(ymoveFirst);
}
else if (eFirst.keyCode==38) {
ymoveFirst-=25;
console.log(ymoveFirst);
}
can.width=can.width;
ctx.fillStyle="white";
// Redraw BOTH paddles
ctx.fillRect(xmoveFirst,ymoveFirst,5,75);
ctx.fillRect(xmoveSecond,ymoveSecond,5,75);
}
Also, replace
document.onkeydown = moveFirst; and document.onkeydown = moveSecond;
with
document.addEventListener("keydown", moveFirst);, and document.addEventListener("keydown", moveSecond);
Currently, you are overwritting the first listener with document.onkeydown = moveSecond;. By using addEventListener, you don't overwrite the ones that already exist.

How to add a rectangle here? [duplicate]

As the title suggests, I am having trouble with object collision...
I am currently working on a 2d Html5 canvas game using JavaScript. I know how to keep the "player" object from going outside the width/height of the game canvas, and i know how to do something when the player collides with an object (such as a power up or enemy or whatever) but i just don't know how to make a "solid" object meaning when the player hits the solid object, the player just stops, and cannot go through the solid object.
This is what I have now (not all the code just what I feel is relevant, sorry if it's too much/too little.:
var canvasPlayer = document.getElementById('canvasPlayer');
var ctxPlayer = canvasPlayer.getContext('2d');
var canvasWalls = document.getElementById('canvasWalls');
var ctxWalls = canvasWalls.getContext('2d');
function checkKeyDown(e) {
var keyID = (e.keyCode) || e.which;
if (keyID === 38 || keyID === 87) { // up arrow OR W key
if (!player1.isDownKey && !player1.isLeftKey && !player1.isRightKey) {
player1.isUpKey = true;
e.preventDefault();
} }
if (keyID === 39 || keyID === 68) { //right arrow OR D key
if (!player1.isDownKey && !player1.isLeftKey && !player1.isUpKey) {
player1.isRightKey = true;
e.preventDefault();
} }
if (keyID === 40 || keyID === 83) {//down arrow OR S key
if (!player1.isUpKey && !player1.isLeftKey && !player1.isRightKey) {
player1.isDownKey = true;
e.preventDefault();
} }
if (keyID === 37 || keyID === 65) {//left arrow OR A key
if (!player1.isDownKey && !player1.isUpKey && !player1.isRightKey) {
player1.isLeftKey = true;
e.preventDefault();
}
}
}
Walls.prototype.draw = function (){
ctxWalls.drawImage(imgSprite,this.srcX,this.srcY,this.width,this.height,this.drawX,this.drawY,this.width,this.height);
this.checkHitPlayer();
};
Walls.prototype.checkHitPlayer = function() {
if (this.drawX > player1.drawX &&
this.drawX <= player1.drawX + player1.width &&
this.drawY >= player1.drawY &&
this.drawY < player1.drawY + player1.height) {
player1.isUpKey = false;
player1.isDownKey = false;
player1.isRightKey = false;
player1.isLeftKey = false;
}
};
This works... except when trying to go up or left, the player only moves maybe 2-3 pixels, so it takes 3 left or up arrows to go left or up. As well the player can move straight through the wall which is not what i want. Any help is much appreciated sorry if i included too much or not enough code. Oh, i also forgot to mention the game is a puzzle game, and I have it set-up so a player can only move one direction at a time until hitting a wall.
If you just want your player to stop when the reach a wall, you can apply some math:
For example: assume your player is a 10px by 10px rectangle and the right wall's X position is 200.
The X position of the right side of the rectangle is calculated like this:
var playerRightSide = player.x + player.width;
You can test if the player has reached the wall like this:
if( playerRightSide >= 200 )
If the user tries to push their player beyond the wall, you would hold the player to the left of the wall using the players X position.
if( playerRightSide >= 200 ) { player.x = 190; }
The 190 is the wall's X position (200) minus the player's width (10).
Read further if you're interested in doing more advanced collision testing.
Many basic game collisions can be classified into 3 types:
Circle versus Circle collision
Rectangle versus Rectangle collision
Rectangle versus Circle collision
Here’s an illustration of how to detect each of these common collisions.
Assume you define a circle like this:
var circle1={
x:30,
y:30,
r:10
};
Assume you define a rectangle like this:
var rect1={
x:20,
y:100,
w:20,
h:20
};
You can detect Circle vs Circle collisions like this...
...Using this Circle vs Circle collision-test code:
// return true if the 2 circles are colliding
// c1 and c2 are circles as defined above
function CirclesColliding(c1,c2){
var dx=c2.x-c1.x;
var dy=c2.y-c1.y;
var rSum=c1.r+c2.r;
return(dx*dx+dy*dy<=rSum*rSum);
}
You can detect Rectangle vs Rectangle collisions like this...
...Using this Rectangle vs Rectangle collision-test code:
// return true if the 2 rectangles are colliding
// r1 and r2 are rectangles as defined above
function RectsColliding(r1,r2){
return !(r1.x>r2.x+r2.w || r1.x+r1.w<r2.x || r1.y>r2.y+r2.h || r1.y+r1.h<r2.y);
}
You can detect Rectangle vs Circle collisions like this...
...Using this Rectangle vs Circle collision-test code:
// return true if the rectangle and circle are colliding
// rect and circle are a rectangle and a circle as defined above
function RectCircleColliding(rect,circle){
var dx=Math.abs(circle.x-(rect.x+rect.w/2));
var dy=Math.abs(circle.y-(rect.y+rect.y/2));
if( dx > circle.r+rect.w2 ){ return(false); }
if( dy > circle.r+rect.h2 ){ return(false); }
if( dx <= rect.w ){ return(true); }
if( dy <= rect.h ){ return(true); }
var dx=dx-rect.w;
var dy=dy-rect.h
return(dx*dx+dy*dy<=circle.r*circle.r);
}
For example, you can use these collision tests to respond to a player touching a power-up cube:
// create a circular player object
// that's located at [30,30] and has a radius of 10px
var player={x:30,y:30,r:10};
// create a rectangular power-up at position [200,30]
var powerup={x:200, y:30, w:20, h:20};
// Let's say the user keys the player to coordinate [200,35]
// (touching the power-up)
player.x = 220;
player.y = 35;
// you can test if the circular player is touching the rectangular power-up
if( RectCircleColliding(powerup,player) ) {
// the player has collided with the power-up, give bonus power!
player.power += 100;
}
Here is code and a Fiddle: http://jsfiddle.net/m1erickson/u6t48/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; padding:20px; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
window.requestAnimFrame = (function(callback) {
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
ctx.fillStyle="lightgray";
ctx.strokeStyle="skyblue";
// top collision circle vs circle
var circle1={x:30,y:30,r:10};
var circle2={x:70,y:40,r:10};
var circle3={x:100,y:30,r:10};
var direction1=1;
// middle collision rect vs rect
var rect1={x:20,y:100,w:20,h:20};
var rect2={x:50,y:110,w:20,h:20};
var rect3={x:90,y:100,w:20,h:20};
var direction2=1;
// bottom collision rect vs circle
var circle4={x:30,y:200,r:10};
var rect4={x:50,y:205,w:20,h:20};
var circle5={x:100,y:200,r:10};
var direction3=1;
function drawAll(){
ctx.clearRect(0,0,canvas.width,canvas.height);
drawCircle(circle1);
drawCircle(circle2);
drawCircle(circle3);
drawCircle(circle4);
drawCircle(circle5);
drawRect(rect1);
drawRect(rect2);
drawRect(rect3);
drawRect(rect4);
}
function drawCircle(c){
ctx.beginPath();
ctx.arc(c.x,c.y,c.r,0,Math.PI*2,false);
ctx.closePath();
ctx.fill();
ctx.stroke();
}
function drawRect(r){
ctx.beginPath();
ctx.rect(r.x,r.y,r.w,r.h);
ctx.closePath();
ctx.fill();
ctx.stroke();
}
// return true if the 2 circles are colliding
function CirclesColliding(c1,c2){
var dx=c2.x-c1.x;
var dy=c2.y-c1.y;
var rSum=c1.r+c2.r;
return(dx*dx+dy*dy<=rSum*rSum);
}
// return true if the 2 rectangles are colliding
function RectsColliding(r1,r2){
return !(r1.x>r2.x+r2.w || r1.x+r1.w<r2.x || r1.y>r2.y+r2.h || r1.y+r1.h<r2.y);
}
// return true if the rectangle and circle are colliding
function RectCircleColliding(rect,circle){
var dx=Math.abs(circle.x-(rect.x+rect.w/2));
var dy=Math.abs(circle.y-(rect.y+rect.h/2));
if( dx > circle.r+rect.w/2 ){ return(false); }
if( dy > circle.r+rect.h/2 ){ return(false); }
if( dx <= rect.w ){ return(true); }
if( dy <= rect.h ){ return(true); }
var dx=dx-rect.w;
var dy=dy-rect.h
return(dx*dx+dy*dy<=circle.r*circle.r);
}
var fps = 15;
function animate() {
setTimeout(function() {
requestAnimFrame(animate);
// circle vs circle
circle2.x = circle2.x+direction1;
if( CirclesColliding(circle2,circle1) || CirclesColliding(circle2,circle3) ){
direction1=-direction1;
}
// rect vs rect
rect2.x = rect2.x+direction2;
if( RectsColliding(rect2,rect1) || RectsColliding(rect2,rect3) ){
direction2=-direction2;
}
// rect vs circle
rect4.x = rect4.x+direction3;
if( RectCircleColliding(rect4,circle4) || RectCircleColliding(rect4,circle5) ){
direction3=-direction3;
}
drawAll();
}, 1000 / fps);
}
animate();
}); // end $(function(){});
</script>
</head>
<body>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>

Game using HTML5 canvas

I'm trying to develop game similar to snake games in old model phones. I created the game, which you can have a look at here. But when controlling the snake I switched the width and height of the snake to turn in any direction, but I want to make the snake to look like real snake, I mean it should have an L shape when turning and gradually changes into straight again, Please give me any idea, how I can do that using canvas. the snake is a fillrect() in canvas.
The following is the entire code..
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Snake</title>
</head>
<body>
Use Arrow keys of to control the snake.
<canvas id="can_game" height="400" width="400" style="border:1px solid;"></canvas>
<div id="score_div"></div>
<div id="time">0.00</div>
<script type="text/javascript">
var score=0,fps=30,canvas=document.getElementById("can_game"),context=canvas.getContext('2d'),dir="",score_div=document.getElementById("score_div"),width_h=45,pass='left',timediv=document.getElementById("time"),time=0.00;
var player = {
color : '#ff00',
px :'180',
py : '384',
height : '12',
width : '45',
draw : function(){
context.fillRect(this.px,this.py,this.width,this.height);
}
}
var ball = {
x :Math.round(Math.random(4)*400),
y : Math.round(Math.random(4)*400),
draw : function() {
context.beginPath();
context.fillStyle = "#ff0000";
context.arc(this.x,this.y,6,0,2*Math.PI,false);
context.fill();
}
}
setInterval(function(){
score_div.innerHTML=score;
timediv.innerHTML=Math.round(time/1000)+" Seconds";
time=parseInt(time)+1000/fps;
update();
draw();
var test=hitTestPoint(player.px,player.py,player.width,player.height,ball.x,ball.y);
if(test===true)
{
ball.x=Math.round(Math.random(80)*400);
ball.y=Math.round(Math.random(80)*400);
score+=10;
width_h+=3;
}
console.log(player.py,canvas.width);
if(player.px <=0)
player.px=360;
else if(player.px >= (canvas.width))
player.px=0;
else if(player.py <= 0)
player.py=360;
else if(player.py >= (canvas.height))
player.py=0;
document.onkeydown = function() {
var key_pressed = window.event.keyCode;
/* if(key_pressed == 32)
dir = "freeze";*/
if(key_pressed == 37)
dir = "left";
else if(key_pressed == 39)
dir = "right";
else if(key_pressed == 38)
dir = "up";
else if(key_pressed == 40)
dir = "down";
};
},1000/fps);
function hitTestPoint(x1, y1, w1, h1, x2, y2)
{
if ((x1 <= x2 && x1+w1 >= x2) &&
(y1 <= y2 && y1+h1 >= y2))
return true;
else
return false;
}
function draw()
{
context.clearRect(0,0,canvas.width,canvas.height);
player.draw();
ball.draw();
context.fill();
}
function update()
{
switch(dir){
case "left":
player.px-=3;
player.width=width_h;
player.height=12;
break;
case "right":
player.px= parseInt(player.px)+3;
player.width=width_h;
player.height=12;
break;
case "up":
player.py-=3;
player.width=12;
player.height=width_h;
break;
case "down":
player.py+=3;
player.width=12;
player.height=width_h;
break;
}
}
</script>
</body>
</html>
You might want to take a look at this CSS deck tutorial to solve your problem.
Here the trick is , that the snake is composed of individual small and discrete blocks rather than the one as in your case where its a static block, and every time the snake is progressed one step, one block is popped from the rear and a new block is added to the top(face).
So when the snakes moves in another direction(or axis) its face block are gradually added in another axis and it gives an illusion of snake turning in the way you expect it

HTML5 arrow keys not working correctly

Hello everyone,
I'm currently having issues making the arrow keys working correctly while pressing [space] key. Everything is working whle holding [space] key and one of the arrow keys. But if I try to press [space] and hold [up] and [left] at the same time , it will go straigh to the top of the screen as if the [left] key wasn't even pressed (it should move diagonally to the top left corner). This is only happening while pressing [space].
I'd like to use this key for shooting bullets later on.
Is there something wrong in my code? Or is it some kind of bug?
Apologies for my bad english :/ ..
<!DOCTYPE html>
<body>
<canvas id="myCanvas" width="568" height="262">
</canvas>
<script type="text/javascript">
var canvas;
var ctx;
var dx = 0.5;
var dy = 0.5;
var x = 284;
var y = 130;
var WIDTH = 568;
var HEIGHT = 262;
var keys = new Array();
function circle(x,y,r) {
ctx.beginPath();
ctx.arc(x, y, r, 0, Math.PI*2, true);
ctx.fill();
}
function rect(x,y,w,h) {
ctx.beginPath();
ctx.rect(x,y,w,h);
ctx.closePath();
ctx.fill();
ctx.stroke();
}
function clear() {
ctx.clearRect(0, 0, WIDTH, HEIGHT);
}
function init() {
canvas = document.getElementById("myCanvas");
ctx = canvas.getContext("2d");
window.addEventListener('keydown',doKeyDown,true);
window.addEventListener('keyup',doKeyUp,true);
return setInterval(draw, 1);
}
function draw() {
move();
clear();
ctx.fillStyle = "white";
ctx.strokeStyle = "black";
rect(0,0,WIDTH,HEIGHT);
// player ///
ctx.fillStyle = "purple";
circle(x, y, 20);
}
function doKeyDown(evt)
{
keys[evt.keyCode] = true;
evt.preventDefault(); // Prevents the page to scroll up/down while pressing arrow keys
}
function doKeyUp(evt){
keys[evt.keyCode] = false;
}
function move() {
if (32 in keys && keys[32]){
; // fire bullets
}
if (38 in keys && keys[38]){ //up
y -= dy;
}
if (40 in keys && keys[40]){ //down
y += dy;
}
if (37 in keys && keys[37]){ //left
x -= dx;
}
if (39 in keys && keys[39]){ //right
x += dx;
}
}
init();
</script>
</body>
</html>
Odds are this is nothing to do with your code. Many keyboards, to save cost, are not capable of supporting every combination of 3 or more keys at once, and are specifically wired to support combinations that are in common use (such as Ctrl+Alt+Del). The phenomenon is known as "ghosting".
See this page from Microsoft for an expanded explanation.
I had the same issue (I found the issue but not the solution) the key codes for up and left follow each other. so do down and right. The fact that the key codes follow each other seems to be the issue. if you set A to move player UP and B to move player DOWN, then press A+B+spaceBar the same issue occurs. but when the key codes are different (e.g. wasd) the three button press works. using WASD might seem like a good idea but wait until someone using a French keyboard plays your game.
Any way I wrote this hoping that someone would find a work around.

phonegap JavaScript canvas doesn't work

I've found a simple JavaScript bouncing ball animation, it works on chrome browser (PC)
When I run it, using Phonegap eclipse android emulator it compiles but canvas is blank and the following message displayed "unfortunately system ui has stopped"
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" charset="utf-8" src="cordova.js"></script>
<script type="text/javascript" charset="utf-8">
<script>
var context;
var x = 100;
var y = 100;
var radius = 20;
var dx = 5;
var dy = 5;
function init(){
context = myCanvas.getContext('2d');
//Set an interval for the canvas to be refreshed.
// Basically call the draw function every 10 ms
setInterval(draw, 10);
}
function draw(){
//Clear the old circle before we draw the new one
context.clearRect(0,0, myCanvas.width, myCanvas.height); //This erases the entire canvas
context.beginPath();
context.fillStyle = "#0000ff";
//Draw a circle of radius 20 at the current coordinates on the canvas.
context.arc(x, y, radius, 0, Math.PI*2, true);
context.closePath();
context.fill();
//Check boundaries and negate if necessary
if (x + radius <= 0 || x - radius <= 0 || x + radius >= myCanvas.width || x - radius >= myCanvas.width)
dx = -dx;
if (y + radius <= 0 || y - radius <= 0 || y + radius >= myCanvas.height || y - radius >= myCanvas.height)
dy = -dy;
//Increment dx,dy
x+=dx;
y+=dy;
}
</script>
<title>Ball Bouncing</title>
</head>
<body onLoad = "init();"> <!-- when the body is loaded, call the init() function -->
<canvas id = "myCanvas" width ="500" height = "400">
</canvas>
</body>
</html>
Also for some reason it works if i remove the following:
<script type="text/javascript" charset="utf-8" src="cordova.js"></script>
<script type="text/javascript" charset="utf-8">
ok so i removed
<body onLoad = "init();">
and added
document.addEventListener("deviceready",init,false);
and i get the following logcat:
09-23 21:53:30.886: E/Web Console(796): Uncaught SyntaxError: "Unexpected token < at file:///android_asset/www/index.html:7" whats is wrong ?
please help
Update
<script type="text/javascript" charset="utf-8">
<script>
var context;
Maybe the second script-tag is causing the problem?
There is a line missing like:
myCanvas = document.getElementById("myCanvas");
The html-canvas element should work fine with android. Try the "standard" browser from the android device (not chrome) and test the URL. Cordova uses this browserengine and not chrome.
If that works it is probabaly the app-initialization.
Don't use onLoad() in a caordova application. Before you do anything you have to wait for deviceready-event.
In your init() register for documenready and start your draw-timer within that event-handler.
Something different:
Use requestAnimationFrame() it is better for mobile devices, because you wull only repaint within the default paint-loop. Also only paint if needed (maybe if currently touched), you will suck the battery empty, if you always repaint.

Categories

Resources