How to create obstacles in canvas - javascript

I am trying to make a simple platformer like game.The code i am using is shown below
window.onload = function(){
var canvas = document.getElementById('game');
var ctx = canvas.getContext("2d");
var rightKeyPress = false;
var leftKeyPress = false;
var upKeyPress = false;
var downKeyPress = false;
var playerX = canvas.width / 2;
var playerY = -50;
var dx = 3;
var dy = 3;
var dxp = 3;
var dyp = 3;
var dxn = 3;
var dyn = 3;
var prevDxp = dxp;
var prevDyp = dyp;
var prevDxn = dxn;
var prevDyn = dyn;
var playerWidth = 50;
var playerHeight = 50;
var obstacleWidth = 150;
var obstacleHeight = 50;
var obstaclePadding = 10;
var G = .98;
var currentVelocity = 0;
var obstacles = [];
var imageLoaded = false;
document.addEventListener("keyup",keyUp,false);
document.addEventListener("keydown",keyDown,false);
function keyDown(e){
if(e.keyCode == 37){
leftKeyPress = true;
if(currentVelocity > 2){
currentVelocity -= .1;
}
}
if(e.keyCode == 38){
upKeyPress = true;
}
if(e.keyCode == 39){
rightKeyPress = true;
if(currentVelocity < 2){
currentVelocity += .1;
}
}
if(e.keyCode == 40){
downKeyPress = true;
}
}
function keyUp(e){
if(e.keyCode == 37){
leftKeyPress = false;
}
if(e.keyCode == 38){
upKeyPress = false;
}
if(e.keyCode == 39){
rightKeyPress = false;
}
if(e.keyCode == 40){
downKeyPress = false;
}
}
function createObstacles(){
for(x=0;x < 4;x++){
var obX = (200 * x) + Math.round(Math.random() * 150);
var obY = 50 + Math.round(Math.random() * 400);
obstacles.push({"x":obX,"y":obY});
}
}
createObstacles();
function drawObstacles(){
ctx.beginPath();
for(x=0;x < 4;x++){
var obX = obstacles[x].x;
var obY = obstacles[x].y;
ctx.rect(obX,obY,obstacleWidth,obstacleHeight)
}
ctx.fillStyle = "grey";
ctx.fill();
ctx.closePath();
}
function initPlayer(){
ctx.beginPath();
ctx.rect(playerX,playerY,50,50);
ctx.fillStyle="orange";
ctx.fill();
ctx.closePath();
}
function KeyPressAndGravity(){
checkObstacleCollision();
playerX += currentVelocity;
if(rightKeyPress && playerX + 50 < canvas.width){
playerX += dxp;
}
if(leftKeyPress && playerX > 0){
playerX -= dxn;
}
if(upKeyPress && playerY > 0){
playerY -= dyn;
}
if(downKeyPress && playerY + 50 < canvas.height){
playerY += dyp;
}
if(playerY+50 < canvas.height){
playerY += G;
}
if(playerX <= 0){
currentVelocity = 0;
}else if(playerX + 50 >= canvas.width){
currentVelocity = 0;
}
dxp = prevDxp;
dyp = prevDyp;
dxn = prevDxn;
dyn = prevDyn;
G = .98;
if(currentVelocity != 0){
if(currentVelocity > 0){
currentVelocity -= .01;
}else{
currentVelocity += .01;
}
}
}
/*-----------------------------------------------------------
-------------------------------------------------------------
-------------------------------------------------------------
---------------------------Check this part-------------------
-------------------------------------------------------------
-------------------------------------------------------------
-------------------------------------------------------------
------------------------------------------------------------*/
function checkObstacleCollision(){
var obLen = obstacles.length;
for(var x=0;x<obLen;x++){
var obX = obstacles[x].x;
var obY = obstacles[x].y;
if((playerX + playerWidth > obX && playerX + playerWidth < obX + obstacleWidth || playerX > obX && playerX < obX + obstacleWidth) && playerY + playerHeight > obY - obstaclePadding && playerY + playerHeight < obY){
dyp = 0;
G = 0;
}else if((playerX + playerWidth > obX && playerX + playerWidth < obX + obstacleWidth || playerX > obX && playerX < obX + obstacleWidth) && playerY > obY + obstacleHeight && playerY < obY + obstacleHeight + obstaclePadding){
dyn = 0;
}else if(playerX + playerWidth > obX - obstaclePadding && playerX + playerWidth < obX && ((playerY + playerHeight > obY && playerY + playerHeight < obY + obstacleHeight) || (playerY > obY && playerY < obY + obstacleHeight))){
dxp = 0;
}else if(playerX > obX + obstacleWidth && playerX < obX + obstacleWidth + obstaclePadding && ((playerY + playerHeight > obY && playerY + playerHeight < obY + obstacleHeight) || (playerY > obY && playerY < obY + obstacleHeight))){
dxn = 0;
}
}
}
function draw(){
ctx.clearRect(0,0,canvas.width,canvas.height);
initPlayer();
KeyPressAndGravity();
drawObstacles();
}
setInterval(draw,15);
}
<canvas id="game" width="1000" height="600" style="border:1px solid #000;"></canvas>
The problem is that sometimes when the speed of the "player" is high it can go through obstacles like the below image. How can i stop that from happening ?
So what i want is that the player should stop right as he reaches the obstacle and not pass through it

There is a complication when collision testing objects that are moving quickly
You must determine if your player and obstacle intersected at any time during the move -- even if the player has moved beyond the obstacle by the end of the move. Therefore you must account for the complete path the player has moved from start to end of the move.
...
Then you can check if the player ever intersected the obstacle during the move by checking if the player's track intersects the obstacle.
A relatively efficient method for testing collisions involving fast moving objects
Define the 3 line segments that connect the 3 vertices of the player's starting rectangle that are closest to the player's ending rectangle.
For any of the 3 lines that intersect an obstacle, calculate the distance of the line segment to the obstacle. Select the line that has the shortest distance between starting vertex and the obstacle.
Calculate the "x" & "y" distances of the selected line segment.
var dx = obstacleIntersection.x - start.x;
var dy = obstacleIntersection.y - start.y;
Move the player from their starting position by the distance calculated in #3. This results in the player moving to the spot where it first collided with the obstacle.
player.x += dx;
player.y += dy;
Code and Demo:
Useful functions in the code:
setPlayerVertices determines the 3 line segments that connect the 3 vertices of the player's starting rectangle that are closest to the player's ending rectangle.
hasCollided finds the shortest segment connecting a vertex from the player's starting position with the collision point on the obstacle.
line2lineIntersection finds the intersection point (if any) between 2 lines. This is used to test for an intersection between a start-to-end segment (from #1) and any of the 4 line segments that make up the obstacle rectangle. Attribution: This function is adapted from Paul Bourke's useful treatice on intersections.
Here is example code and a Demo showing how to halt the player at the collision point on the obstacle:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
function reOffset(){
var BB=canvas.getBoundingClientRect();
offsetX=BB.left;
offsetY=BB.top;
}
var offsetX,offsetY;
reOffset();
window.onscroll=function(e){ reOffset(); }
window.onresize=function(e){ reOffset(); }
var isDown=false;
var startX,startY,dragging;
ctx.translate(0.50,0.50);
ctx.textAlign='center';
ctx.textBaseline='middle';
var pts;
var p1={x:50,y:50,w:25,h:25,fill:''};
var p2={x:250,y:250,w:25,h:25,fill:''};
var ob={x:100,y:150,w:125,h:25,fill:''};
var obVertices=[
{x:ob.x,y:ob.y},
{x:ob.x+ob.w,y:ob.y},
{x:ob.x+ob.w,y:ob.y+ob.h},
{x:ob.x,y:ob.y+ob.h}
];
var s1,s2,s3,e1,e2,e3,o1,o2,o3,o4;
draw();
$("#canvas").mousedown(function(e){handleMouseDown(e);});
$("#canvas").mousemove(function(e){handleMouseMove(e);});
$("#canvas").mouseup(function(e){handleMouseUpOut(e);});
$("#canvas").mouseout(function(e){handleMouseUpOut(e);});
function draw(){
ctx.clearRect(0,0,cw,ch);
//
ctx.lineWidth=4;
ctx.globalAlpha=0.250;
ctx.strokeStyle='blue';
ctx.strokeRect(ob.x,ob.y,ob.w,ob.h);
ctx.globalAlpha=1.00;
ctx.fillStyle='black';
ctx.fillText('obstacle',ob.x+ob.w/2,ob.y+ob.h/2);
//
ctx.globalAlpha=0.250;
ctx.strokeStyle='gold';
ctx.strokeRect(p1.x,p1.y,p1.w,p1.h);
ctx.strokeStyle='purple';
ctx.strokeRect(p2.x,p2.y,p2.w,p2.h);
ctx.fillStyle='black';
ctx.globalAlpha=1.00;
ctx.fillText('start',p1.x+p1.w/2,p1.y+p1.h/2);
ctx.fillText('end',p2.x+p2.w/2,p2.y+p2.h/2);
}
function handleMouseDown(e){
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
startX=parseInt(e.clientX-offsetX);
startY=parseInt(e.clientY-offsetY);
// Put your mousedown stuff here
var mx=startX;
var my=startY;
if(mx>p1.x && mx<p1.x+p1.w && my>p1.y && my<p1.y+p1.h){
isDown=true;
dragging=p1;
}else if(mx>p2.x && mx<p2.x+p2.w && my>p2.y && my<p2.y+p2.h){
isDown=true;
dragging=p2;
}
}
function handleMouseUpOut(e){
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// Put your mouseup stuff here
isDown=false;
dragging=null;
}
function handleMouseMove(e){
if(!isDown){return;}
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// Put your mousemove stuff here
var dx=mouseX-startX;
var dy=mouseY-startY;
startX=mouseX;
startY=mouseY;
//
dragging.x+=dx;
dragging.y+=dy;
//
draw();
//
setPlayerVertices(p1,p2);
var c=hasCollided(obVertices);
if(c.dx){
ctx.strokeStyle='gold';
ctx.strokeRect(p1.x+c.dx,p1.y+c.dy,p1.w,p1.h);
ctx.fillStyle='black';
ctx.fillText('hit',p1.x+c.dx+p1.w/2,p1.y+c.dy+p1.h/2);
line(c.s,c.i,'red');
}
}
function setPlayerVertices(p1,p2){
var tl1={x:p1.x, y:p1.y};
var tl2={x:p2.x, y:p2.y};
var tr1={x:p1.x+p1.w, y:p1.y};
var tr2={x:p2.x+p2.w, y:p2.y};
var br1={x:p1.x+p1.w, y:p1.y+p1.h};
var br2={x:p2.x+p2.w, y:p2.y+p2.h};
var bl1={x:p1.x, y:p1.y+p1.h};
var bl2={x:p2.x, y:p2.y+p2.h};
//
if(p1.x<=p2.x && p1.y<=p2.y){
s1=tr1; s2=br1; s3=bl1;
e1=tr2; e2=br2; e3=bl2;
o1=0; o2=1; o3=3; o4=0;
}else if(p1.x<=p2.x && p1.y>=p2.y){
s1=tl1; s2=tr1; s3=br1;
e1=tl2; e2=tr2; e3=br2;
o1=2; o2=3; o3=3; o4=0;
}else if(p1.x>=p2.x && p1.y<=p2.y){
s1=tl1; s2=br1; s3=bl1;
e1=tl2; e2=br2; e3=bl2;
o1=0; o2=1; o3=1; o4=2;
}else if(p1.x>=p2.x && p1.y>=p2.y){
s1=tl1; s2=tr1; s3=bl1;
e1=tl2; e2=tr2; e3=bl2;
o1=1; o2=2; o3=2; o4=3;
}
}
function hasCollided(o){
//
var i1=line2lineIntersection(s1,e1,o[o1],o[o2]);
var i2=line2lineIntersection(s2,e2,o[o1],o[o2]);
var i3=line2lineIntersection(s3,e3,o[o1],o[o2]);
var i4=line2lineIntersection(s1,e1,o[o3],o[o4]);
var i5=line2lineIntersection(s2,e2,o[o3],o[o4]);
var i6=line2lineIntersection(s3,e3,o[o3],o[o4]);
//
var tracks=[];
if(i1){tracks.push(track(s1,e1,i1));}
if(i2){tracks.push(track(s2,e2,i2));}
if(i3){tracks.push(track(s3,e3,i3));}
if(i4){tracks.push(track(s1,e1,i4));}
if(i5){tracks.push(track(s2,e2,i5));}
if(i6){tracks.push(track(s3,e3,i6));}
//
var nohitDist=10000000;
var minDistSq=nohitDist;
var halt={dx:null,dy:null,};
for(var i=0;i<tracks.length;i++){
var t=tracks[i];
var testdist=t.dx*t.dx+t.dy*t.dy;
if(testdist<minDistSq){
minDistSq=testdist;
halt.dx=t.dx;
halt.dy=t.dy;
halt.s=t.s;
halt.i=t.i;
}
}
return(halt);
}
//
function track(s,e,i){
dot(s);dot(i);line(s,i);line(i,e);
return({ dx:i.x-s.x, dy:i.y-s.y, s:s, i:i });
}
function line2lineIntersection(p0,p1,p2,p3) {
var unknownA = (p3.x-p2.x) * (p0.y-p2.y) - (p3.y-p2.y) * (p0.x-p2.x);
var unknownB = (p1.x-p0.x) * (p0.y-p2.y) - (p1.y-p0.y) * (p0.x-p2.x);
var denominator = (p3.y-p2.y) * (p1.x-p0.x) - (p3.x-p2.x) * (p1.y-p0.y);
// Test if Coincident
// If the denominator and numerator for the ua and ub are 0
// then the two lines are coincident.
if(unknownA==0 && unknownB==0 && denominator==0){return(null);}
// Test if Parallel
// If the denominator for the equations for ua and ub is 0
// then the two lines are parallel.
if (denominator == 0) return null;
// If the intersection of line segments is required
// then it is only necessary to test if ua and ub lie between 0 and 1.
// Whichever one lies within that range then the corresponding
// line segment contains the intersection point.
// If both lie within the range of 0 to 1 then
// the intersection point is within both line segments.
unknownA /= denominator;
unknownB /= denominator;
var isIntersecting=(unknownA>=0 && unknownA<=1 && unknownB>=0 && unknownB<=1)
if(!isIntersecting){return(null);}
return({
x: p0.x + unknownA * (p1.x-p0.x),
y: p0.y + unknownA * (p1.y-p0.y)
});
}
function dot(pt){
ctx.beginPath();
ctx.arc(pt.x,pt.y,3,0,Math.PI*2);
ctx.closePath();
ctx.fill();
}
function line(p0,p1,stroke,lw){
ctx.beginPath();
ctx.moveTo(p0.x,p0.y);
ctx.lineTo(p1.x,p1.y);
ctx.lineWidth=lw || 1;
ctx.strokeStyle=stroke || 'gray';
ctx.stroke();
}
body{ background-color: ivory; }
#canvas{border:1px solid red; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h4>Drag start & end player position rects<br>The shortest segment intersecting the obstacle is red.<br>The repositioned player is shown on the obstacle.</h4>
<canvas id="canvas" width=400 height=400></canvas>

What you are experiencing is usually called tunneling.
There are lot's of different ways to solve it, but the easiest is usually to save last position and do one of the following.
A
Calculate a new larger collision box for each element, containing the element's last position and it's new one. Think of this as a box containing your element twice. Once for it's last position (LP) and one for it's new position (NP),
------------
|| LP | |
||____| |
| ____ |
| | NP ||
|______|____||
Now if you use this new box to check collisions, it will take the path travelled into account to avoid tunneling. This might create unexpected collisions in the top right and bottom left corner, but it's a simple implementation and the tradeoff might be worth it.
B
Check collisions for each step along the path traveled from it's last position to it's new position. If your element has traveled 5 pixels since the last frame, you check the collision once for each pixel (or minimum acceptable collision distance).
____
| LP |
|____||
---- |___
|___|NP |
|____|
This will of course increase the number of collision detections and have an impact on performance. Here you could look into quadtrees to compensate for the performance loss.
Moving forward, there are a lot more elegant and advanced solutions, but the subject is to broad for a complete answer here.
Hope it helps!

Well, I made a "collision calculator" about several months ago, so you can change and use the code below as you like :) For better explanation:
p_x is last player position x plus his width
p_y is last player position y plus his height
p_x_m is last player position x
p_y_m is last player position y
y_m is new player position (his y - somevalue)
x_m is new player position (his x - somevalue)
y_p is new player position (his y + somevalue + his height)
y_p_m is new player position (his y + somevalue)
x_p is new player position (his x + somevalue + his width)
x_p_m is new player position (his x + somevalue)
w_x is wall position x
w_y is wall position y
w_w is wall width
w_h is wall height
pressedKeys is a string telling which keys player pressed (for example "was" or "wd" or "ad" etc.)
this.walls is a variable with walls (for example if I've got 4 walls the array would look like [false,'s',false,false] because I touched second wall with "s" key).
Code:
if(
pressedKeys.indexOf("s")>-1 &&
(
( // P
p_y>w_y&&p_y<(w_y+w_h)&&x_p_m>w_x && x_p-5>w_x && x_m<w_x // +----
) || // |
( // P
y_p>w_y&&p_y<(w_y+w_h) && x_p-5>w_x && x_p<=(w_x+w_w) // +--------+
) || // | |
( // P
y_p>w_y&&p_y<(w_y+w_h) && x_p>(w_x+w_w)&&p_x_m<(w_x+w_w) && x_m+5<(w_x+w_w) // ----+
) // |
)
)
{
if(this.walls[i] == false)
this.walls[i] = "";
this.walls[i] += "s";
}
if(
pressedKeys.indexOf("d")>-1 &&
(
( // P+----
p_x>w_x&&p_x<(w_x+w_w)&&y_p_m>w_y && y_p-5>w_y && y_m<w_y // |
) || // |
( // |
x_p>w_x&&p_x<(w_x+w_w) && y_p-5>w_y && y_p<=(w_y+w_h) // P|
) || // |
( // |
x_p>w_x&&p_x<(w_x+w_w) && y_p>(w_y+w_h)&&p_y_m<(w_y+w_h) && y_m+5<(w_y+w_h) // |
) // P+----
)
)
{
if(this.walls[i] == false)
this.walls[i] = "";
this.walls[i] += "d";
}
if(
pressedKeys.indexOf("w")>-1 &&
(
( // |
y_m<(w_y+w_h)&&y_p-5>w_y && x_p-5>w_x && x_m<w_x &&x_p_m>w_x // +----
) || // P
( // | |
y_m<(w_y+w_h)&&y_p-5>w_y && x_p-5>w_x && x_p<=(w_x+w_w) // +--------+
) || // P
( // |
y_m<(w_y+w_h)&&y_p-5>w_y && x_p>(w_x+w_w)&&p_x_m<(w_x+w_w) && x_m+5<(w_x+w_w) // ----+
) // P
)
)
{
if(this.walls[i] == false)
this.walls[i] = "";
this.walls[i] += "w";
}
if(
pressedKeys.indexOf("a")>-1 &&
(
( // ----+P
x_m<(w_x+w_w)&&x_p-5>w_x && y_p-5>w_y && y_m<w_y &&y_p_m>w_y // |
) || // |
( // |
x_m<(w_x+w_w)&&x_p-5>w_x && y_p-5>w_y && y_p<=(w_y+w_h) // |P
) || // |
( // |
x_m<(w_x+w_w)&&x_p-5>w_x && y_p>(w_y+w_h)&&p_y_m<(w_y+w_h) && y_m+5<(w_y+w_h) // |P
) // ----+P
)
)
{
if(this.walls[i] == false)
this.walls[i] = "";
this.walls[i] += "a";
}
Comments at the right side of the code show how player collides.
This code 100% works, I use it everytime I want to check for collision.
Hope it helped a bit :)

Related

Javascript snake game bugs

There are two issues, I can't control the snake, and now it just resets constantly without giving the snake a chance to move. I figured that if i kept coding the problem would go away but sadly its not a perfect world.
I tried different key codes for controlling the snake but it didn't work.
window.onload = function() {
var cvs = document.getElementById("canvas");
var ctx = cvs.getContext("2d");
var cvsW = cvs.width;
var cvsH = cvs.height;
var snekW = 10;
var snekH = 10;
//score
var score = 0;
//default direction
var direction = "right";
//read users directions
document.addEventListener("keydown",getDirection);
//To my knowledge this function should control the snake with the
keyboard
function getDirection(e)
{
if(e.keyCode == 37 && direction != "right"){
direction = "left";
}else if(e.keyCode == 38 && direction != "down"){
direction = "up";
}else if(e.keyCode == 39 && direction != "left"){
direction = "right";
}else if(e.keycode == 40 && direction != "up"){
direction = "down";
}
}
function drawSnek(x,y)
{
ctx.fillStyle = "Lime";
ctx.fillRect(x*snekW,y*snekH,snekW,snekH);
ctx.fillStyle = "#000";
ctx.strokeRect(x*snekW,y*snekH,snekW,snekH);
}
var len = 4; //default length of the snake
var snek = []; //the snake is an array
for(var i = len-1; i>=0; i--)
{
snek.push({x:i,y:0});
}
//exceptions for direction of snake
if(direction == "left") snekx--;
else if( direction == "up") sneky--;
else if( direction == "right") snekx++;
else if( direction == "down") sneky++;
//Functions for creating snake food have been removed.
var newHead = { x: snekx, y: sneky};
snek.unshift(newHead);
drawScore(score);
}
setInterval(draw, 10); //I do not fully understand this function
}
There were several issues with your code:
You needed a draw function that could be called in an interval. In this draw function you could draw the individual parts of the snake.
You were unable to control the snake correctly as you had a typo in the down part of the callback function.
You have to either introduce x / y variables for the head of the snake or use the first element of the array to retrieve it.
Unshifting the new position using snek.unshift will cause the snake to grow indefinitely. Removing array elements using
snek.splice(len,snek.length - len);
solves this problem.
If you only draw new elements to the screen the old parts you drew will still exist in new frames. A good idea would be to clear the canvas using
ctx.clearRect(0, 0, cvsW, cvsH);
window.onload = function () {
var cvs = document.getElementById("canvas");
var ctx = cvs.getContext("2d");
var cvsW = cvs.width;
var cvsH = cvs.height;
var snekW = 10;
var snekH = 10;
//score
var score = 0;
//default direction
var direction = "right";
//read users directions
document.addEventListener("keydown", getDirection);
//To my knowledge this function should control the snake with the keyboard
function getDirection(e) {
if (e.keyCode == 37 && direction != "right") {
direction = "left";
} else if (e.keyCode == 38 && direction != "down") {
direction = "up";
} else if (e.keyCode == 39 && direction != "left") {
direction = "right";
} else if (e.keyCode == 40 && direction != "up") {
direction = "down";
}
}
function drawSnek(x, y) {
ctx.fillStyle = "Lime";
ctx.fillRect(x * snekW, y * snekH, snekW, snekH);
ctx.fillStyle = "#000";
ctx.strokeRect(x * snekW, y * snekH, snekW, snekH);
}
let snekx = 0;
let sneky = 0;
var len = 4; //default length of the snake
var snek = []; //the snake is an array
for (var i = len - 1; i >= 0; i--) {
snek.push({ x: i, y: 0 });
}
function draw() {
ctx.clearRect(0, 0, cvsW, cvsH);
//exceptions for direction of snake
if (direction == "left") snekx--;
else if (direction == "up") sneky--;
else if (direction == "right") snekx++;
else if (direction == "down") sneky++;
//Functions for creating snake food have been removed.
var newHead = { x: snekx, y: sneky };
snek.unshift(newHead);
for(let i = 0; i < snek.length; i++) {
drawSnek(snek[i].x, snek[i].y)
}
snek.splice(len,snek.length - len);
//drawScore(score);
}
setInterval(draw, 50); //I do not fully understand this function
}
CodePen with fixed code:
https://codepen.io/lukasmoeller/pen/PvVzqq?editors=1111
(boundaries are not checked - the snake will leave the canvas)
Explanation of setInterval(fn, interval)
setInterval calls the function specified using fn every interval ms until it is cleared. The interval can be cleared by using clearInterval, passing it the return value of setInterval. If you want to delay function execution at the beginning you could add setTimeout, add a callback that sets the interval:
setTimeout(function(){
setInterval(draw, 50);
}, 1000)
This would only start drawing the game every 50 ms after 1000ms have passed.

detecting collisions on the canvas

im trying to make a game where collisions happen between the protagonist and the antagonist. I cant get the collision to work though, i've tried using the x and y position then the x and y positions plus the width and the height of the protagonist and the antagonist
var canvas = document.getElementById('canvas');
var PX = 10;
var PY = 10;
var PW = 10;
var PH = 10;
var P = PX + PY;
var EX1 = 100;
var EY1 = 100;
var EW1 = 10;
var EH1 = 10;
var E1 = EX1 + EY1;
window.addEventListener("keydown", charMove);
window.addEventListener("keyup", charMove);
window.addEventListener("keypress", charMove);
window.onload = function() {
context = canvas.getContext("2d");
canvas.style.background = "black";
var framesPerSecond = 30;
setInterval(function() {
draw();
move();
}, 1000/framesPerSecond);
}
function draw() {
//EX context.fillRect(PosX, PosY, width, height);
//draws protagonist
context.beginPath();
context.clearRect(0, 0, canvas.width, canvas.height);
context.fillStyle = "blue"
context.fillRect(PX, PY, PW, PH);
context.stroke();
context.closePath();
//draws antagonist(s)
context.beginPath();
context.fillStlyle = "red";
context.fillRect(EX1, EY1, EW1, EH1);
context.stroke();
context.closePath();
}
function move() {
}
function charMove(){
var x = event.which || event.keyCode;
if(x == 37){
PX -= 1;
}
if(x == 38){
PY -= 1;
}
if(x == 39){
PX += 1;
}
if(x == 40){
PY += 1;
}
}
//detect collision
setInterval(function() {
if(PX > EX1 || PX + PW < EX1 + EW1 && PY + PH > EY1 + EH1 || PY + PH < EY1 + EH1){
window.alert("collision");
}
}, 1);
<html>
<head>
</head>
<body>
<canvas width="500px" height="500px" id="canvas" class="canvas">
</body>
</html>
Your formula for collision is wrong.
This problem is called Axis Aligned Bounding Box collision.
Two AABBs collide if their projections to each axis collide. In your 2-dimensinal case you have to consider the horizontal and vertical projections.
The projections are segments of 1-d space. Collision for those is very easy: if the start or the end of a segment is on the other they collide. Formally start2 <= start1 <= end2 or start2 <= end1 <= end2
In code:
intersects([p.x, p.x + p.width], [e.x, e.x + e.width]) && intersects([p.y, p.y + p.height], [e.y, e.y + e.height])
where
function intersects(seg1, seg2) {
return contains(seg1, seg2[0]) || contains(seg1, seg2[1])
}
function contains(segment, point) {
return segment[0] <= point && point <= segment[1]
}

How to perform per pixel collision test for transparent images? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
If I have two partially transparent images (GIF, PNG, SVG etc.), how do I check if the non-transparent areas of the images intersect?
I'm fine with using canvas if it's necessary. The solution needs to work with all image formats that support transparency. No jQuery please.
Touching
Not Touching
Fast GPU assisted Pixel / Pixel collisions using 2D API.
By using the 2D context globalCompositeOperation you can greatly increase the speed of pixel pixel overlap test.
destination-in
The comp operation "destination-in" will only leave pixels that are visible on the canvas and the image you draw on top of it. Thus you create a canvas, draw one image, then set the comp operation to "destination-in" then draw the second image. If any pixels are overlapping then they will have a non zero alpha. All you do then is read the pixels and if any of them are not zero you know there is an overlap.
More speed
Testing all the pixels in the overlapping area will be slow. You can get the GPU to do some math for you and scale the composite image down. There is some loss as pixels are only 8bit values. This can be overcome by reducing the image in steps and rendering the results several times. Each reduction is like calculating a mean. I scale down by 8 effectively getting the mean of 64 pixels. To stop pixels at the bottom of the range disappearing due to rounding I draw the image several times. I do it 32 time which has the effect of multiplying the alpha channel by 32.
Extending
This method can easily be modified to allow both images to be scaled, skewed and rotated without any major performance hit. You can also use it to test many images with it returning true if all images have pixels overlapping.
Pixels are small so you can get extra speed if you reduce the image size before creating the test canvas in the function. This can give a significant performance boost.
There is a flag reuseCanvas that allows you to reuse the working canvases. If you use the test function a lot (many times a second) then set the flag to true. If you only need the test every now and then then set it to false.
Limits
This method is good for large images that need occasional tests; it is not good for small images and many tests per frame (such as in games where you may need to test 100's of images). For fast (almost perfect pixel) collision tests see Radial Perimeter Test.
The test as a function.
// Use the options to set quality of result
// Slow but perfect
var slowButPerfect = false;
// if reuseCanvas is true then the canvases are resused saving some time
const reuseCanvas = true;
// hold canvas references.
var pixCanvas;
var pixCanvas1;
// returns true if any pixels are overlapping
// img1,img2 the two images to test
// x,y location of img1
// x1,y1 location of img2
function isPixelOverlap(img1,x,y,img2,x1,y1){
var ax,aw,ay,ah,ctx,canvas,ctx1,canvas1,i,w,w1,h,h1;
w = img1.width;
h = img1.height;
w1 = img2.width;
h1 = img2.height;
// function to check if any pixels are visible
function checkPixels(context,w,h){
var imageData = new Uint32Array(context.getImageData(0,0,w,h).data.buffer);
var i = 0;
// if any pixel is not zero then there must be an overlap
while(i < imageData.length){
if(imageData[i++] !== 0){
return true;
}
}
return false;
}
// check if they overlap
if(x > x1 + w1 || x + w < x1 || y > y1 + h1 || y + h < y1){
return false; // no overlap
}
// size of overlapping area
// find left edge
ax = x < x1 ? x1 : x;
// find right edge calculate width
aw = x + w < x1 + w1 ? (x + w) - ax : (x1 + w1) - ax
// do the same for top and bottom
ay = y < y1 ? y1 : y;
ah = y + h < y1 + h1 ? (y + h) - ay : (y1 + h1) - ay
// Create a canvas to do the masking on
if(!reuseCanvas || pixCanvas === undefined){
pixCanvas = document.createElement("canvas");
}
pixCanvas.width = aw;
pixCanvas.height = ah;
ctx = pixCanvas.getContext("2d");
// draw the first image relative to the overlap area
ctx.drawImage(img1,x - ax, y - ay);
// set the composite operation to destination-in
ctx.globalCompositeOperation = "destination-in"; // this means only pixels
// will remain if both images
// are not transparent
ctx.drawImage(img2,x1 - ax, y1 - ay);
ctx.globalCompositeOperation = "source-over";
// are we using slow method???
if(slowButPerfect){
if(!reuseCanvas){ // are we keeping the canvas
pixCanvas = undefined; // no then release referance
}
return checkPixels(ctx,aw,ah);
}
// now draw over its self to amplify any pixels that have low alpha
for(var i = 0; i < 32; i++){
ctx.drawImage(pixCanvas,0,0);
}
// create a second canvas 1/8th the size but not smaller than 1 by 1
if(!reuseCanvas || pixCanvas1 === undefined){
pixCanvas1 = document.createElement("canvas");
}
ctx1 = pixCanvas1.getContext("2d");
// reduced size rw, rh
rw = pixCanvas1.width = Math.max(1,Math.floor(aw/8));
rh = pixCanvas1.height = Math.max(1,Math.floor(ah/8));
// repeat the following untill the canvas is just 64 pixels
while(rw > 8 && rh > 8){
// draw the mask image several times
for(i = 0; i < 32; i++){
ctx1.drawImage(
pixCanvas,
0,0,aw,ah,
Math.random(),
Math.random(),
rw,rh
);
}
// clear original
ctx.clearRect(0,0,aw,ah);
// set the new size
aw = rw;
ah = rh;
// draw the small copy onto original
ctx.drawImage(pixCanvas1,0,0);
// clear reduction canvas
ctx1.clearRect(0,0,pixCanvas1.width,pixCanvas1.height);
// get next size down
rw = Math.max(1,Math.floor(rw / 8));
rh = Math.max(1,Math.floor(rh / 8));
}
if(!reuseCanvas){ // are we keeping the canvas
pixCanvas = undefined; // release ref
pixCanvas1 = undefined;
}
// check for overlap
return checkPixels(ctx,aw,ah);
}
The demo (Use full page)
The demo lets you compare the two methods. The mean time for each test is displayed. (will display NaN if no tests done)
For the best results view the demo full page.
Use left or right mouse buttons to test for overlap. Move the splat image over the other to see overlap result. On my machine I am getting about 11ms for the slow test and 0.03ms for the quick test (using Chrome, much faster on Firefox).
I have not spent much time testing how fast I can get it to work but there is plenty of room to increase the speed by reducing the number of time the images are drawn over each other. At some point faint pixels will be lost.
// Use the options to set quality of result
// Slow but perfect
var slowButPerfect = false;
const reuseCanvas = true;
var pixCanvas;
var pixCanvas1;
// returns true if any pixels are overlapping
function isPixelOverlap(img1,x,y,w,h,img2,x1,y1,w1,h1){
var ax,aw,ay,ah,ctx,canvas,ctx1,canvas1,i;
// function to check if any pixels are visible
function checkPixels(context,w,h){
var imageData = new Uint32Array(context.getImageData(0,0,w,h).data.buffer);
var i = 0;
// if any pixel is not zero then there must be an overlap
while(i < imageData.length){
if(imageData[i++] !== 0){
return true;
}
}
return false;
}
// check if they overlap
if(x > x1 + w1 || x + w < x1 || y > y1 + h1 || y + h < y1){
return false; // no overlap
}
// size of overlapping area
// find left edge
ax = x < x1 ? x1 : x;
// find right edge calculate width
aw = x + w < x1 + w1 ? (x + w) - ax : (x1 + w1) - ax
// do the same for top and bottom
ay = y < y1 ? y1 : y;
ah = y + h < y1 + h1 ? (y + h) - ay : (y1 + h1) - ay
// Create a canvas to do the masking on
if(!reuseCanvas || pixCanvas === undefined){
pixCanvas = document.createElement("canvas");
}
pixCanvas.width = aw;
pixCanvas.height = ah;
ctx = pixCanvas.getContext("2d");
// draw the first image relative to the overlap area
ctx.drawImage(img1,x - ax, y - ay);
// set the composite operation to destination-in
ctx.globalCompositeOperation = "destination-in"; // this means only pixels
// will remain if both images
// are not transparent
ctx.drawImage(img2,x1 - ax, y1 - ay);
ctx.globalCompositeOperation = "source-over";
// are we using slow method???
if(slowButPerfect){
if(!reuseCanvas){ // are we keeping the canvas
pixCanvas = undefined; // no then release reference
}
return checkPixels(ctx,aw,ah);
}
// now draw over its self to amplify any pixels that have low alpha
for(var i = 0; i < 32; i++){
ctx.drawImage(pixCanvas,0,0);
}
// create a second canvas 1/8th the size but not smaller than 1 by 1
if(!reuseCanvas || pixCanvas1 === undefined){
pixCanvas1 = document.createElement("canvas");
}
ctx1 = pixCanvas1.getContext("2d");
// reduced size rw, rh
rw = pixCanvas1.width = Math.max(1,Math.floor(aw/8));
rh = pixCanvas1.height = Math.max(1,Math.floor(ah/8));
// repeat the following untill the canvas is just 64 pixels
while(rw > 8 && rh > 8){
// draw the mask image several times
for(i = 0; i < 32; i++){
ctx1.drawImage(
pixCanvas,
0,0,aw,ah,
Math.random(),
Math.random(),
rw,rh
);
}
// clear original
ctx.clearRect(0,0,aw,ah);
// set the new size
aw = rw;
ah = rh;
// draw the small copy onto original
ctx.drawImage(pixCanvas1,0,0);
// clear reduction canvas
ctx1.clearRect(0,0,pixCanvas1.width,pixCanvas1.height);
// get next size down
rw = Math.max(1,Math.floor(rw / 8));
rh = Math.max(1,Math.floor(rh / 8));
}
if(!reuseCanvas){ // are we keeping the canvas
pixCanvas = undefined; // release ref
pixCanvas1 = undefined;
}
// check for overlap
return checkPixels(ctx,aw,ah);
}
function rand(min,max){
if(max === undefined){
max = min;
min = 0;
}
var r = Math.random() + Math.random() + Math.random() + Math.random() + Math.random();
r += Math.random() + Math.random() + Math.random() + Math.random() + Math.random();
r /= 10;
return (max-min) * r + min;
}
function createImage(w,h){
var c = document.createElement("canvas");
c.width = w;
c.height = h;
c.ctx = c.getContext("2d");
return c;
}
function createCSSColor(h,s,l,a) {
var col = "hsla(";
col += (Math.floor(h)%360) + ",";
col += Math.floor(s) + "%,";
col += Math.floor(l) + "%,";
col += a + ")";
return col;
}
function createSplat(w,h,hue, hue2){
w = Math.floor(w);
h = Math.floor(h);
var c = createImage(w,h);
if(hue2 !== undefined) {
c.highlight = createImage(w,h);
}
var maxSize = Math.min(w,h)/6;
var pow = 5;
while(maxSize > 4 && pow > 0){
var count = Math.min(100,Math.pow(w * h,1/pow) / 2);
while(count-- > 0){
const rhue = rand(360);
const s = rand(25,75);
const l = rand(25,75);
const a = (Math.random()*0.8+0.2).toFixed(3);
const size = rand(4,maxSize);
const x = rand(size,w - size);
const y = rand(size,h - size);
c.ctx.fillStyle = createCSSColor(rhue + hue, s, l, a);
c.ctx.beginPath();
c.ctx.arc(x,y,size,0,Math.PI * 2);
c.ctx.fill();
if (hue2 !== undefined) {
c.highlight.ctx.fillStyle = createCSSColor(rhue + hue2, s, l, a);
c.highlight.ctx.beginPath();
c.highlight.ctx.arc(x,y,size,0,Math.PI * 2);
c.highlight.ctx.fill();
}
}
pow -= 1;
maxSize /= 2;
}
return c;
}
var splat1,splat2;
var slowTime = 0;
var slowCount = 0;
var notSlowTime = 0;
var notSlowCount = 0;
var onResize = function(){
ctx.font = "14px arial";
ctx.textAlign = "center";
splat1 = createSplat(rand(w/2, w), rand(h/2, h), 0, 100);
splat2 = createSplat(rand(w/2, w), rand(h/2, h), 100);
}
function display(){
ctx.clearRect(0,0,w,h)
ctx.setTransform(1.8,0,0,1.8,w/2,0);
ctx.fillText("Fast GPU assisted Pixel collision test using 2D API",0, 14)
ctx.setTransform(1,0,0,1,0,0);
ctx.fillText("Hold left mouse for Traditional collision test. Time : " + (slowTime / slowCount).toFixed(3) + "ms",w /2 , 28 + 14)
ctx.fillText("Hold right (or CTRL left) mouse for GPU assisted collision. Time: "+ (notSlowTime / notSlowCount).toFixed(3) + "ms",w /2 , 28 + 28)
if((mouse.buttonRaw & 0b101) === 0) {
ctx.drawImage(splat1, w / 2 - splat1.width / 2, h / 2 - splat1.height / 2)
ctx.drawImage(splat2, mouse.x - splat2.width / 2, mouse.y - splat2.height / 2);
} else if(mouse.buttonRaw & 0b101){
if((mouse.buttonRaw & 1) && !mouse.ctrl){
slowButPerfect = true;
}else{
slowButPerfect = false;
}
var now = performance.now();
var res = isPixelOverlap(
splat1,
w / 2 - splat1.width / 2, h / 2 - splat1.height / 2,
splat1.width, splat1.height,
splat2,
mouse.x - splat2.width / 2, mouse.y - splat2.height / 2,
splat2.width,splat2.height
)
var time = performance.now() - now;
ctx.drawImage(res ? splat1.highlight: splat1, w / 2 - splat1.width / 2, h / 2 - splat1.height / 2)
ctx.drawImage(splat2, mouse.x - splat2.width / 2, mouse.y - splat2.height / 2);
if(slowButPerfect){
slowTime += time;
slowCount += 1;
}else{
notSlowTime = time;
notSlowCount += 1;
}
if(res){
ctx.setTransform(2,0,0,2,mouse.x,mouse.y);
ctx.fillText("Overlap detected",0,0)
ctx.setTransform(1,0,0,1,0,0);
}
//mouse.buttonRaw = 0;
}
}
// Boilerplate code below
const RESIZE_DEBOUNCE_TIME = 100;
var w, h, cw, ch, canvas, ctx, mouse, createCanvas, resizeCanvas, setGlobals, globalTime = 0, resizeCount = 0;
var firstRun = true;
createCanvas = function () {
var c,
cs;
cs = (c = document.createElement("canvas")).style;
cs.position = "absolute";
cs.top = cs.left = "0px";
cs.zIndex = 1000;
document.body.appendChild(c);
return c;
}
resizeCanvas = function () {
if (canvas === undefined) {
canvas = createCanvas();
}
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
ctx = canvas.getContext("2d");
if (typeof setGlobals === "function") {
setGlobals();
}
if (typeof onResize === "function") {
if(firstRun){
onResize();
firstRun = false;
}else{
resizeCount += 1;
setTimeout(debounceResize, RESIZE_DEBOUNCE_TIME);
}
}
}
function debounceResize() {
resizeCount -= 1;
if (resizeCount <= 0) {
onResize();
}
}
setGlobals = function () {
cw = (w = canvas.width) / 2;
ch = (h = canvas.height) / 2;
}
mouse = (function () {
function preventDefault(e) {
e.preventDefault();
}
var mouse = {
x : 0,
y : 0,
buttonRaw : 0,
over : false,
bm : [1, 2, 4, 6, 5, 3],
active : false,
bounds : null,
mouseEvents : "mousemove,mousedown,mouseup,mouseout,mouseover".split(",")
};
var m = mouse;
function mouseMove(e) {
var t = e.type;
m.bounds = m.element.getBoundingClientRect();
m.x = e.pageX - m.bounds.left;
m.y = e.pageY - m.bounds.top;
m.alt = e.altKey;
m.shift = e.shiftKey;
m.ctrl = e.ctrlKey;
if (t === "mousedown") {
m.buttonRaw |= m.bm[e.which - 1];
} else if (t === "mouseup") {
m.buttonRaw &= m.bm[e.which + 2];
} else if (t === "mouseout") {
m.buttonRaw = 0;
m.over = false;
} else if (t === "mouseover") {
m.over = true;
}
e.preventDefault();
}
m.start = function (element) {
if (m.element !== undefined) {
m.removeMouse();
}
m.element = element === undefined ? document : element;
m.mouseEvents.forEach(n => {
m.element.addEventListener(n, mouseMove);
});
m.element.addEventListener("contextmenu", preventDefault, false);
m.active = true;
}
m.remove = function () {
if (m.element !== undefined) {
m.mouseEvents.forEach(n => {
m.element.removeEventListener(n, mouseMove);
});
m.element = undefined;
m.active = false;
}
}
return mouse;
})();
resizeCanvas();
mouse.start(canvas, true);
window.addEventListener("resize", resizeCanvas);
function update1(timer) { // Main update loop
if(ctx === undefined){
return;
}
globalTime = timer;
display(); // call demo code
requestAnimationFrame(update1);
}
requestAnimationFrame(update1);

Not working condition for killing snake plus not working property of an object

I am making snake game through javascript and I have come to 2 problems.
First problem: I set condition for "killing" snake when his X and Y hits the border of canvas and I can not get why it is not working.
Second problem: My method "draw" of "snake" is working fine how it is written but if I put instead of "var i = snake.dlzka" "var i = snake.snake_body.length" it types an error: Cannot read property 'x' of undefined.
var canvas = document.getElementById("canvas");
canvas.width = 800;
canvas.height = 650;
var ctx = canvas.getContext("2d");
var dotW = 10;
var dotH = 10;
var direction = "right"; //smer pohybu hadika
var snake = {
dlzka: 5,
snake_body: [],
arraySpawn: function(){
for(var i = snake.dlzka; i >= 0; i--){
this.snake_body.push({
x: i*dotW,
y: 0
});
}
},
draw: function(){
ctx.clearRect(0,0, canvas.width, canvas.height);
for(var i = snake.dlzka; i>0; i--){
//alert(snake.snake_body.length);
ctx.fillStyle = "blue";
ctx.fillRect(snake.snake_body[i].x, snake.snake_body[i].y, dotW, dotH);
}
snake.move();
},
move: function(){
var first = snake.snake_body[0];
var first_x = first.x;
var first_y = first.y;
switch(direction){
case "right":
first_x += 10;
break;
case "left":
first_x -= 10;
break;
case "up":
first_y -= 10;
break;
case "down":
first_y += 10;
break;
}
this.snake_body.pop();
this.snake_body.unshift({
x: first_x,
y: first_y
});
if((first_x == canvas.width || 0) || (first_y == canvas.height || 0)){
var restart = confirm("Do you want to play again?");
if(restart){
clearInterval(intervalID);
snake.snake_body = [];
snake.arraySpawn();
intervalID = setInterval(function(){snake.draw()}, 33);
}
}
},
update_direction: function(e){
var key = e.keyCode;
if(key == 37 && direction !== "right"){
direction = "left";
}else if(key == 38 && direction !== "down"){
direction = "up";
}else if(key == 39 && direction !== "left"){
direction = "right";
}else if(key == 40 && direction !== "up"){
direction = "down";
}
}
};
snake.arraySpawn();
var intervalID = setInterval(function(){snake.draw()}, 33);
window.onkeydown = snake.update_direction;
canvas{
border: 1px solid black;
}
<canvas id="canvas"></canvas>
fiddle: http://jsfiddle.net/scarface/pdz4k8dj/20/
The first problem arises from the code snippet (first_x == canvas.width || 0) Based on operator precedence rules and left to right evaluation, this evaluates to
((first_x == canvas.width) || 0)
which is not what you wanted. Try
(first_x == canvas.width || first_x == 0)
instead but please try and read the rules to make you sure you understand why. Note the test requires the canvas to be an exact multiple of the x (or y) amount the snake is moved by, and it arguably could be better written as
(first_x >= canvas.width || first_x <= 0)
Don't forget to change the y value test because it has exactly the same problem.
The second issue is due to not taking into account that array indices are zero based. So
for(var i = snake.snake_body.length; i>0; i--)
iterates from past the end of the array (generating the error) back to the second element. Changing this to
for(var i = snake.snake_body.length - 1; i >= 0; i--)
worked in the fiddle.

Using EaselJS sprite sheet in existing code

I want to modify existing code which uses an image to animate, and use a sprite sheet instead. I'm using the EaselJS library for this.
The piece of code that creates the objects for animation:
function initStageObjects(){
car = new Car('img/car.png',canvas.width()/2,canvas.height()/2);
}
And this is the complete code:
$(window).load(function(){
$(document).ready(function(){
// Canvas Variables
var canvas = $('#canvas');
var context = canvas.get(0).getContext('2d');
var canvasWidth = canvas.width();
var canvasHeight = canvas.height();
// Keyboard Variables
var leftKey = 37;
var upKey = 38;
var rightKey = 39;
var downKey = 40;
// Keyboard event listeners
$(window).keydown(function(e){
var keyCode = e.keyCode;
if(keyCode == leftKey){
car.left = true;
} else if(keyCode == upKey){
car.forward = true;
} else if(keyCode == rightKey){
car.right = true;
} else if (keyCode == downKey){
car.backward = true;
}
});
$(window).keyup(function(e){
var keyCode = e.keyCode;
if(keyCode == leftKey){
car.left = false;
} else if(keyCode == upKey){
car.forward = false;
} else if(keyCode == rightKey){
car.right = false;
} else if (keyCode == downKey){
car.backward = false;
}
});
// Start & Stop button controlls
var playAnimation = true;
var startButton = $('#startAnimation');
var stopButton = $('#stopAnimation');
startButton.hide();
startButton.click(function(){
$(this).hide();
stopButton.show();
playAnimation = true;
updateStage();
});
stopButton.click(function(){
$(this).hide();
startButton.show();
playAnimation = false;
});
// Resize canvas to full screen
function resizeCanvas(){
canvas.attr('width', $(window).get(0).innerWidth);
canvas.attr('height', $(window).get(0).innerHeight);
canvasWidth = canvas.width();
canvasHeight = canvas.height();
}
resizeCanvas();
$(window).resize(resizeCanvas);
function initialise(){
initStageObjects();
drawStageObjects();
updateStage();
}
// Car object and properties
function Car(src, x, y){
this.image = new Image();
this.image.src = src;
this.x = x;
this.y = y;
this.vx = 0;
this.vy = 0;
this.angle = 90;
this.topSpeed = 5;
this.acceleration = 0.1;
this.reverse = 0.1;
this.brakes = 0.3;
this.friction = 0.05;
this.handeling = 15;
this.grip = 15;
this.minGrip = 5;
this.speed = 0;
this.drift = 0;
this.left = false;
this.up = false;
this.right = false;
this.down = false;
}
// Create any objects needed for animation
function initStageObjects(){
car = new Car('img/car.png',canvas.width()/2,canvas.height()/2);
}
function drawStageObjects(){
context.save();
context.translate(car.x,car.y);
context.rotate((car.angle + car.drift) * Math.PI/180);
context.drawImage(car.image, -25 , (-47 + (Math.abs(car.drift / 3))));
// context.fillRect(-5, -5, 10, 10);
context.restore();
}
function clearCanvas(){
context.clearRect(0, 0, canvasWidth, canvasHeight);
context.beginPath();
}
function updateStageObjects(){
// Faster the car is going, the worse it handels
if(car.handeling > car.minGrip){
car.handeling = car.grip - car.speed;
}
else{
car.handeling = car.minGrip + 1;
}
// Car acceleration to top speed
if(car.forward){
if(car.speed < car.topSpeed){
car.speed = car.speed + car.acceleration;
}
}
else if(car.backward){
if(car.speed < 1){
car.speed = car.speed - car.reverse;
}
else if(car.speed > 1){
car.speed = car.speed - car.brakes;
}
}
// Car drifting logic
if(car.forward && car.left){
if(car.drift > -35){
car.drift = car.drift - 3;
}
}
else if(car.forward && car.right){
if(car.drift < 35){
car.drift = car.drift + 3;
}
}
else if(car.forward && !car.left && car.drift > -40 && car.drift < -3){
car.drift = car.drift + 3;
}
else if(car.forward && !car.right && car.drift < 40 && car.drift > 3){
car.drift = car.drift - 3;
}
if(car.drift > 3){
if(!car.forward && !car.left){
car.drift = car.drift - 4;
}
}
else if(car.drift > -40 && car.drift < -3){
if(!car.forward && !car.right){
car.drift = car.drift + 4;
}
}
// General car handeling when turning
if(car.left){
car.angle = car.angle - (car.handeling * car.speed/car.topSpeed);
} else if(car.right){
car.angle = car.angle + (car.handeling * car.speed/car.topSpeed);
}
// Use this div to display any info I need to see visually
$('#stats').html("Score: 0");
// Constant application of friction / air resistance
if(car.speed > 0){
car.speed = car.speed - car.friction;
} else if(car.speed < 0) {
car.speed = car.speed + car.friction;
}
// Update car velocity (speed + direction)
car.vy = -Math.cos(car.angle * Math.PI / 180) * car.speed;
car.vx = Math.sin(car.angle * Math.PI / 180) * car.speed;
// Plot the new velocity into x and y cords
car.y = car.y + car.vy;
car.x = car.x + car.vx;
}
// Main animation loop
function updateStage(){
clearCanvas();
updateStageObjects();
drawStageObjects();
if(playAnimation){
setTimeout(updateStage, 25);
}
}
// Initialise the animation loop
initialise();
});
});//]]>
// JavaScript Document
How could i replace the image being used by the sprite i created?
Even though you maybe could implement a createjs.SpriteSheet into a non-Createjs/non-EaselJS project I would strongly recommend you not to do that, EaselJS-classes where not designed to be used as single modular classes in any kind of project, they best work together in the framework. In the end you probably will need more time and probably end up with more code by trying get everything to work than with just converting your (yet still small) project to EaselJS all together. So learn something new today and refacture your project to EaselJS ;-)
but if you want more work, continue reading:
A createjs.SpriteSheet basically only handles SpriteSheet data and you can use mySpriteSheet.getFrame(int); to get the image and the source-rect to that frame so you can draw it to your canvas. But a SpriteSheet alone won't handle animations for you, so you would have to advance the "playhead" every frame yourself(go back to frame 0 if at the end ect...), or you can implement another createjs-class called createjs.BitmapAnimation - however, drawing this to your canvas would again require additional code or yet an additional createjs-class: you see where this is getting.

Categories

Resources